fopen(NULL, "r") 在内核 __range_ok(NULL, 1) 变为真时崩溃

fopen(NULL, "r") crashes as kernel __range_ok(NULL, 1) got true

当我在用户space中打开一个NULL文件时,我们的系统(armv7平台,基于linux内核3.2.16)会由于"unhandled Null pointer"[=13=而崩溃]

我知道我不应该在我的应用程序中这样做,我只是好奇为什么 glibc 和系统调用 sys_open 都没有检查 NULL 指针,而它实际上依赖于 MACRO access_ok in strncpy_from_user。调用顺序:sys_open->do_sys_open->get_name->do_getname->strncpy_from_user->access_ok->range_ok.

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
    long ret;

    if (force_o_largefile())
        flags |= O_LARGEFILE;

    ret = do_sys_open(AT_FDCWD, filename, flags, mode);
    /* avoid REGPARM breakage on x86: */
    asmlinkage_protect(3, ret, filename, flags, mode);
    return ret;
}

long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
    struct open_flags op;
    int lookup = build_open_flags(flags, mode, &op);
    char *tmp = getname(filename);
    int fd = PTR_ERR(tmp);

    if (!IS_ERR(tmp)) {
            fd = get_unused_fd_flags(flags);
            if (fd >= 0) {
                    struct file *f = do_filp_open(dfd, tmp, &op, lookup);
                    if (IS_ERR(f)) {
                            put_unused_fd(fd);
                            fd = PTR_ERR(f);
                    } else {
                            fsnotify_open(f);
                            fd_install(fd, f);
                    }
            }
            putname(tmp);
    }
    return fd;
}

char *getname(const char __user * filename)
{
    return getname_flags(filename, 0, 0);
}

static int do_getname(const char __user *filename, char *page)
{
    int retval;
    unsigned long len = PATH_MAX;

    if (!segment_eq(get_fs(), KERNEL_DS)) {
            if ((unsigned long) filename >= TASK_SIZE)
                    return -EFAULT;
            if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
                    len = TASK_SIZE - (unsigned long) filename;
    }

    retval = strncpy_from_user(page, filename, len);
    if (retval > 0) {
            if (retval < len)
                    return 0;
            return -ENAMETOOLONG;
    } else if (!retval)
            retval = -ENOENT;
    return retval;
}

static long
strncpy_from_user(char *dst, const char __user *src, long count)
{
    if (!access_ok(VERIFY_READ, src, 1))
        return -EFAULT;
    return __strncpy_from_user(dst, src, count);
}

#define access_ok(type,addr,size)   (__range_ok(addr,size) == 0)

/* We use 33-bit arithmetic here... */
#define __range_ok(addr,size) ({ \
     unsigned long flag, roksum; \
     __chk_user_ptr(addr);   \
     __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \
             : "=&r" (flag), "=&r" (roksum) \
             : "r" (addr), "Ir" (size), "" (current_thread_info()->addr_limit) \
             : "cc"); \
     flag; })

我想当我调用 __range_ok(0, 1) 时我应该得到 0,所以一切都会很好,只是出现了一个 -EFAULT 错误。

我看不懂__range_ok中的汇编,所以我不知道为什么我在这里得到1,有人可以帮忙吗?

谢谢!!

Linux 内核代码并不总是最直接的。 __range_ok() 具有以下语义:

/*
 * Test whether a block of memory is a valid user space address.
 * Returns 0 if the range is valid, nonzero otherwise.
 */
int __range_ok(unsigned long addr, unsigned long size);

因此,如果对该范围的访问无效,您期望 __range_ok() 的计算结果为 0,而在这种情况下,macro/function 的计算结果为非零。