【以下内容都为前辈智慧,我仅做了一点排版,同时增加了一些图片,便于理解】
【不知道这篇文章的最初出处是哪里,在此向原作者致歉!】
intel手册3B卷4.10.4节详细论述了为什么要有RPL的原因。
保护模式中最重要的一个思想就是,通过分级把代码隔离了起来。不同的代码在不同的级别,大多数情况下都只和同级代码发生关系。
Intel的80286以上的cpu可以识別4个特权级(或特权层) ,0级到3级。数值越大特权越小。一般用把系统内核放在0级,系统的其他服务程序位于1、2级,3级则是应用软件。一般情况下代码都在自己的级别下做自己的工作,同一级别之间可以相互访问,而一般是不允许不同级别的代码间随意访问的。
但有时候不同级别的程序之间一定要访问,比如系统的接口函数等,必须能够使得应用程序能够随意调用。于是Intel将代码分为:
1.非一致码:受到隔离的代码,只能在同一级别间相互访问
2.一致码:不受到隔离的就是,允许被同等级或低等级代码调用
至于这部分我们下回再详细说,这里主要搞清楚RPL、DPL、CPL之间的关系。 Intel设置DPL、RPL、CPL以实现分级和权限检查。
DPL:描述符特权(Descriptor Privilege Level), 存储在段描述符中的权限位,用于描述代码的所属的特权等级,也就是代码本身真正的特权级。一个程序可以使用多个段(Data,Code,Stack),也可以只用一个code段等。正常的情况下,当程序的环境建立好后,段描述符都不需要改变——当然DPL也不需要改变,因此每个段的DPL值是固定。
RPL:请求特权级RPL(Request Privilege Level), RPL保存在段选择子的最低两位。RPL说明的是进程对段访问的请求权限,意思是当前进程想要的请求权限。RPL的值由程序员自己来自由的设置,并不一定RPL>=CPL,但是当RPL
二、为什么还要有RPL?
那么,我们弄明白RPL的效果后,还是要发问,究竟为什么在CPL之外增加一个RPL?
Intel手册上的解释为:The RPL can be used to insure that privileged code does not access a segment on behalf of an application program unless the program itself has access privileges for that segment.(RPL能够用来确保具有特权级的代码不会代表另一个应用程序去访问一个段,除非那个应用程序具有访问那个段的权限.)
比方说:A进程的DPL为0,C进程的DPL为1,现在有一个B进程他的DPL为2,这B进程想委托A进程(外围程序可以访问一致代码段的内核)去访问C的数据(内核可以访问外围数据)
如果没有RPL来限制的话,这样的委托访问是可以成功的,但这样是非常不安全的。
有了RPL以后,A进程在访问C的时候还要受到RPL的约束,此时可以将访问C的选择子的RPL设为B的DPL,这样A的访问权限就相当为EPL=max(RPL,DPL)=2,这样他就无法代表B去越权访问C了。(那还要委托A干嘛?反正B如果不够权限,委托谁都没用;如果B有权限,不用委托别人也可以啊?)
有RPL的情形,CPU同时检查CPL和RPL来判断是否允许对一个段的访问。在低特权级代码调用高特权级代码时,你可以把RPL认为是调用者CPL的影子。即使高特权级代码在运行,但它是应低特权级代码的请求,作为低特权级代码的代理在执行任务,在必要的时候,RPL作为对CPL的覆盖,可以削弱当前执行代码的可访问的区域,从而保证高特权级代码不会代表低特权级代码去访问一个后者没有访问权限的段。
这里有一个问题,就是低特权级代码在向高特权级代码传递段选择子时,可以任意设置RPL。所以x86处理器有一条专门的指令ARPL用来纠正RPL。
三、农民的例子
一个农民(低特权级)请县长(高特权级)打听一种超级种子,如果找到的话帮忙拿一点回来,听闻这种超级种子可让收成倍增。县长说:好!我认识很多当官的,我可以帮你打听一下哪里有,但是有些地方如果需要表示身分的话我只能说我是农民的代理人。县长利用自己的身份很容易找到了种子在哪里---找的时候没有人问起他代表谁。县长问种子管理员可不可以给他一点,管理员说种子不能给农民因为种子还在试验阶段,我们可以给县长让他们带回当地的专家来帮忙一起做试验,但是一定要县长来申请。那你是谁?县长说我是农民的代理人,因为县长保证他会这样回答的(他也不知道那农民是不是专家),管理员当然不给。县长没办法只能告诉农民拿不到种子。这件事里面县长是以县长的身份帮农民找到种子,但需要表示身分的时候他说只是农民的代理人。这样做县长可以帮人但也不会给别人利用。(农民可能把种子拿回来卖钱也说不定,没人知道)
在这里RPL就是县长的另一个身份---农民的代理人也就是农民---他会带在身上,人家没有问他的时候他不会告诉别人,所以别人也就以县长的身分来看待他。当查身份的时候他才告诉你---我是农民的代理人。
四、微服私访的例子
本身你是个县长(CPL=3),你去一个省访问(DPL=1),按理说你就只能接受县长待遇,访问只能由县长访问的地方(DPL=3)。访问的时候需要领一个许可证RPL,它决定你可以访问哪些地方。这个时候你想伪装成总理,于是申请了一个RPL=0许可证(实际表现为传递给操作系统一个RPL=0的段选择符),但检查部门(操作系统内核)发现了你的伪装(通过使用ARPL指令),把你打回原形(让RPL=CPL=3),最后你还是只能访问县长能访问的地方,你的伪装计划失败了。 (这个所谓许可证RPL不是多余吗?为什么不直接看县长身份?)
五、RPL的理解
CPL和DPL都是见字面就能理解含义的。唯独RPL不容易理解。
今天就说说我的理解,将来回头来看看,今天说的到底对不对。
1、- 当前进程的意愿 -
RPL代表了当前进程的意愿(因为是一种request)。注意,是当前进程的意愿而不是别的进程。其次是一种意愿,是否实现还有待CPU的许可。
2、- 不能超越自己的特权级 -
前面的帖子已经说了,RPL可以由当前进程随便写,但是CPU会检查RPL和CPL的取值,如果RPL填写的特权级比自己实际情况还高,CPU就不会认可,仍旧给他当前的权限。
3、- 只能下降权限 -
鉴于上面的原因,使用RPL的真正情况就是当前进程使用比自己低的或者相同的RPL。这种情况最典型的,就是外围程序A调用内核B,然后通过内核B再访问程序C。由于访问的来源是A,所以内核为了安全起见,将RPL(来自A的访问意愿)设置为A的DPL。这样在逻辑上就完整了。内核就不会越俎代庖了。