Chapter 4.5 - 保护
Created by : Mr Dk.
2019 / 07 / 26 14:11
Nanjing, Jiangsu, China
4.5 保护
用于保护各个任务免受相互之间的干扰。当程序对错误的内存空间执行了一次非期望的引用,保护机制可以阻止这种操作并报告此类事件。
4.5.1 段级保护
在使用保护机制时,每次内存引用都将受到检查。检查操作与地址变换并行操作,不会影响到 CPU 的性能。保护检查可分为:
- 段界限检查
- 段类型检查
- 特权级检查
- 可寻址范围限制
- 过程入口点限制
- 指令集限制
所有违反保护的操作都将引起异常。
4.5.1.1 段限长 Limit 检查
用于防止寻址到段外内存位置。段限长的有效值依赖于颗粒度标志 G,以及段描述符中的 20-bit Limit:
- 1B 颗粒度下 -
0x00000-0xfffff
- 4KB 颗粒度下 -
0x00000fff-0xffffffff
这只是段长度限制,因此实际上段内偏移 0x000-0xfff
都是有效的、有效 Limit 值应当是段中允许被访问的最后一个地址,比段长度少 1。超出有效地址范围的访问将导致一个 一般保护异常。
下扩数据段?
CPU 还会检查描述符表的长度,GDTR、IDTR、LDTR 寄存器中包含 16-bit 的限长。防止程序访问描述符表之外的描述符。
4.5.1.2 段类型 TYPE 检查
段描述符的 S 标志和 TYPE 字段含有类型信息:
- S 指出描述符是系统描述符还是应用描述符
- TYPE 定义代码、数据、系统等描述符的具体类型
当操作段选择符和段描述符时,CPU 随时检查类型信息。段寄存器只能存放特定类型的描述符,指令只能使用某些预定义的方法来访问某些段。
4.5.1.3 特权级
CPU 可以识别 4 个特权级,0-3
,数值越大,特权越小。利用特权级可以防止较低特权级的程序访问较高特权级的段,除非是在受控的条件下。当 CPU 检测到违反特权级的操作时,产生一个一般保护性异常。CPU 可以识别三种类型的特权级:
当前特权级 CPL,存放在 CS 或 SS 寄存器中,通常 CPL 就是当前代码段的特权级
描述符特权级 DPL,是一个段或门的特权级,存放在描述符的 DPL 字段中,根据访问的段或门的类型不同,DPL 也有不同的含义
请求特权级 RPL,是一种赋予段选择符的超越特权级,存放在段选择符中
- CPU 会同时检查 RPL 和 CPL,以确定是否允许访问一个段
- 始终使用 RPL 和 CPL 中的较低特权与 DPL 进行比较
RPL 确保高特权级的代码不会代表应用程序去访问一个段。除非应用程序自己具有访问这个段的权限。
4.5.2 访问数据段时的特权级检查
为访问数据段中的操作数,段选择符需要被加载进数据段寄存器或堆栈段寄存器。把段选择符加载进段寄存器之前,CPU 会进行特权级检查,只有 DPL ≥ (CPL & RPL),段选择符才会被加载,否则产生一个一般保护性异常。
使用堆栈段寄存器也会进行特权级检查,此时,CPL、RPL、DPL 都必须相同,否则产生一个一般保护性异常。
4.5.3 代码段之间转移控制时的特权级检查
将程序控制权从一个代码段转移到另一个代码段,代码段的段选择符需要加载进 CS 中。控制转移使用指令 JMP
、RET
、INT
和 IRET
实现,JMP 或 CALL 可以利用以下四种方法之一来引用另一个代码段:
- 目标操作数含有目标代码段的段选择符
- 目标操作数指向一个调用门描述符,该门描述符中含有目标代码段的选择符
- 目标操作数指向一个 TSS,TSS 中含有目标代码段的选择符
- 目标操作数指向一个任务门,该门指向一个 TSS,TSS 含有目标代码段的选择符
4.5.3.1 直接调用或跳转到代码段
- 近转移:在当前代码段中执行程序控制转移,不会执行特权级检查
- 远转移:将控制转移到另一个代码段中,CPU 一定会进行特权级检查
进行特权级检查时,验证 4 种特权级信息:
- CPL
- 被调用过程目的代码段段描述符中的 DPL
- 目的代码段的段选择符中的 RPL
- 目的代码段段描述符中的一致性标志 C
访问非一致代码段时:
- CPL 必须等于 DPL,否则引发一个一般保护异常
- RPL 在数值上必须 ≤ CPL
- 当非一致代码段的选择符被加载进 CS 寄存器时,CPL 不会改变
访问一致代码段时:
- 只有 CPL < DPL 时,才会产生一般保护异常
- 忽略对 RPL 的检查
- CPL 不改变
大多数代码段都是非一致代码段,程序的控制权只能转移到具有相同特权级的代码段中,除非通过调用门。
4.5.3.2 门描述符
对具有不同特权级的代码段提供受控的访问,共有四种类型:
- 调用门 - Call Gate
- 陷阱门 - Trap Gate
- 中断门 - Interrupt Gate
- 任务门 - Task Gate
其中,调用门用于在不同特权级之间实现受控的程序控制转移。调用门描述符可以存放在 GDT 或 LDT 中,不能放在 IDT 中。调用门的功能:
- 段选择符 - 指定要访问的代码段
- 偏移值 - 指向对应代码段中程序的入口处
- DPL - 指定调用门的特权级,从而指定了通过调用门访问的程序所要具备的特权级
- P - 指定调用门描述符是否有效
- 参数个数 - 指明发生堆栈切换时,从调用者堆栈复制到新堆栈中的参数个数
Linux 内核中没有用到调用门。。。。。。
4.5.3.3 通过调用门访问代码段
为了访问调用门,需要为 CALL 或 JMP 提供一个远指针:
- 段选择符用于指定调用门
- 偏移值可以是任意的,因为 CPU 并不会使用
段选择符定位了调用门描述符。调用门描述符中的段选择符定位到了目的代码段的段描述符,调用门描述符中的偏移值是目的代码段中的程序入口偏移,从而形成了目的代码段中指定程序入口处的线性地址。通过调用门进行控制转移时,CPU 对 4 种特权级进行检查:
- CPL
- 调用门选择符中的 RPL
- 调用门描述符中的 DPL
- 目标代码段描述符中的 DPL
- 目标代码段描述符中的一致性标志 C
其中,CPL 和 RPL 应当 ≤ 调用门描述符的 DPL。DPL 指明了调用程序能够访问调用门的最低特权级。如果调用者与调用门之间的特权级检查通过,CPU 将 CPL 与目的代码段描述符的 DPL 进行检查:
- CALL 指令可以通过调用门将控制转移到 特权级更高 的 非一致性代码段 中 (DPL < CPL)
- JMP 指令只能把控制转移到 DPL == CPL 的 非一致性代码段 中
- CALL 和 JMP 都可以把控制转移到 特权级更高 的 一致性代码段 中 (DPL ≤ CPL)
如果控制转移到了更高特权级的非一致代码段中,CPL 被设置为目的代码段的 DPL,引发堆栈切换。而如果控制转移到了更高特权级的一致性代码段上,CPL 不会改变,因此不会引起堆栈切换。调用门可以让一个代码段中的过程被不同特权级的程序访问,比如一个代码段中的 OS 代码可以被自身或应用程序执行。
4.5.3.4 堆栈切换
当程序控制被转移到 更高特权级 的 非一致性代码 中,CPU 会自动切换到目的代码段特权级的堆栈中:
- 防止高特权级程序由于栈空间不足而崩溃
- 防止低特权级程序通过共享的堆栈有意或无意地干扰高特权级程序
每个任务定义最多 4 个栈,分别对应一个特权级。每个栈都位于不同的段中,使用段选择符和段内偏移指定。OS 负责为所用到特权级建立堆栈和堆栈段描述符,并在 TSS 中设置初始指针值。当通过调用门造成特权级改变时,CPU 切换堆栈,并开始在新的特权级上执行被调用过程。切换堆栈的过程如下:
- 根据目的代码段的 DPL (即新的 CPL) 从 TSS 中选择新栈的指针
- 检查新栈指针的段描述符特权级和类型是否有效,否贼产生一个无效 TSS 异常
- 临时保存当前 SS、ESP,将新栈的段选择符加载到 SS、ESP,将临时保存的 SS、ESP 压入新栈
- 把调用门描述符中指定参数个数个参数从调用栈复制到新栈中 (最大 31 个)
- 把返回指令指针 (CS + EIP) 压入新栈,把新代码段选择符加载到 CS 中,把调用门描述符中的偏移量加载到 EIP 中,开始执行被调用过程
4.5.3.5 从被调用过程返回
指令 RET 用于执行近返回、同特权级远返回、不同特权级远返回
- 近返回仅在当前代码段中转移控制权,CPU 仅进行段界限检查
- 同特权级远返回,CPU 从堆栈中弹出返回指令指针,CPU 还是会进行特权级检查,应付一些问题
- 特权级改变的远返回仅允许返回到低特权级程序中
CPU 会执行以下步骤:
- 检查 CS 寄存器中 RPL 的值,确定返回时特权级是否需要改变
- 弹出堆栈上保存的 CS 和 EIP,并加载,同时执行代码段描述符和选择符的特权级和类型检查
- 如果返回会改变特权级,那么在 ESP 寄存器上加上参数个数,丢弃栈上的参数,此时 ESP 指向原调用者的 SS 和 ESP
- 把保存的 SS 和 ESP 加载到 SS 和 ESP 中,切换回调用者堆栈;此时被盗用着堆栈的 SS 和 ESP 被抛弃
- 检查段寄存器,如果有 DPL < 新 CPL 的段,那么 CPU 用空选择符来加载该段寄存器
4.5.4 页级保护
分页机制只识别两级权限:
- 0、1、2 被归类为超级用户级
- 3 被作为普通用户级
在超级用户级执行的程序对任何页面都具有可读、可写、可执行的权限 (包括用户级的页面)。首先,所有段级保护被检查和测试;如果通过检查,再进行页级保护检查。分段和分页保护机制类似串行电路,只要有一个不通过,就不会通过检查。页表中标志作用于单个页面,页目录项中的标志作用于所映射的所有页面。组合保护属性由两者属性相“与”构成。
4.5.4.1 修改页表项的软件问题
OS 软件修改页表项内容所需遵守的规则:页表项被缓存在 TLB 中,处理器不维护 TLB 与内存中页表的一致性,需要 OS 来确保。OS 必须在改动过页表后刷新 TLB,以保证一致性 - 通过重新加载 CR3 寄存器实现。
特殊情况:当不存在页面的表项被修改时,不需要重新刷新 TLB,因为无效的表项不会被存入 TLB。
4.5.5 组合页级和段级保护
CPU 首先执行段级保护,再执行页级保护。若在任何一级检测到一个保护违规错误,则会放弃内存访问,并产生一个异常。页级保护不能替代或忽略段级保护,而是被用于增强段级保护。