我是靠谱客的博主 俭朴未来,这篇文章主要介绍linux access_ok 用户指针检查确保系统调用传给kernel的指针是用户空间而非内核空间的地址,现在分享给大家,希望可以做个参考。

copy_from_user 与 copy_to_user 函数在使用使用user space指针的时候都会用access_ok 函数检查

检查的内容:

#define access_ok(type, addr, size)    (__range_ok(addr, size) == 0)
#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), "0" (current_thread_info()->addr_limit) 
        : "cc"); 
    flag; })
static inline void __chk_user_ptr(const volatile void *p, size_t size)
{
    assert(p >= __user_addr_min && p + size <= __user_addr_max);
}

asm C嵌入汇编:

int a=10,b;
asm("movl %1, %%eax;
     movl %%eax, %0;"
    :"=r"(b)          /*输出部*/
    :"r"(a)           /*输入部*/
    :"%eax"           /*毁坏部*/
   );

表示C语言里的“b=a;”r表示使用任意寄存器,%0、%1表示使用两个寄存器,一般只能%0~%9共十个操作数,按输出输入部变量出现顺序进行映射。寄存器用两个百分号,是因为使用了%0%1这些数字使百分号有了特殊意义,所以在操作数出现的寄存器必须用双百分表示。毁坏部里边的%eax表示eax寄存器在汇编代码块执行过程中会被改写,在执行前要保护好,这是提交给编
译器决定的。

adds %1, %2, %3

rosum = addr + size 这个操作影响状态位(目的是影响是进位标志C),以下的两个指令都带有条件CC,也就是当C=0的时候才执行。
如果上面的加法指令进位了(C=1),则以下的指令都不执行,flag就为初始值current_thread_info()->addr_limit(非0),并返回。
如果没有进位(C=0),就执行下面的指令:
sbcccs %1, %1, %0
rosum = rosum - flag - 1,也就是(addr + size) -  (current_thread_info()->addr_limit) - 1,操作影响符号位。
如果(addr + size) >= (current_thread_info()->addr_limit) - 1,则C=1
如果(addr + size) < (current_thread_info()->addr_limit) - 1,则C=0
当C=0的时候执行以下指令,否则跳过(flag非零)。
movcc %0, #0
flag = 0,给flag赋值0。
综上所述:__range_ok宏其实等价于:
如果(addr + size) >= (current_thread_info()->addr_limit) - 1,返回非零值
如果(addr + size) < (current_thread_info()->addr_limit),返回零
access_ok就是检验将要操作的用户空间的地址范围是否在当前进程的用户地址空间限制中。这个宏的功能很简单,完全可以用C实现,不是必须使用汇编。但于这两个函数使用频繁,就使用汇编来实现部分功能来增加效率。

从这里再次可以认识到,copy_from_user的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。
 

为什么?

先看一段小视频,如果内核访问用户不做access_ok会怎样?

https://v.qq.com/x/page/i1342goaqn3.html

漏洞的利用

比如内核的如下commit引入了一个严重的安全漏洞(编号CVE-2017-5123):

 

 

危害

一个攻击案例可以参考:

http://www.freebuf.com/vuls/152412.html

《Linux内核Waitid系统调用本地提权漏洞(CVE-2017-5123)的分析与利用》

修复

而内核的这个commit对其进行了修复:

显然,它只是增加了对access_ok的调用。

最后

以上就是俭朴未来最近收集整理的关于linux access_ok 用户指针检查确保系统调用传给kernel的指针是用户空间而非内核空间的地址的全部内容,更多相关linux内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部