You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM64架构下Ftrace挂钩内核函数触发无限循环问题排查

ARM64下Ftrace挂钩内核函数触发无限循环的问题分析

问题背景

在启用CONFIG_DYNAMIC_FTRACE_WITH_REGS的ARM64架构Linux 5.7系统中,尝试挂钩kmem_cache_alloc等内核函数时触发无限循环。简化后的_do_fork挂钩示例在x86-64下正常运行,但ARM64下出现无限循环,且发现ftrace_set_filter_ip无法工作,ftrace_set_filter可正常使用。

相关代码

asmlinkage long (*original_do_fork)(unsigned long clone_flags,...);

asmlinkage long hooked_do_fork(unsigned long clone_flags, unsigned long stack_start,
                               unsigned long stack_size, int __user *parent_tidptr,
                               int __user *child_tidptr, unsigned long tls) {
    printk("hooked_do_fork called!\n");
    return original_do_fork(clone_flags, stack_start, stack_size, parent_tidptr, child_tidptr, tls);
}

// fh_ftrace_thunk
static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
                                    struct ftrace_ops *ops, struct pt_regs *regs) {
    printk("fh_ftrace_thunk called!\n");
    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);

    //this should stop infinite loop but not working
    if (!within_module(parent_ip, THIS_MODULE)) regs->pc = (unsigned long)hook->function;
}

static int __init fh_init(void) {
    hook.address = kallsyms_lookup_name(hook.name);
    *((unsigned long*)&original_do_fork) = hook.address+ MCOUNT_INSN_SIZE;
    hook.ops.func = fh_ftrace_thunk;
    hook.ops.flags =  FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED | FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_IPMODIFY;
    
    err = ftrace_set_filter(&hook.ops, hook.name, strlen(hook.name), 0);
   
    err = register_ftrace_function(&hook.ops);
    return 0;
}

程序输出

[   26.788937] fh_ftrace_thunk called!
[   26.789224] fh_ftrace_thunk called!
[   26.789544] fh_ftrace_thunk called!
[   26.789788] fh_ftrace_thunk called!
[   26.790090] fh_ftrace_thunk called!
[   26.790314] fh_ftrace_thunk called!
[   26.790600] fh_ftrace_thunk called!
[   26.790866] fh_ftrace_thunk called!
[   26.791429] fh_ftrace_thunk called!
[   26.791661] fh_ftrace_thunk called!
[   26.800664] fh_ftrace_thunk called!
[   26.801035] fh_ftrace_thunk called!
[   26.801426] fh_ftrace_thunk called!

原因分析

  1. ARM64与x86-64的parent_ip语义差异
    x86-64中parent_ip是触发ftrace回调的外部调用者地址,但ARM64的ftrace实现里,parent_ip实际是被挂钩函数内部mcount调用的返回地址(即被挂钩函数中mcount指令后的地址),并非真正的外部调用者地址。这导致!within_module(parent_ip, THIS_MODULE)的判断永远为真,每次都会修改regs->pc跳转到钩子函数。
  2. 钩子函数调用原始函数时重复触发ftrace
    钩子函数hooked_do_fork调用original_do_fork时,原始函数的ftrace探针依然会触发——因为original_do_fork指向的是跳过mcount后的地址,但ftrace过滤规则未排除钩子函数的调用场景,导致回调反复执行,陷入循环。
  3. FTRACE_OPS_FL_RECURSION_SAFE的局限性
    该标志仅让ftrace在回调执行时临时禁用当前ops的回调,但ARM64下的实现细节差异,或是你修改pc的逻辑绕过了递归保护机制,导致该标志未生效。

修复方案

1. 正确判断调用来源

ARM64下需通过pt_regs中的返回地址(x30寄存器)判断是否为钩子函数调用,替换原有的parent_ip判断逻辑:

static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
                                    struct ftrace_ops *ops, struct pt_regs *regs) {
    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
    unsigned long caller = regs->regs[30]; // ARM64中x30为返回地址

    // 仅当调用者不是钩子函数时,才跳转到钩子逻辑
    if (caller != (unsigned long)hooked_do_fork) {
        regs->pc = (unsigned long)hook->function;
    }
}

2. 调用原始函数时临时禁用ftrace ops

在钩子函数调用原始函数的前后,临时注册/注销ftrace ops,避免重复触发:

asmlinkage long hooked_do_fork(unsigned long clone_flags, unsigned long stack_start,
                               unsigned long stack_size, int __user *parent_tidptr,
                               int __user *child_tidptr, unsigned long tls) {
    printk("hooked_do_fork called!\n");
    // 临时禁用当前ops
    unregister_ftrace_function(&hook.ops);
    long ret = original_do_fork(clone_flags, stack_start, stack_size, parent_tidptr, child_tidptr, tls);
    register_ftrace_function(&hook.ops);
    return ret;
}

3. 修正original_do_fork的地址

ARM64下MCOUNT_INSN_SIZE的宏定义需匹配实际指令集(A64为8字节,A32为4字节),确保hook.address + MCOUNT_INSN_SIZE指向跳过mcount后的正确地址,避免原始函数调用时再次触发探针。


内容的提问来源于stack exchange,提问作者JamesMcgill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:14:54