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 的计算结果为非零。
当我在用户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 的计算结果为非零。