Search code examples
clinuxlinux-kernelsystem-calls

How to correctly extract a string from a user space pointer in kernel space?


I wrote a hook for the execve system call and in the beginning when I wrote it to print "hi", each time that a file is executed. It worked fine, but when I tried to print the filename that is passed to the system call this resulted in a crash and of course I had to restart my computer.

This is my code:

static asmlinkage long our_execl(const char __user * filename,
            const char __user * const __user * argv,
            const char __user * const __user * envp) {
    printk("%s\n",filename);
    return original_execl(filename, argv, envp);
}

This is how I inject the new syscall:

static int lkm_example_init(void)
{

    printk("new new new 2");

    write_cr0(read_cr0()&(~ 0x10000));

    sys_call_table = (void*)0xdd8c4240//the syscall address from the   /proc/kallsyms ;

    execl= sys_call_table[__NR_execve];
    sys_call_table[__NR_execve]=our_execl;

    write_cr0(read_cr0() | 0X10000);
    return 0;
}

Solution

  • What is most likely happening here is that SMAP (Supervisor Mode Access Prevention) is preventing the kernel from accessing a raw user space pointer, causing a panic.

    The correct way to access a string from user space is to copy its content using strncpy_from_user() first. Also, be careful and make sure to correctly terminate the string.

    static asmlinkage long our_execl(const char __user * filename,
                const char __user * const __user * argv,
                const char __user * const __user * envp) {
        char buf[256];
        buf[255] = '\0';
    
        long res = strncpy_from_user(buf, filename, 255);
        if (res > 0)
            printk("%s\n", buf);
    
        return original_execl(filename, argv, envp);
    }
    

    In this case, since we are specifically talking about a file name, depending on the kernel version you may be able to use the getname() and putname() from linux/fs.h (if they are exported in the kernel version you are working with). These work using a struct filename.

    static asmlinkage long our_execl(const char __user * filename,
                const char __user * const __user * argv,
                const char __user * const __user * envp) {
    
        struct filename *fname = getname(filename);
        if (!IS_ERR(fname)) {
            printk("%s\n", fname->name);
            putname(fname);
        }
    
        return original_execl(filename, argv, envp);
    }