我一直在做一些研究,我对这个宏有点困惑。希望有人能给我一些指导。我有一些 ioctl 代码(这是我继承的,而不是编写的),如果在继续从用户空间复制数据之前检查 access_ok()
,它做的第一件事是:
#define __lddk_copy_from_user(a,b,c) copy_from_user(a,b,c)
#define __lddk_copy_to_user(a,b,c) copy_to_user(a,b,c)
long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case COMMAND:
if(! access_ok(VERIFY_READ, (void *)arg, sizeof(Message_par_t)))
return(retval);
if(! access_ok(VERIFY_WRITE, (void *)arg, sizeof(Message_par_t)))
return(retval);
argp = &Command;
__lddk_copy_from_user( (void *) argp,(Command_par_t *) arg, sizeof(Command_par_t));
所以代码工作得很好,但我不确定是否需要它。第一个问题来自access_ok返回的这段描述:
- 如果该区域可能可访问(尽管访问可能仍会导致 -EFAULT),则该函数返回非零值。此函数只是检查地址是否可能在用户空间中,而不是在内核中。
所以这意味着除了确保我们正在检查的指针可能在用户空间中初始化之外,它真的什么都不做?因为我们知道除了用户空间调用之外我们不能进入这个函数,除非我们打开一个有效的文件描述符到这个设备,否则它不会发生,这真的需要吗?真的比确保我们没有得到 NULL 指针更安全吗?
第二个问题来自这个描述:
- 类型参数可以指定为 VERIFY_READ 或 VERIFY_WRITE。 VERIFY_WRITE 符号还标识内存区域是否可读和可写。
这是否意味着我的代码中的第一次检查是多余的?如果我们要检查可写区域,我们可以免费获得可读区域吗?
我使用的是 x86 架构,所以 access_ok() 和 __range_no_ok() 的定义来自/usr/src/linux-3.1.10-1.16/arch/x86/include/asm/uaccess.h,如下所示:
#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))
#define __range_not_ok(addr, size) \
({ \
unsigned long flag, roksum; \
__chk_user_ptr(addr); \
asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0" \
: "=&r" (flag), "=r" (roksum) \
: "1" (addr), "g" ((long)(size)), \
"rm" (current_thread_info()->addr_limit.seg)); \
flag; \
})
最佳答案
如果 __lddk_copy_from_user()
只是调用 copy_from_user()
,那么 access_ok()
检查是多余的,因为 copy_from_user()
自己执行这些检查。
access_ok()
检查确保用户空间应用程序不会要求内核读取或写入内核地址(它们是完整性/安全检查)。仅仅因为用户空间提供了一个指针并不意味着它绝对是一个用户空间指针 - 在许多情况下,“内核指针”仅仅意味着它指向虚拟地址空间的特定区域。
此外,使用VERIFY_WRITE
调用access_ok()
意味着VERIFY_READ
,因此如果您检查前者,则不需要同时检查后者.
截至this commit in 2019 ,
access_ok()
不再有 type
参数,因此 VERIFY_WRITE
与 VERIFY_READ
点没有实际意义。
关于c - 使用 linux 宏 access_ok() 有什么意义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12357752/