0
votes

I hook a syscall(open) on Linux, and want to print this opened filename. then I call syscall(getcwd) to get the absolute path.

this is source code:

void *memndup_from_user(const void __user *src, long len)
{
    void *kbuf = NULL;
    if(src == NULL) {
        return kbuf;
    }
    kbuf = kmalloc(len + 1, GFP_KERNEL);
    if(kbuf != NULL) {
        if (copy_from_user(kbuf, src, len)) {
            printk(KERN_ALERT "%s\n", "copy_from_user failed.");
            kfree(kbuf);
            kbuf = NULL;
        }
        else {
            ((char *)kbuf)[len] = '\0';
        }
    } else {
        printk(KERN_ALERT "%s\n", "kmalloc failed.");
    }
    return kbuf;
}
void *memdup_from_user(const void __user *src)
{
    long len = 0;
    if(src == NULL) {
        return NULL;
    }
    len = strlen_user(src);
    return memndup_from_user(src, len);
}

asmlinkage long fake_getcwd(char __user *buf, unsigned long size)
{
    return real_getcwd(buf, size);
}

asmlinkage long
fake_open(const char __user *filename, int flags, umode_t mode)
{
    if(flags & O_CREAT) {
        char *k_filename = (char *)memdup_from_user(filename);
        char *u_path = (char *)kmalloc(PAGE_SIZE, GFP_USER);
        if(k_filename != NULL) {
            printk(KERN_ALERT "ano_fake_open pid:%ld create : %s\n", ano_fake_getpid(), k_filename);
            kfree(k_filename);
        }

        if(u_path != NULL) {
            long retv;
            retv = fake_getcwd(u_path, PAGE_SIZE);
            if(retv > 0) {
                printk(KERN_ALERT "getcwd ret val: %ld, path: %s\n", retv, u_path);
            } else {
                printk(KERN_ALERT "getcwd ret val: %ld, error...\n", retv);
            }

            kfree(u_path);
        }
    }
    return real_open(filename, flags, mode);
}

the sys_getcwd requires an user space memory, and I call kmalloc with GFP_USER. but sys_getcwd always return -EFAULT(Bad Address)...

this is dmesg logs:

[344897.726061] fake_open pid:70393 create : sssssssssssssssss
[344897.726065] getcwd ret val: -14, error...
[344897.727431] fake_open pid:695 create : /var/lib/rsyslog/imjournal.state.tmp
[344897.727440] getcwd ret val: -14, error...

so I find the implement in sys_getcwd, he does

# define __user     __attribute__((noderef, address_space(1)))
# define __kernel   __attribute__((address_space(0)))
#define __getname()     kmem_cache_alloc(names_cachep, GFP_KERNEL)

SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
{
    char *page = __getname();
    get_fs_root_and_pwd_rcu(current->fs, &root, &pwd);
    ...
    // char *cwd = page + xxx; (xxx < PAGE_SIZE)
    // len = PAGE_SIZE + page - cwd;
    ...
    if (len <= size) {
        error = len;
        if (copy_to_user(buf, cwd, len))
            error = -EFAULT;
    }
}

obviously, getcwd alloc memory with flag GFP_KERNEL, then copy to my buffer( __user *buf ) from (GFP_KERNEL) !!!

isn't __user MACRO be GFP_USER ?

the flag GFP_USER brief is https://elixir.bootlin.com/linux/v4.4/source/include/linux/gfp.h#L208:

/*   GFP_USER is for userspace allocations that also need to be directly
 *   accessibly by the kernel or hardware. It is typically used by hardware
 *   for buffers that are mapped to userspace (e.g. graphics) that hardware
 *   still must DMA to. cpuset limits are enforced for these allocations.
 */

what's wrong ?

1
The userspace thread ought to have the relevant page mapped into its virtual address space. Simply allocating it on the kernel side is not enough. - oakad
It looks like really complicate, but what I want to do is how to call getcwd in sys_open, that I can piece a relative opened filename to a integral path. - dawei lii
The usual approach is to allocate/reserve some memory on the user side, then pass the pointer to that already reserved memory and its length to the kernel. - oakad

1 Answers

0
votes

This is wrong on at least two accounts:

  1. syscall hijacking (let alone for something like open) is just a bad idea. the only sensible method to catch all possible open path is through using LSM hooks. it also happens to deal with the actual file being opened avoiding the race: you read the path in your routine, wrapped opens reads it again. but by that time malicious userspace could have changed it and you ended up looking at the wrong file.
  2. it should be clear getcwd has to have a method of resolving a name in order to put it into the userspace buffer. you should dig in into the call and see what can be changed to put it in a kernel buffer.

Why are you doing this to begin with?