现在的位置: 首页 > 综合 > 正文

CPL DPL RPL的区别 &&一致性代码段和非一致性代码段

2017年05月25日 ⁄ 综合 ⁄ 共 4557字 ⁄ 字号 评论关闭

概述:在谈论保护模式编程的时候,一直会有这样的困惑:为什么除了CPLDPL还有RPL?什么时候高特权级不能访问低特权级?什么时候低特权级不能访问高特权级?一致性代码和非一致性代码有什么区别?等等这些问题,如果仅仅停留在知其然的级别,很容易会困惑,本文主要说明以上问题的答案和蕴涵在背后的原因。

1.特权级


       首先,了解以下操作系统的特权级

    1CPL是存寄存器如CS,当前特权级只有03两个值,分别表示用户态和内核态。

    2RPL是代码中根据不同段跳转而确定,以动态刷新CS里的CPL,在代码段选择符中。而且RPL对每个段来说不是固定的,两次访问同一段时的RPL可以不同。

    3DPL是在GDT/LDT描述符表中,静态的。

    RPL是用来做什么的?

    Intel手册上的解释为:TheRPL
can be used to insure that privileged code does not access asegment on behalf of an application program unless the program itselfhas access privileges for that segment.
RPL能够用来确保具有特权级的代码不会代表另一个应用程序去访问一个段,除非那个应用程序具有访问那个段的权限.

    比方说:A进程的DPL0C进程的DPL1,现在有一个B进程他的DPL2,这B进程想委托A进程去访问C的数据,如果没有RPL的话这样的委托访问是可以成功的,但这样是非常不安全的(实际上B没有访问C的权限,但是A有这个权限)。有了RPL以后A进程在访问C的时候还要受到RPL的约束,此时可以将访问C的选择子的RPL设为BDPL这样A的访问权限就相当为EPL=maxRPLDPL=2,这样他就无法代表B去越权访问C了。RPL相当于附加的一个权限控制,只有当RPL>DPL的时候,才起到实际的限制作用。


简单的说就是这样:B需要委托A去到C那里办理某件事情,但是A的权利太大,所以需要附加委托人的权利。用一个不恰当的比喻,你爷爷是皇帝,你爸爸是太子,你是太子的儿子,你爷爷还有很多孙子;其中太子的请求,如果不考虑其他因素,皇帝都会答应;你要去对皇帝递交某个请求,来请你爸爸传递,你爷爷看见是你爸爸,还要核对你爸爸是帮谁传递的请求,如果是帮你,你皇帝爷爷就批准了,如果你爸爸是递交的外人的请求,你爷爷未必会批准。

2.段间的转移和跳转


程序是通过选择子/门调用等等来在段之间来回走动的.实现用户级与系统级的调用跳转.


2.1数据段与堆栈段

    所有的数据段都是非一致性的,就是说他们不能被低特权级的程序访问,但是可以被高特权级的程序访问而不需使用特殊的访问门。这里,RPL可能会削弱CPL的作用,访问数据段或堆栈段时,默认用CPLRPL中的最小特权级去访问数据段,所以max {CPL, RPL} ≤ DPL否则访问失败。

2.2代码段 
      

    
  转跳后程序的
CPL(CS.RPL)只会有下面的俩种可能

1)转跳后程序的CPL(CS.RPL)=转跳前程序的CPL(CS.RPL)
2)
或转跳后程序的CPL(CS.RPL)= 转跳后程序的CodeDescriptor.DPL

准则:RPL是不会赋值给CPL的

   如何决定跳转之后CPL的值呢?

1)如果能成功转跳到一致代码段,转跳后程序的CPL(CS.RPL)=转跳前程序的CPL(CS.RPL)(转跳后程序的CPL继承了转跳前程序的CPL,一致一致,翻译时一致含义就是这个意思!)
2
如果能成功转跳到非一致代码段,转跳后程序的CPL(CSRPL) =转跳后程序的Descriptor.DPL(转跳后程序的CPL变成了该代码段的特权级.我在前面提到DPL是段描述符中的特权级,它的本意是用来代表它所描述的段的特权级)

2.3.一致性代码与非一致性代码段

    什么是一致代码和非一致代码段?为什么要作这个区别?

    代码段可以是一致性的或者非一致性的。向特权级更高的一致性代码段的控制转移,允许程序以当前特权级继续执行,向不同特权级的非一致代码段转移将导致一般保护异常,除非使用了任务门或者调用门。产生一致性代码和非一致性代码的主要原因是:单纯的0-3特权级只能保证高特权级可以访问特权级的东西,而低特权级的段有时候要访问内核数据段,此时就需要一些灵活策略。

    不访问保护措施的系统工具和某些异常类型的处理过程需要放在一致性代码段中。需要防止低特权级程序访问的工具要放在非一致代码段中。

1)一致代码段:

简单理解,就是操作系统拿出来被共享的代码段,可以被低特权级的用户直接调用访问的代码.

通常这些共享代码,是"不访问"受保护的资源和某些类型异常处理。比如一些数学计算函数库,为纯粹的数学运算计算,

一致代码段的限制作用

1.1).特权级高的程序不允许访问特权级低的数据:核心态不允许调用用户态的数据.
1.2).
特权级低的程序可以访问到特权级高的数据.但是特权级不会改变:用户态还是用户态.

2)非一致代码段:

为了避免低特权级的访问而被操作系统保护起来的系统代码.

非一致代码段的限制作用

2.1).只允许同级间访问.
2.2).
绝对禁止不同级访问:核心态不用用户态.用户态也不使用核心态.

通常低特权代码必须通过""来实现对高特权代码的访问和调用.


2.4段间成功跳转的条件

    明晰一些概念:段的保护观念是高特权级不找低特权级办事,低特权级找高特权级帮忙;但是低特权级不能直接处理一些事情,而高特权级可以访问低特权级。(好吧,这句话好像矛盾了)

    具体而言:还是要问怎样才能成功转跳?这里分两种情况:


2.4.1普通转跳(没有经过Gate 这东西):

    即JMPCall后跟着48位全指针(16位段选择子+32位地址偏移),且其中的段选择子指向代码段描述符,这样的跳转称为直接(普通)跳转。普通跳转不能使特权级发生跃迁,即不会引起CPL的变化,看下面的详细描述:

1)目标是一致代码段:
要求:CPL(CS.RPL)>=DestinationDescriptorCode.DPL ,其他RPL是不检查的。
原因:一致性代码设计的初衷就是让低特权级可以访问到高特权级的东西

结果:CPL不变

异常原因:CPL<DPL

总结:一致性代码,不允许高特权级访问低特权级的内容。

2目标是非一致代码段:
要求:
CPL(CS.RPL)=DestinationDescriptorCode.DPL ANDRPL≤CPL(CS.RPL)
原因:非一致性代码的设计初衷就是让低特权级无法接触到高特权级的代码。

结果:CPL不变
异常原因:如果访问时不满足前提CPL=DPL,则引发异常。

总结:非一致性代码,只允许同特权级的进程之间互相访问。


2.4.2通过调用门的跳转:

    当段间转移指令JMP和段间转移指令CALL后跟着的目标段选择子指向一个调用门描述符时,该跳转就是利用调用门的跳转。这时如果选择子后跟着32位的地址偏移,也不会被cpu使用,因为调用门描述符已经记录了目标代码的偏移。使用调门进行的跳转比普通跳转多一个步骤,即在访问调用门描述符时要将描述符当作一个数据段来检查访问权限,要求指示调用门的选择子的RPL≤门描述符DPL,同时当前代码段CPL≤门描述符DPL,就如同访问数据段一样,要求访问数据段的程序的CPL≤待访问的数据段的DPL,同时选择子的RPL≤待访问的数据段或堆栈段的DPL。只有满足了以上条件,CPU才会进一步从调用门描述符中读取目标代码段的选择子和地址偏移,进行下一步的操作。

   从调用门中读取到目标代码的段选择子和地址偏移后,我们当前掌握的信息又回到了先前,和普通跳转站在了同一条起跑线上(普通跳转一开始就得到了目标代码的段选择子和地址偏移),有所不同的是,此时,CPU会将读到的目标代码段选择子中的RPL0,即忽略了调用门中代码段选择子的RPL的作用。完成这一步后,CPU开始对当前程序的CPL,目标代码段选择子的RPL(事实上它被清0后总能满足要求)以及由目标代码选择子指示的目标代码段描述符中的DPL进行特权级检查,并根据情况进行跳转,具体情况如下:

目标是一致代码段:
要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL RPL不检查,因为RPL被清0,所以事实上永远满足RPL≤DPL,这一点与普通跳转一致,适用于JMPCALL
         
转跳后程序的CPL(NewCS.RPL) = 转跳前程序的CPL( OldCS.RPL),因此特权级没有发生跃迁。 
                         

目标是非一致代码段:

   当用JMP指令跳转时:
    
要求:CPL(CS.RPL)DestinationDescriptorCode.DPL AND  RPL<= CPL(CS.RPL)(事实上因为RPL被清0,所以RPL≤CPL总能满足,因此RPLCPL的关系在此不检查)。若不满足要求则程序引起异常。
         
转跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL
    
因为前提是CPL=DPL,所以转跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL不会改变CPL的值,特权级也没有发生变化。如果访问时不满足前提CPL=DPL,则引发异常。

当用CALL指令跳转时:

    要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPLRPL被清0,不检查),若不满足要求则程序引起异常。

         转跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL

    当条件CPL=DPL时,程序跳转后CPL=DPL,特权级不发生跃迁;当CPLDPL时,程序跳转后CPL=DPL,特权级发生跃迁,这是我们当目前位置唯一见到的使程序当前执行优先级(CPL)发生变化的跳转方法,即用CALL指令+调用门方式跳转,且目标代码段是非一致代码段。

    总结:以上介绍了两种情况的跳转,分别是普通跳转和使用调用门的跳转,其中又可细分为JMP跳转和CALL跳转,跳转成功已否是由CPLRPLDPL综合决定的。所有跳转都是从低特权级代码向同级或更高特权级(DPL)跳转,但保持当前执行特权级(CPL)不变,这里有点难于区别为什么说向高特权级跳转,又说特权级没变,这里“高特权级”是指目标代码段描述符的DPL,它规定了可以跳转到该段代码的最高特权级;而后面的CPL不变才真正说明了特权级未发生跃迁。我们可以看到,只有用CALL指令+调用门方式跳转,且目标代码段是非一致代码段时,才会引起CPL的变化,即引起代码执行特权级的跃迁,这是目前得知的改变执行特权级的唯一办法.

如何记忆:所谓一致,可以理解成可以共享的意思。而两个进程之间,什么需要共享?往往是哪些不访问受保护资源或者某些类型的异常处理的代码。进程之间,在数据上是相互独立的,遵循cow原则。

抱歉!评论已关闭.