Quantcast
Channel: Active questions tagged linux-kernel - Stack Overflow
Viewing all articles
Browse latest Browse all 12308

ftrace ftrace_set_filter_ip/ftrace_set_filter return EINVAL

$
0
0

I have asked a question about hooking functions in Linux kernel. Now I'm learning about ftrace.

I wrote a sample program that you can see below, it worked fine on Linux 5.x and 6.x 66-bit as well as Aarch64 on a Raspberry Pi 4 and it worked as expected.

Now I'm testing it on an older kernel, 4.x running inside Qemu as it's Aarch64. But I keep getting EINVAL from ftrace_set_filter() and ftrace_set_filter_ip().

This code is inspired by xcellerator examples https://github.com/xcellerator/linux_kernel_hacking, I modified the source code a lot based on anything I could find online to fix my issue, so any problem with the code is probably mine mistake and not the original authors.

Header,

/* * Helper library for ftrace hooking kernel functions * Author: Harvey Phillips (xcellerator@gmx.com) * License: GPL * */#include <linux/ftrace.h>#include <linux/linkage.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/version.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0)#define FTRACE_OPS_FL_RECURSION FTRACE_OPS_FL_RECURSION_SAFE#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(5,11,0)#define ftrace_regs pt_regsstatic __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs){        return fregs;}#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)#define KPROBE_LOOKUP 1#include <linux/kprobes.h>static struct kprobe kp = {    .symbol_name = "kallsyms_lookup_name"};#endif#define HOOK(_name, _hook, _orig)   \{                                   \    .name = (_name),                \    .function = (_hook),            \    .original = (_orig),            \}#define USE_FENTRY_OFFSET 0#if !USE_FENTRY_OFFSET#pragma GCC optimize("-fno-optimize-sibling-calls")#endifstruct ftrace_hook {    const char *name;    void *function;    void *original;    unsigned long address;    struct ftrace_ops ops;};static int fh_resolve_hook_address(struct ftrace_hook *hook){#ifdef KPROBE_LOOKUP    typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);    kallsyms_lookup_name_t kallsyms_lookup_name;    int ret;    ret = register_kprobe(&kp);    if (ret < 0) {        printk("register_kprobe failed: %d\n", ret);        return ret;    }    kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;    unregister_kprobe(&kp);#endif    hook->address = kallsyms_lookup_name(hook->name);    if (!hook->address) {        printk("unresolved symbol: %s\n", hook->name);        return -ENOENT;    }#if USE_FENTRY_OFFSET    *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE;#else    *((unsigned long*) hook->original) = hook->address;#endif    printk("Resolved address of %s: %lx\n", hook->name, hook->address);    return 0;}static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip,                                    struct ftrace_ops *ops, struct ftrace_regs *fregs){    printk("fh_ftrace_thunk - Init\n");    struct pt_regs *regs = ftrace_get_regs(fregs);    printk("ftrace_get_regs - After\n");    struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);#if USE_FENTRY_OFFSET    regs->pc = (unsigned long)hook->function;#else    if (!within_module(parent_ip, THIS_MODULE))        regs->pc = (unsigned long)hook->function;#endif}int fh_install_hook(struct ftrace_hook *hook){    int err;    printk("fh_install_hook - prologue\n");    err = fh_resolve_hook_address(hook);    printk("fh_resolve_hook - calling fh_resolve_hook_address\n");    if (err) {        printk("fh_resolve_hook_address() failed: %d\n", err);        return err;    }    hook->ops.func = fh_ftrace_thunk;    hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS                    | FTRACE_OPS_FL_RECURSION                    | FTRACE_OPS_FL_IPMODIFY;    printk("fh_install_hook - BEFORE ftrace_set_filter_ip\n");    err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);    printk("fh_install_hook - AFTER ftrace_set_filter_ip\n");    if (err) {        printk("ftrace_set_filter_ip() failed: %d\n", err);        return err;    }    printk("register_ftrace_function - before\n");    err = register_ftrace_function(&hook->ops);    printk("register_ftrace_function - after\n");    if (err) {        printk("register_ftrace_function() failed: %d\n", err);        ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);        return err;    }    printk("register_ftrace_function - return\n");    return 0;}/** * fh_remove_hooks() - disable and unregister a single hook * @hook: a hook to remove */void fh_remove_hook(struct ftrace_hook *hook){    int err;    err = unregister_ftrace_function(&hook->ops);    if (err) {        printk("unregister_ftrace_function() failed: %d\n", err);    }    err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);    if (err) {        printk("ftrace_set_filter_ip() failed: %d\n", err);    }}/** * fh_install_hooks() - register and enable multiple hooks * @hooks: array of hooks to install * @count: number of hooks to install * * If some hooks fail to install then all hooks will be removed. * * Returns: zero on success, negative error code otherwise. */int fh_install_hooks(struct ftrace_hook *hooks, size_t count){    int err;    size_t i;    for (i = 0; i < count; i++) {        printk("fh_install_hooks - %lu\n", i);        err = fh_install_hook(&hooks[i]);        if (err)            goto error;    }    return 0;error:    printk("fh_install_hooks - error statement\n");    while (i != 0) {        fh_remove_hook(&hooks[--i]);    }    return err;}/** * fh_remove_hooks() - disable and unregister multiple hooks * @hooks: array of hooks to remove * @count: number of hooks to remove */void fh_remove_hooks(struct ftrace_hook *hooks, size_t count){    size_t i;    for (i = 0; i < count; i++)        fh_remove_hook(&hooks[i]);}

Kernel module,

#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/ftrace.h>#include <linux/ptrace.h>#include <linux/sched.h>#include <linux/uaccess.h>#include <linux/version.h>#include "ftrace_helper.h"MODULE_LICENSE("GPL");MODULE_AUTHOR("jelal");MODULE_DESCRIPTION("Hook into execve syscall");MODULE_VERSION("0.01");#ifdef CONFIG_ARM64static asmlinkage long (*orig_sys_execve)(const char __user *filename,                                          const char __user *const __user *argv,                                          const char __user *const __user *envp);static asmlinkage long hook_sys_execve(const char __user *filename,                                       const char __user *const __user *argv,                                       const char __user *const __user *envp) {    char fname[256];    if (strncpy_from_user(fname, filename, sizeof(fname)) > 0) {        printk(KERN_INFO "execve called with filename: %s\n", fname);    } else {        printk(KERN_INFO "execve called with an unknown filename\n");    }    return orig_sys_execve(filename, argv, envp);}static struct ftrace_hook hooks[] = {    HOOK("__arm64_sys_execve", hook_sys_execve, &orig_sys_execve),};static int __init testftrace_init(void) {    int err;    printk(KERN_INFO "testftrace: Init\n");    err = fh_install_hooks(hooks, ARRAY_SIZE(hooks));    if (err) {        printk(KERN_ERR "fh_install_hooks() failed: %d\n", err);        return err;    }    printk(KERN_INFO "testftrace: Loaded >:-)\n");    return 0;}static void __exit testftrace_exit(void) {    fh_remove_hooks(hooks, ARRAY_SIZE(hooks));    printk(KERN_INFO "testftrace: Unloaded :-(\n");}module_init(testftrace_init);module_exit(testftrace_exit);#elsestatic int __init testftrace_init(void) {    printk(KERN_INFO "testftrace: Only supported on ARM64 architecture\n");    return -ENODEV;}static void __exit testftrace_exit(void) {    printk(KERN_INFO "testftrace: Unloaded\n");}module_init(testftrace_init);module_exit(testftrace_exit);#endif

The Makefile,

obj-m += testftrace.oall:    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

I am root user, running make followed by insmod testftrace.ko and I'm getting the following error,

insmod: ERROR: could not insert module testftrace.ko: Invalid parameters

and in dmesg -w,

19855.397220] testftrace: Init[19855.400273] Resolved address of __arm64_sys_execve: ffff00000834a260[19855.400536] ftrace_set_filter_ip() failed: -22[19855.400563] fh_install_hooks - error statement[19855.400581] fh_install_hooks() failed: -22

Error -22 or EINVAL could happen when the address for the function you want to hook is invalid, I checked kallsyms to check the address,

root@localhost execvehook]# cat /proc/kallsyms | grep _sys_execffff00000834a260 T __arm64_sys_execve...

As you can see, the address for this function seem to be correct. I tried with system call functions and none-system call function but I'm getting the same error.

I tried to use ftrace_set_filter instead of ftrace_set_filter_ip but I got the same EINVAL error.

This is also the kernel config of the Linux that I'm running under Qemu,

CONFIG_HAVE_LIVEPATCH_WO_FTRACE=yCONFIG_LIVEPATCH_WO_FTRACE=y# CONFIG_PSTORE_FTRACE is not setCONFIG_HAVE_DYNAMIC_FTRACE=yCONFIG_HAVE_FTRACE_MCOUNT_RECORD=yCONFIG_FTRACE=yCONFIG_FTRACE_SYSCALLS=yCONFIG_DYNAMIC_FTRACE=yCONFIG_FTRACE_MCOUNT_RECORD=y# CONFIG_FTRACE_STARTUP_TEST is not set

At this point, I ran out of ideas to test and my searches didn't lead to anything helpful. I hope to get some help from here.

The problem I'm trying to solve is, to be able to use ftrace on kernel 4.x and older on Arm and Intel.

  • Am I missing something in my code? This is basically my very first code for ftrace, so I'm pretty new.
  • Have you tried ftrace on 4.x kernels or older on Aarch64? Is there anything I need to watch for, or customize my code for older kernels when using ftrace?
  • Are there limitations/differences between ftrace on 5.x/6.x and 4.x and older?

Thanks,Jelal


Viewing all articles
Browse latest Browse all 12308

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>