Как выделить память пространства пользователя в пространстве ядра? - PullRequest
0 голосов
/ 02 мая 2018

Я подключаю системный вызов (open) в Linux и хочу напечатать это открытое имя файла. затем я вызываю 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 требует памяти пользовательского пространства, и я вызываю kmalloc с помощью GFP_USER. но 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;
    }
}

очевидно, getcwd выделяет память с флагом GFP_KERNEL, затем копирует в мой буфер (__user * buf) из (GFP_KERNEL) !!!

не __user MACRO будет GFP_USER?

Сводка флага GFP_USER 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.
 */

что не так?

1 Ответ

0 голосов
/ 02 мая 2018

Это неверно как минимум для двух учетных записей:

  1. угон системного вызова (не говоря уже о чем-то вроде открытого) - просто плохая идея. единственный разумный метод, чтобы поймать все возможные открытые пути - это использовать LSM-хуки. это также происходит с фактическим открываемым файлом, избегая гонки: вы читаете путь в своей программе, обернутые открытия читают его снова. но к тому времени злонамеренное пользовательское пространство могло изменить его, и вы в итоге посмотрели не на тот файл.
  2. должно быть ясно, что getcwd должен иметь метод разрешения имени, чтобы поместить его в буфер пространства пользователя. вам нужно разобраться в вызове и посмотреть, что можно изменить, чтобы поместить его в буфер ядра.

Почему вы делаете это для начала?

...