linuxarmhookarm64ftrace

Use Ftrace to hook kernel functions in arm64 but cause infinite loop


I'm trying to hook some kernel functions like kmem_cache_alloc in arm64 linux5.7(this version has CONFIG_DYNAMIC_FTRACE_WITH_REGS in arm64). But I always enters an infinite loop when the function is called.

So I simplify the code and write a demo to hook _do_fork below, at x86-64 the code is fine and the part that should stop looping works fine. A little detail is that ftrace_set_filter_ip is not working in arm64 but ftrace_set_filter works good.

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;
}

Outputs below:

[   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!

What is causing this infinite loop? Are there any arm64 details I'm missing?


Solution

  • By removing printk("fh_ftrace_thunk called!\n"); .

    And change

    *((unsigned long*)&original_do_fork) = hook.address+MCOUNT_INSN_SIZE;
    

    to

    *((unsigned long*)&original_do_fork) = hook.address;
    

    I solved the problem above, but still don't know how this works.