如何在内核空间中分配用户空间内存?

时间:2018-05-02 02:55:30

标签: linux-kernel hook system-calls

我在Linux上挂了一个系统调用(open),想要打印这个打开的文件名。 然后我调用syscall(getcwd)来获取绝对路径。

这是源代码:

validate

sys_getcwd需要一个用户空间内存,我用GFP_USER调用kmalloc。 但是sys_getcwd总是返回-EFAULT(错误地址)......

这是dmesg日志:

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

所以我找到了sys_getcwd中的工具,他做了

[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...
显然,getcwd使用标志GFP_KERNEL分配内存,然后从(GFP_KERNEL)复制到我的缓冲区(__user * buf)!!!

不是__user MACRO是GFP_USER?

标志GFP_USER简介是https://elixir.bootlin.com/linux/v4.4/source/include/linux/gfp.h#L208

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

出了什么问题?

1 个答案:

答案 0 :(得分:0)

至少有两个帐户出错:

  1. 系统调用劫持(更别说像开放这样的东西)只是一个坏主意。捕获所有可能的开放路径的唯一合理方法是使用LSM钩子。它也碰巧处理正在打开的实际文件,避免竞争:你读取例程中的路径,包装打开再次读取它。但到那时,恶意用户空间可能已经改变了它,你最终查看了错误的文件。
  2. 应该清楚getcwd必须有一个解析名称的方法,以便将它放入用户空间缓冲区。你应该深入研究调用,看看可以改变什么来把它放在内核缓冲区中。
  3. 为什么你这么做呢?