1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > CPU段访问控制:特权级(RPL CPL DPL)和代码段一致性

CPU段访问控制:特权级(RPL CPL DPL)和代码段一致性

时间:2020-02-08 12:44:16

相关推荐

CPU段访问控制:特权级(RPL CPL DPL)和代码段一致性

最近笔者回顾CPU硬件的段访问控制机制,重新看到了代码段一致性问题。虽然目前操作系统没有应用分段机制,但了解其运行原理仍然具有吸引力XD。本片文章,我们就来理清CPU段访问控制中,代码段一致性的概念。

Privilege Level 特权级

首先,应当分清三个特权级的基本概念:

DPL (Descriptor Privilege Level 段描述符特权级): 定义于**段描述符 (Segment Descriptor)**中,对于不同的段,DPL的意义不尽相同

Data Segments 数据段:访问该段的最低特权等级。如果程序特权级过低,则不可访问该段Stack Segment 栈段:访问该段的特权等级(需要相同,否则触发general-protection fault#GPNon-conforming Code Segments 非一致性代码段:Direct Call(直接跳转,不经过 call gate)时,caller需要的特权等级(必须相同Call gate & TSS:访问该Call gate / TSS 的最低特权等级,低权限程序无权使用高权限服务。invoking Code Segments via Call Gates 通过调用门调用的代码段:访问该段的最高等级,这里与一般设置不同。事实上,通过调用门调用代码,通常是低特权级程序请求高特权服务时使用,这个设计也符合以上应用需求。详见下一节。

RPL (Requested Privilege Level 请求特权级):定义于段选择子中(最后两位),表示当前发起请求的特权级(以何种身份发起请求)。可以用于防止低权限程序借用高权限代码破坏高权限段

CPL (Current Privilege Level 当前特权级): 定义于当前程序的CS中,即CPL = CS.RPL。表示当前运行程序的特权级。

特权级检查

特权级检查,在将Segment Selector放入Segment Register时进行。按照上述规则执行检查

如果检查不通过,通常会产生异常:General-protection fault (#GP)

关于跳转中各种情况的分类解析,详见下节:控制权转移

控制权转移

有多种途径进行控制权的转移:

应用程序执行的指令:JMPCALLRETINT nIRET来自硬件的事件:中断 interrupt异常 exception

在跨段的跳转中,会执行特权级检查,跳转主要有以下几种情形:

direct jump

当通过CALL或者JMP直接跳转到另一个代码段时,会检查CPL、目标段描述符的DPL、目标段选择子(Segment Selector)的RPL。此时按照Target Segment Descriptor的C标志位(Conforming 一致性),可分为两种情况:

一致性代码段(Conforming Code Segment):CPL 必须大于等于DPL(低权限代码借用高权限代码段)。即使跳转到DPL较高(数字小)的一致性代码段时,CPL也不会改变,因此不会进行堆栈切换(Stack-Switch)

可以将一些不需要系统保护的部分API(数学函数、Exception Handlers等)设置在Conforming Segment中,由于CPL不改变,避免其影响高特权级数据

非一致性代码段(non-Conforming Code Segment):CPL一定与Target DPL相同,且RPL小于等于CPL (特权级更高,请求方必须以高权限访问该段)(如果RPL比CPL更低(大于),则表示请求来自低特权级程序,访问失败)

CPL=DPLRPL≤CPLCPL=DPL\\ RPL\le CPL CPL=DPLRPL≤CPL

一般情况下,大部分其他Code Segment都应当设定为非一致性的

Call Gate

**Gate(门)**是一种系统段,它的Segment Descriptor 称为 gate descriptor。通常有四种门:Call Gates 调用门Trap Gates 陷门Interrupt Gates 中断门Task Gates 任务门。这里只讨论Call Gates。

typedef struct _CALL_GATE{USHORT OffsetLow;USHORT Selector;UCHAR NumberOfArguments:5;UCHAR Reserved:3;UCHAR Type:5; // 01100 in i386, 00100 in i286UCHAR Dpl:2;UCHAR Present:1;USHORT OffsetHigh;}CALL_GATE,*PCALL_GATE;

JMPCALL指令的目标段寄存器存放的选择子是Call Gate时,视为使用调用门(忽略偏移,在Gate Descriptor中已经指定)。

经由Call gate调用API时,CPL和RPL都要小于等于(特权级高于)Call gate的DPL

Call使用调用门时,CPL和RPL都要大于等于Call gate对应Segment的DPL(非Call Gate的DPL)。如果调用者权限更高,则不能通过调用门进行调用;JMP使用时,若target segment是non-conforming segment,则target DPL必须与CPL相同。

按照目标代码段区分:

一致性代码段:

CPL≥targetDPLCPL \ge target\ DPL CPL≥targetDPL

CPL不改变,没有特权级转换。

非一致代码段:

JMP跳转

CPL=targetDPLRPL≤CPLCPL = target\ DPL \\ RPL \le CPL CPL=targetDPLRPL≤CPL

事实上,由于RPL被清零,条件二一定被满足。

跳转后,CPL不变,没有特权级变化

CALL跳转

CPL≥targetDPLRPL≤CPLCPL \ge target DPL \\ RPL \le CPL CPL≥targetDPLRPL≤CPL

事实上,由于RPL被清零,条件二一定被满足。

由低等级程序通过调用门请求访问高等级代码。

跳转后,CPL=Target DPL产生特权级变化。(这是目前唯一产生变化的跳转方式)。

由程序中返回

使用RET指令进行跨段返回时,会检查返回目标的Segment Descriptor中的DPL是否大于等于当前CPL(权限较低),否则说明堆栈中数据错误。

CPU使用栈存储的返回CS中的RPL进行权限判断,检查是否需要改变权限。如果需要改变权限,则同时进行堆栈切换。所以,处理器会先将CS:IP载入,再载入SS:SP,同时,CPU还会恢复其他段寄存器的权限,并将指向更高权限的段的选择子置为空。

Real World

从设计原理上,RPL只是提供了一个CPU和操作系统之间的协议:CPU负责检查特权级、操作系统负责RPL的正确性。内核知道用户态请求操作的段选择子,也就能检查/修改对应的RPL,保证其与需要的权限相同。

通常,我们不会使用分段的保护机制,硬件架构建议将所有段的RPL设为0,这样可以通过所有RPL判断,不会进行这部分检查。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。