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

Linux内核6.6/6.8版本Ftrace钩子无限循环问题求助

内核6.6/6.8下Ftrace回调无限循环问题解决

问题场景

内核版本6.8,使用Ftrace钩子时,通过within_module(parent_ip, THIS_MODULE)判断是否来自当前模块以避免递归,但该检查在6.6、6.8版本中始终返回false,导致Ftrace触发无限循环;相同代码在内核5.15版本可正常运行,怀疑是内核模块布局相关提交导致地址检查失效。

回调代码如下:

static void notrace ftrace_callback_handler(unsigned long ip, unsigned long parent_ip,
        struct ftrace_ops *ops, struct ftrace_regs *fregs)
{
    struct pt_regs *regs = ftrace_get_regs(fregs);
    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);

#if USE_FENTRY_OFFSET
    regs->ip = (unsigned long)hook->function;
#else
    if (!within_module(parent_ip, THIS_MODULE)){
        LOG_DEBUG("not true within_module(parent_ip, THIS_MODULE)");
        regs->ip = (unsigned long)hook->function;
    } else {
        LOG_DEBUG("true within_module(parent_ip, THIS_MODULE)");
    }
        
#endif //USE_FENTRY_OFFSET
}

原因分析

内核6.6及以后版本对模块内存布局做了调整,将代码段拆分为多个独立段(如.text、.fentry、.exit.text等),而within_module函数默认仅检查模块核心.text段的地址范围。Ftrace回调函数通常被放置在.fentry或其他非.text段,导致parent_ip(回调自身的地址)不在within_module的检查范围内,因此始终返回false,无法阻止递归触发。

在5.15及更早版本中,模块所有代码都集中在.text段,within_module可以正确识别回调函数的地址归属。

解决方案

方案1:直接检查地址是否为回调函数自身(最可靠)

递归的本质是回调函数触发自身的Ftrace钩子,直接对比ip或parent_ip与回调函数的地址,精准阻止递归:

static void notrace ftrace_callback_handler(unsigned long ip, unsigned long parent_ip,
        struct ftrace_ops *ops, struct ftrace_regs *fregs)
{
    struct pt_regs *regs = ftrace_get_regs(fregs);
    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);

    // 检查当前执行IP或父IP是否为回调函数本身,避免递归
    if (ip != (unsigned long)ftrace_callback_handler && 
        parent_ip != (unsigned long)ftrace_callback_handler) {
        regs->ip = (unsigned long)hook->function;
    }
}

方案2:扩展模块地址检查范围

手动遍历模块的所有代码段,判断地址是否属于当前模块的任意段:

#include <linux/module.h>
#include <linux/elf.h>

static bool within_any_module_segment(unsigned long addr, struct module *mod)
{
    struct module_sect_attrs *sect_attrs = mod->sect_attrs;
    struct module_section *sec;

    if (!sect_attrs)
        return false;

    // 遍历模块所有段,检查地址是否在段范围内
    list_for_each_entry(sec, &sect_attrs->sections, list) {
        if (addr >= sec->addr && addr < sec->addr + sec->size)
            return true;
    }
    return false;
}

static void notrace ftrace_callback_handler(unsigned long ip, unsigned long parent_ip,
        struct ftrace_ops *ops, struct ftrace_regs *fregs)
{
    struct pt_regs *regs = ftrace_get_regs(fregs);
    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);

    if (!within_any_module_segment(parent_ip, THIS_MODULE)) {
        regs->ip = (unsigned long)hook->function;
    }
}

方案3:启用USE_FENTRY_OFFSET(推荐)

如果内核配置支持,启用USE_FENTRY_OFFSET,该机制通过偏移量跳转,本身不会触发回调函数的递归调用:

#define USE_FENTRY_OFFSET 1

static void notrace ftrace_callback_handler(unsigned long ip, unsigned long parent_ip,
        struct ftrace_ops *ops, struct ftrace_regs *fregs)
{
    struct pt_regs *regs = ftrace_get_regs(fregs);
    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);

#if USE_FENTRY_OFFSET
    regs->ip = (unsigned long)hook->function;
#endif //USE_FENTRY_OFFSET
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:53:17