天天看点

copy_from_user copy_to_user的权限控制

在计算机操作系统中核心的一个概念就是特权级别,在x86上即Ring0-Ring3.在Ring 0拥有最高特权,并且可以和最多的硬件直接交互,而在Ring 3中只能访问一部分资源,访问其他资源会引起指令异常.

在linux系统上Ring 3分配给应用,内核使用Ring 0,内核空间管理系统中所有的资源和设备,应用只能通过系统调用陷入到内核向其发出请求,由内核来代为完成对硬件资源的操作。

特权级别也引申出了地址空间的划分,典型的32bit系统中寻址空间为4G,划分为用户空间和内核空间:3/1,每个普通进程看起来独享4G全部地址空间,但是有权限访问的只有1-3G,无法访问3-4G的内核地址空间,系统通过Ring权限的控制,保证了用户态中不能访问内核空间数据,不同进程间的地址空间隔离.

copy_from_user copy_to_user的权限控制

反过来问,内核态中是否可以访问用户空间数据?

内核不能随意访问用户空间的数据,这里传达了两个信息:内核可以访问用户空间,内核只能通过特定的api完成对用户空间数据的访问。

在页表项pte中有标志位记录用户态是否可以访问当前page:_PAGE_BIT_USER,当它置位的时候标明允许用户态访问,反之则禁止,一般用在禁止用户态访问内核地址.

在理论上看,内核的权限较高,高权限空间有权访问低权限空间,只是用户空间的对应的物理内存和内核的物理内存有很多不一样的地方,内核空间使用的物理内存是真实的,当分配页时它返回成功则一定有物理页给他使用,但是用户空间看到的只是虚拟地址空间,内核对其提供的物理内存有很多虚假的地方,合法的用户空间地址不一定有对应的物理内存,例如mmap,sbrk等申请的内存在没有实际写之前是没有为其分配物理内存的,堆和栈地址空间的物理内存可能已经被swap出去了,文件映射区域还没有关联到对应的page cache,当从内核访问上述这些地址空间时可能就会触发地址异常;另外内核态中通过memcpy这类接口访问非法的用户空间地址会产生oops,一个普通的程序导致整个系统的异常属于设计上的失败。

所以不能通过memcpy这种操作访问用户空间数据,而是需要通过专用的接口copy_from_user/copy_to_user。

重点

copy_from_user/copy_to_user可能会触发缺页异常,在缺页异常中会实际分配物理页又可能会触发页回收,所以这个api它返回的时间不定,还可能会引起睡眠。

在x86上copy_from_user/copy_to_user相对来说简单一些,简单过程如下:

1.检查用户空间地址的合法性,是否在有效的VMA区域中

2.尝试访问用户空间地址,如果没有对应的物理内存则触发缺页异常,进行修复,这部分代码并没有在copy_from_user中直接调用而是通过MMU的异常间接触发的。无法修复缺页异常时

search_exception_table

会查找修复代码,即操作失败之后如何处理的代码,它会根据发生访问错误的地址跳转到fixup部分代码copy_user_handle_tail,将剩余的内核地址空间进行清零操作,恢复寄存器状态并返回。

3.操作失败时copy_from_user不再访问用户空间地址,而是将目标地址全部清零并返回错误。

arm64上的PAN

在arm64中有禁止内核访问应用数据的功能,CONFIG_ARM64_PAN,它使用权限标志位限制了内核访问用户空间数据,在访问之前清除掉PAN标志位,访问结束后恢复PAN标志位。

reflink:http://siguza.github.io/PAN/

PAN:Privileged Access Never, 在armv8.1之后arm64添加了PAN特性,内核4.3以后软件支持该特性,它不允许内核态中随意访问用户空间地址.在armv8中使用EL(Exception Level)代替了原来的几种模式,用户空间位于EL0,内核OS位于EL1, Hyper OS位于EL2,当处于EL1和EL2中访问一个用户空间地址时会触发权限异常,可以使用copy_from_user/copy_to_user接口,里面封装了对该标志位的操作.

About PSTATE.PAN
When the value of PSTATE.PAN is 1, any privileged data access from EL1, or EL2 when HCR_EL2.E2H is 1, 
to a virtual memory address that is accessible at EL0, generates a Permission fault.
           

intel的SMAP

reflink:https://lwn.net/Articles/517475/

2014年Intel Broadwell微架构中引入的的SMAP(Supervisor Mode Access Proteaction)功能,用途是禁止内核因为自身错误意外访问用户空间的数据,以避免一些内核漏洞所导致的安全隐患。该扩展在CR4控制寄存器中定义了一个新的SMAP位。设置该位后,在特权模式下运行时访问用户空间内存的任何尝试都将导致page fault。如果设置了AC标志位,则SMAP保护有效,否则允许访问用户空间内存。两个新指令STAC(Set AC Flag)和CLAC(Clear AC Flag)被用来相对快速地操纵该标志位(对,这就是cisc的风格,专有指令多一些,执行稍快一些)。

与之类似是在2012年Ivybridge微架构引入的指令SMEP(Supervisor Mode Execution Proteaction).SMEP可用于特权模式(内核空间)无意中执行用户空间代码,SMAP将此保护扩展到普通的读和写访问。

x86中的地址访问异常处理

在编译内核的时候,通过gcc的限定语

.section

创建异常表

__ex_table

,起始地址是__start___ex_table,结束地址是__stop___ex_table,里面是exception_table_entry类型的数组,是一对执行地址和fixup代码的地址。当地址异常发生的时候,触发缺页异常,如果无法修复则会通过异常地址和

search_exception_table

搜索到fixup代码,这部分的处理类似于java中的catch,go的recover机制,将系统从异常状态中尽量恢复到正常状态。当copy_form_user/copy_to_user发生的时候会通过copy failed来修复寄存器状态,保证内核能够正常运行。

由于水平有限,因此不能保证作品内容准确无误。如果你发现了任何错误,请不吝指出。

继续阅读