- 对数据段特权检查
 - 对直接转移的代码段特权检查
 - 栈段的检查
 - 调用门的检查
 
 
权限问题:
 
- 由于CPL,DPL 无法完整表达权限的问题.
 例如用户程序(CPL=3)通过调用门(将调用到内核过程,从低权限到高权限)执行,此时CPL=0,此时可以为所欲为. - 因此加入RPL.此参数由操作系统来保证,CPU仅使用
 -  
- RPL: 想以哪种权限去访问, 操作系统来填写, 一个自由的参数
 
  -  
- 描述符里的DPL=CPL=CS.RPL(16位中的低2位)
 
  -  
- DPL:访问此描述符的权限
 
  
 
对于数据段的特权检查:
 
从大方向说:只要权限比数据段大 ,或者相等就OK
 实际根据以下几个步骤:
 CPL:当前CS段的RPL, RPL:请求这个数据段的权限, DPL: 数据段的权限
 
-  
- 根据CPL, RPL , DPL 这3个来检查
 
  -  
- CPL <= DPL && RPL <= DPL
 
  
 
假设有 数据段 DPL= 2:
 
| 代码段CPL=0 | 代码段CPL=1 | 代码段CPL=2 | 代码段CPL=3 | 
|---|
| RPL=0 可访问 | RPL=1可访问 | RPL=2可访问 | RPL=3 不可访问 | 
| RPL=1或2 可访问 | RPL=0 可访问 | RPL=1 可访问 | RPL=2 不可访问 | 
| RPL=3 不可访问 | RPL=2可访问 | RPL=0可访问 | RPL=1不可访问 | 
 
- 可以看到CPL=0的代码段,即使有最高权限,但如果RPL=3,也无法访问
 - 至于CPL=3的代码段,无论如何都无法访问,毕竟CPL>DPL
 
 
 对跳转或调用的代码段检查
 
这里特指 call far , jmp far
 
- jmp , call ,ret 这些都是段内的, 不需要重新加载cs , 因此不做检查
 -  
- 调用过程的CPL,RPL
 
  -  
- 转移到此处的目标描述符的DPL,C(是否是一致性代码段)
 
  - 综上CPL,RPL,DPL,C 4项参与检查
 - 又根据C 进行分别检查 if ( 1==C ){ 一致性代码段检查} else { 非一致性代码段检查}
 
 
如果C=0, 非一致性代码段的检查:
 
- CPL == DPL , 必须是相同特权级, 否则产生异常
 - 由于只能是平级跳转,因此转移前后CPL不变
 - 对于RPL: RPL<=CPL即可,毕竟RPL是一种希望用哪种方式(权限)去访问,RPL并不会影响CPL,RPL只用于检查
 
 
假设非一致性代码段的描述符:DPL=2,C=0
 
| 非一致性代码段描述符 | 代码段CPL=1 | 代码段CPL=2 | 代码段CPL=3 | 
|---|
| 现有非一致性代码段的描述符:DPL=2,C=0 | CPL不一致,RPL=0~3都不可转移 | RPL=0~2都可以转移,RPL=3则无法转移 | CPL不一致,RPL=0~3都无法转移 | 
| - | - | 成功转移后,CPL不变,RPL只用作检查,而非赋值 | - | 
 
C=1, 一致性代码段检查(依从性)
 
- RPL不参与检查
 - 满足: CPL>=DPL(一致性代码段), 也就是当前特权比目标代码段低或相等即可
 - 一旦转移后, CPL不改变,依旧保持之前调用者的
 
 
假设一致性代码段的描述符:DPL=1,C=1
 
| 一致性代码段描述符 | 代码段CPL=1 | 代码段CPL=2 | 代码段CPL=3 | 代码段CPL=0 | 
|---|
| 一致性代码段的描述符:DPL=1,C=1 | 可以访问 | 可以访问 | 可以访问 | 不可以访问 | 
 
 
综上
 
- 对于代码段, 主要还是看CPL
 - 对于数据段,需要RPL来辅助检查
 
 
栈段检查
 
 
调用门检查
 
- 通过调用门可以执行一个高于本CPL的过程
 - 调用门指向了某个代码段内的某一个过程
 - 调用门本身也有DPL,想要使用调用门,调用者的CPL<=调用门DPL,也就需要达到使用调用门的权限
 - 访问调用门可以使用 jmp far , call far
 - 一旦访问了调用门,检查顺利后,根据调用门描述符内的 段选择子获取段描述符的基址 + 调用门内的偏移地址,就这个过程的线性地址
 - 调用门就是一个描述符,格式:
 
 
| 31 ~ 16 | 15 | 14 ~ 13 | 12 | 11 ~ 8 | 7 ~ 5 | 4 ~ 0 | 
|---|
| 段内偏移高16位 | P | DPL | 0 | TYPE(1100) | 000 | 参数个数 | 
 
| 31 ~ 16 | 15 ~ 0 | 
|---|
| 段选择子 | 段内偏移低16位 | 
 
- 调用门需要4项检查:
 - 当前调用者的CPL
 - 调用门选择子RPL (操作系统 自己维护)
 - 调用门描述符DPL
 - 目标代码段描述符的DPL
 - 检查2步:
 -  
- 首先要满足 本身能够访问调用门:CPL <= 门DPL, RPL <= 门DPL
 
  -  
- 下表格:
 
  
 
| 指令 | 一致性代码段 | 非一致性代码段 | 
|---|
| call far | 代码段描述符DPL<=CPL | 代码段描述符DPL<=CPL | 
| jmp far | 代码段描述符DPL<=CPL | 代码段描述符DPL=CPL | 
| call far指令,栈切换 | CPl不发生变化,栈不切换 | CPL变成目标代码段的DPL,栈需要切换 | 
| jmp far 指令,栈切换 | CPL不变,栈不切换 | 由于DPL=CPL,栈不切换 | 
 
- 再一次的强调,权限无法从高到低
 - 只有CPL变了,栈才会变,因此上面只有当call指令调用 非一致性代码段的时候, 才会切换栈, 除非CPL=DPL(例如CPL=0,目标代码段描述符的DPL=0)
 - 综上:
 - 对于一致性代码段: CPL>=目标代码段DPL, 不论JMP,CALL, 转移后CPL不变,栈不变
 - 对于非一致性代码段:
 -  
- CALL指令要求: CPL >= 目标代码段DPL , CPL变成目标代码段DPL, 切换栈(除非CPL=DPL,那么栈不变)
 
  -  
- JMP 指令: CPL = 目标代码段DPL , CPL不变,栈不变
 
  - 看上去内容有点多,需要到处判断,一会门检查,一会代码段DPL检查,还要考虑栈切换的问题
 - 实际没那么麻烦,也就2条需要注意的
 -  
- 一个是有权限访问门 ,也就是本身权限至少要与门相等 数值上: CPL <= 门DPL, RPL <=门DPL
 
  -  
- 调用者代码段的权限,要低于代码段或相等,也就是低权限到高权限,那么数值上: CPL>= 目标代码段DPL
 
  - 至于jmp 和 call 的区别不用记, 让CPU产生保护性异常(GP)告诉我们即可,一旦产生异常说明此处jmp 指令有问题了( CPL != DPL)
 - 对于栈的问题,一句话就解决,CPL一变, 栈跟着变 ; CPL不变,栈也不变
 
 
调用门的首次检查例子:
 
| 门描述符 | 代码段A | 代码段B | 代码段C | 代码段D | - | 
|---|
| 门DPL=3 | CPL=3 | CPL=2 | CPL=1 | CPL=0 | 代码段A,B,C,D任意+RPL=0~3都可以访问 | 
| 门DPL=2 | CPL=3,RPL=0~3都无法访问 | CPL=2,RPL=0~2可以访问,RPL=3无法访问 | CPL=1,RPL=0~2可以访问,RPL=3无法访问 | CPL=0,RPL=0~2可以访问,RPL=3无法访问 | A的CPL权限不足以访问门DPL;B,C,D的CPL全部满足,同时:RPL<=门DPL |