Linux kernel 从ftrace处理程序返回原始函数时还原任务pt_regs

Linux kernel 从ftrace处理程序返回原始函数时还原任务pt_regs,linux-kernel,ftrace,Linux Kernel,Ftrace,通过内核模块(LKM),linux内核ftrace函数允许您设置ftrace\u OPS\u FL\u SAVE\u REGS和ftrace\u OPS\u FL\u IPMODIFY标志,基本上允许您完全重定向任何可以找到符号地址的内核函数,如下所示: static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *fops, struct p

通过内核模块(LKM),linux内核
ftrace
函数允许您设置
ftrace\u OPS\u FL\u SAVE\u REGS
ftrace\u OPS\u FL\u IPMODIFY
标志,基本上允许您完全重定向任何可以找到符号地址的内核函数,如下所示:

static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
        struct ftrace_ops *fops, struct pt_regs *regs) {
    regs->ip = new_addr;
}
其中
new\u addr
是新函数的地址。
kpatch
工具使用此功能,但从未返回原始功能

如果在
new\u addr
指向的函数末尾,我尝试以下操作:

task_pt_regs(current)->ip = orig_addr + MCOUNT_INSN_SIZE;
有些函数进行时不会出现问题,但大多数函数会导致调用过程出现故障


ftrace
函数具有内置代码,可在返回到原始函数时恢复当前任务的
pt_regs
,这就是为什么我可以转到自己的函数并拥有参数而不会出现问题的原因。但是,在代码的这一点上,
ftrace
不再涉及。我如何告诉内核不要重置当前寄存器,这样函数就可以在新的返回地址使用它们呢?

发布这篇文章后,我想也许我可以直接从ftrace处理程序中的
pt_regs*regs
指针读取参数。事实证明,你可以。通过不重定向到另一个函数,您可以保留寄存器和返回地址,同时决定是否从处理程序本身返回:

int donotexec(void) {
        return -EACCES;
}

static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
                    struct ftrace_ops *fops, struct pt_regs *regs) {

    struct linux_binprm *bprm = (struct linux_binprm *)regs->di;

    if (bprm->file)
            if (allowed_to_exec(bprm->file))
                    regs->ip = (unsigned long)donotexec;
}
此函数钩住
security\u bprm\u check
,其中
allowed\u to\u exec
是另一个函数,用于检查从
regs->di
寄存器读取的
bprm->file


这是依赖于arch的(请参见
arch/x86/include/asm/ptrace.h
中的内核
pt_regs
结构),并且限制为5个函数参数。

在发布这篇文章之后,我想也许我可以直接从ftrace处理程序中的
pt_regs*regs
指针读取参数。事实证明,你可以。通过不重定向到另一个函数,您可以保留寄存器和返回地址,同时决定是否从处理程序本身返回:

int donotexec(void) {
        return -EACCES;
}

static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
                    struct ftrace_ops *fops, struct pt_regs *regs) {

    struct linux_binprm *bprm = (struct linux_binprm *)regs->di;

    if (bprm->file)
            if (allowed_to_exec(bprm->file))
                    regs->ip = (unsigned long)donotexec;
}
此函数钩住
security\u bprm\u check
,其中
allowed\u to\u exec
是另一个函数,用于检查从
regs->di
寄存器读取的
bprm->file

这取决于arch(请参见
arch/x86/include/asm/ptrace.h
中内核的
pt_regs
结构),并且限制为5个函数参数