之前总觉得 x86 特权级校验的规则很难记住,但是今天归纳了一下发现其机制非常简单。
选择子和描述符
在进入正题之前,首先说一说选择子和描述符。
段描述符表示是一个段的地址映射、权限、以及其他的属性。
除此以外,还有门描述符、TSS描述符等,他们都保存在DT表里面,DT表基地址保存在DTR寄存器中(比如GDTR、LDTR)。
描述符本身通常并不随当前进行的任务而发生变化,他记录被访问对象的属性。
而选择子指向一个描述符,除此以外,还记录访问者的属性。
保护了什么
保护模式到底保护了什么?一句很简单的话就是:
被访问对象要被比他权限更高(或者相等)的访问者所访问。
这句话很简单,而且理解了这句话,就理解这三个特权级别的区别。
RPL
RPL,全称是 Requested Privilege Level,其位于段选择子上,表示访问者访问该描述符所具有的权限。
DPL
DPL,全称是 Descriptor Privilege Level,听名字就知道其位于描述符上,表示被访问对象的权限。
一个例子
下面举个违背保护规则的例子,比如我有一条指令 mov rax, qword ptr es:[6400H]
想访问 es 段的内存,此时,es 段选择子的RPL 是 Ring2,但是 es 段描述符 (被访问内容)是 Ring1,就违法了上一节所说的规则,就会触发 CPU 的保护异常 (Protection Fault)。
CPL
作为这三个缩写词中最常见的词,为什么最后说CPL呢?其实在 x86 当中,CPL 只是一种特殊的 RPL,他就是 CS 段以及 SS 段的RPL。(为什么CS和SS放在一起呢,因为通常来说当前指令和栈都是和当前任务密切相关的,并且通过门进行调用时,会自动从 TSS 段中取出新的 SS 值。所以 CS 和 SS 段是放在一起说的。)
而执行代码时,本质是对 cs:rip
处的内存进行访问,权限检查也和上面类似,取 cs 的RPL (或者说是CPL)和 段描述符的 DPL 进行比较。
保护规则的补充
定义了 CPL 之后,就可以对上面的规则进行补充了,如下图所示:
在访问一个段时,需要:
- CPL <= DPL
- RPL <= CPL
只有当这两个条件同时满足时,才能访问成功。否则就会触发保护异常。