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

Linux kernel 如何在内核空间中分配用户空间内存?,linux-kernel,hook,system-calls,Linux Kernel,Hook,System Calls,我在Linux上挂接了一个系统调用(open),并想打印这个打开的文件名。 然后我调用syscall(getcwd)来获取绝对路径 这是源代码: void *memndup_from_user(const void __user *src, long len) { void *kbuf = NULL; if(src == NULL) { return kbuf; } kbuf = kmalloc(len + 1, GFP_KERNEL);

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

这是源代码:

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需要一个用户空间内存,我用GFP_user调用kmalloc。 但是sys_getcwd总是返回-EFAULT(错误地址)

这是dmesg日志:

[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...
所以我在sys_getcwd中找到了这个工具,他找到了

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

有什么问题吗?

至少有两种情况是错误的:

  • 系统调用劫持(更不用说像open这样的东西)是个坏主意。捕捉所有可能的开放路径的唯一合理方法是使用LSM钩子。它还碰巧处理正在打开的实际文件,以避免竞争:在例程中读取路径,然后重新打开。但到那时,恶意用户空间可能已经改变了它,而您最终查看的文件是错误的
  • 应该很清楚,getcwd必须有一个解析名称的方法才能将其放入用户空间缓冲区。您应该深入研究这个调用,看看可以做些什么来将它放入内核缓冲区

  • 为什么要这样做?

    用户空间线程应该将相关页面映射到其虚拟地址空间。仅仅在内核端分配它是不够的。它看起来非常复杂,但我想做的是如何在sys_open中调用getcwd,这样我可以将相对打开的文件名分割成一个整数路径。通常的方法是在用户端分配/保留一些内存,然后将指向已保留内存的指针及其长度传递给内核。我的领导让我监视主机上所有已更改的文件。
    /*   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.
     */