特权级——保护模式的特权级检查 DPL,RPL,CPL, 一致代码段,非一致代码段
?一、CPL、RPL、DPL簡單解釋
??? CPL是當前進程的權限級別(Current?Privilege?Level),是當前正在執行的代碼所在的段的特權級,存在于cs寄存器的低兩位。
??? RPL說明的是進程對段訪問的請求權限(Request?Privilege?Level),是對于段選擇子而言的,每個段選擇子有自己的 RPL,它說明的是進程對段訪問的請求權限,有點像函數參數。而且RPL對每個段來說不是固定的,兩次訪問同一段時的RPL可以不同。RPL可能會削弱 CPL的作用,例如當前CPL=0的進程要訪問一個數據段,它把段選擇符中的RPL設為3,這樣雖然它對該段仍然只有特權為3的訪問權限。
??? DPL放在段描述符中,規定訪問該段的權限級別(Descriptor?Privilege?Level),每個段的DPL固定。
??? 當進程訪問一個段時,需要進程特權級檢查,一般要求DPL?>=?max?{CPL,?RPL}
??? 下面打一個比方,中國官員分為6級國家zhuxi 1、總理2、省長3、市長4、縣長5、鄉長6,假設我是當前進程,級別總理(CPL=2),我去聊城市 (DPL=4)考察(呵呵),我用省長的級別(RPL=3?這樣也能嚇死他們:-))去訪問,可以吧,如果我用縣長的級別,人家就不理咱了(你看看電視上的微服私訪,呵呵),明白了吧!為什么采用RPL,是考慮到安全的問題,就好像你明明對一個文件用有寫權限,為什么用只讀打開它呢,還不是為了安全!
二、CPL、RPL、DPL詳解
??? RPL是段選擇子里面的bit?0和bit?1位組合所得的值,但這里要首先搞清楚什么是段選擇子,根據Intel?的文件(IA-32?IntelR?Architecture?Software?Developer's?Manual,?Volume?3System?Programming?Guide)段選擇子是一個16Bit?identifier?(原文:A?segment?selector?is?a?16-bit?identifier?for?a?segment).?但?identifier?又是什么.?identifier?可以是一個變數的名字 (?An?identifier?is?a?name?for?variables),?簡單的說它可以就是一般意義的變數.?這里?16- bit?identifier?for?a?segment?可以就是一個一般意義的16bit變數但同時要求對它的值解釋的時候必須跟據Intel定下的規則---也就是bit?0和bit?1位的組合值就是RPL等等…?因此在程序里如果有需要的話你可以聲明一個或者多個變數來代表這些段選擇子,這樣的話你的程序在某一時刻就可以有很多段選擇子,當然有多少段選擇子就有多少RPL.可以說程序有多少個是RPL是你怎樣看待你自己聲明的變數.
??? 程序的CPL(CS.RPL)是CS?register里bit?0和bit?1?位組合所得的值.在某一時刻就只有這個值唯一的代表程序的CPL.
??? 而DPL是段描述符中的特權級,它的本意是用來代表它所描述的段的特權級.?一個程序可以使用很多段(Data,Code,Stack)也可以只用一個code段等.在正常的情況下當程序的環境建立好后,段描述符都不需要改變-----當然DPL也不需要改變.?
??? 注:CS.RPL代表CS寄存器中的Register Privilege Level,并不是一般所說的Request Privilege Level.
?
三、對數據段和堆棧段訪問時的特權級控制:
??? 要求訪問數據段或堆棧段的程序的CPL≤待訪問的數據段或堆棧段的DPL,同時選擇子的RPL≤待訪問的數據段或堆棧段的DPL,即程序訪問數據段或堆棧段要遵循一個準則:?? 只有相同或更高特權級的代碼才能訪問相應的數據段。這里,RPL可能會削弱CPL的作用,訪問數據段或堆棧段時,默認用CPU 和RPL中的最小特權級去訪問數據段,所以max?{CPL,?RPL}?≤?DPL,否則訪問失敗。
?
四、對代碼段訪問的特權級控制:
?1. 讓我們先來記一些“定律”——
??? 所有的程序轉跳,CPU都不會把段選擇子的RPL賦給轉跳后程序的CS.RPL.轉跳后程序的CPL(CS.RPL)只會有下面的兩種可能
??? 轉跳后程序的CPL(CS.RPL)?=?轉跳前程序的CPL(CS.RPL)?
?或
??? 轉跳后程序的CPL(CS.RPL)?= 轉跳后程序的CodeDescriptor.DPL
?
??? 以?call?為例(只能跳到等于當前特權級或比當前特權級更高的段):
?
??? 怎樣決定這兩種選擇,這就要首先知道轉跳后程序的段是一致代碼段還是非一致代碼段.其實也很簡單,規則如下:
??? 如果能成功轉跳到一致代碼段,?轉跳后程序的CPL(CS.RPL) = 轉跳前程序的CPL(CS.RPL),(轉跳后程序的CPL繼承了轉跳前程序的CPL)
??? 如果能成功轉跳到非一致代碼段,?轉跳后程序的CPL(CS.RPL) = 轉跳后程序的Descriptor.DPL。(轉跳后程序的CPL變成了該代碼段的特權級.我在前面提到DPL是段描述符中的特權級,?它的本意是用來代表它所描述的段的特權級)
?
2. 怎樣才能成功轉跳啦?
?
??? 這里有四個重要的概念:
??? 1).段的保護觀念是高特權級不找低特權級辦事,低特權級找高特權級幫忙,相同的一定沒問題.(這樣想邏輯是沒錯,事實對不對就不知道.)也就是縣長不找鄉長,鄉長不求農民,反過來農民求鄉長,鄉長找縣長.這個概念是最重要的。
??? 2)?一致代碼段:?讓客人很方便的利用主人(一致代碼段)的東西為自己辦事.但客人這身份沒有改變 NewCS.RPL=OldCS.RPL所以只能幫自己辦事。比方說鄉長有一頭牛,農民可以借來幫自己種田,但不能種別人的田.但是如果你是鄉長當然可以種鄉里所有的田。
??? 3)?非一致代碼段:主人(非一致代碼段)可以幫客人但一定是用自己的身份 NewCS.RPL=?DestinationDescriptorCode.DPL這里可能有安全的問題,搞不好很容易農民變縣長。主人太頑固了一定要堅持自己的身份,有什么方法變通一下,來個妥協好不好。好的,它就是RPL的用處。
??? 4)?RPL:?它讓程序有需要的時候可以表示一個特權級更低的身份Max(RPL,CPL)而不會失去本身的特權級CPL(CS.RPL),有需要的時候是指要檢查身份的時候。事實上RPL跟段本身的特權級DPL和當前特權級CPL沒有什么關系,因為RPL的值在成功轉跳后并不賦給轉跳后的CS.RPL。
?
3. 還是要問怎樣才能成功轉跳啦?這里分兩種情況:
??? 3.1 普通轉跳(沒有經過Gate?這東西):即JMP或Call后跟著48位全指針(16位段選擇子+32位地址偏移),且其中的段選擇子指向代碼段描述符,這樣的跳轉稱為直接(普通)跳轉。普通跳轉不能使特權級發生躍遷,即不會引起CPL的變化,看下面的詳細描述:
??? (1)目標是一致代碼段:
??? 要求:CPL(CS.RPL)>=DestinationDescriptorCode.DPL?,其他RPL是不檢查的。
?
??? 轉跳后程序的CPL(NewCS.RPL)?=?轉跳前程序的CPL(?OldCS.RPL)
??? 上面的安排就是概念1,2的意思,此時,CPL沒有發生變化,縱使它執行了特權級(DPL)較高的代碼。若訪問時不滿足要求,則發生異常。
?
??? (2)目標是非一致代碼段:
??? 要求:CPL(CS.RPL)=DestinationDescriptorCode.DPL AND RPL≤CPL(CS.RPL)
??? 轉跳后程序的CPL(NewCS.RPL)?=?DestinationDescriptorCode.DPL
??? 上面的安排就是概念3的意思和部分1的意思----主人(一致代碼段)只幫相同特權級的幫客人做事。因為前提是 CPL=DPL,所以轉跳后程序的CPL(NewCS.RPL)?=?DestinationDescriptorCode.DPL不會改變CPL的值,特權級(CPL)也沒有發生變化。如果訪問時不滿足前提CPL=DPL,則引發異常。
?
??? 3.2 通過調用門的跳轉:當段間轉移指令JMP和段間轉移指令CALL后跟著的目標段選擇子指向一個調用門描述符時,該跳轉就是利用調用門的跳轉。這時如果選擇子后跟著32位的地址偏移,也不會被cpu使用,因為調用門描述符已經記錄了目標代碼的偏移。使用調門進行的跳轉比普通跳轉多一個步驟,即在訪問調用門描述符時要將描述符當作一個數據段來檢查訪問權限,要求指示調用門的選擇子的RPL≤門描述符DPL,同時當前代碼段CPL≤門描述符DPL,就如同訪問數據段一樣,要求訪問數據段的程序的CPL≤待訪問的數據段的DPL,同時選擇子的RPL≤待訪問的數據段或堆棧段的DPL。只有滿足了以上條件,CPU才會進一步從調用門描述符中讀取目標代碼段的選擇子和地址偏移,進行下一步的操作。
??? 從調用門中讀取到目標代碼的段選擇子和地址偏移后,我們當前掌握的信息又回到了先前,和普通跳轉站在了同一條起跑線上(普通跳轉一開始就得到了目標代碼的段選擇子和地址偏移),有所不同的是,此時,CPU會將讀到的目標代碼段選擇子中的RPL清0,即忽略了調用門中代碼段選擇子的RPL的作用。完成這一步后,CPU開始對當前程序的CPL,目標代碼段選擇子的RPL(事實上它被清0后總能滿足要求)以及由目標代碼選擇子指示的目標代碼段描述符中的DPL進行特權級檢查,并根據情況進行跳轉,具體情況如下:
?
??? (1)目標是一致代碼段:
??? 要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL?,RPL不檢查,因為RPL被清0,所以事實上永遠滿足RPL≤DPL,這一點與普通跳轉一致,適用于JMP和CALL。
??? 轉跳后程序的CPL(NewCS.RPL)?=?轉跳前程序的CPL(?OldCS.RPL),因此特權級沒有發生躍遷。
?
??? (2)目標是非一致代碼段:
(2.1)當用JMP指令跳轉時:
??? 要求:CPL(CS.RPL)=DestinationDescriptorCode.DPL AND RPL<=?CPL(CS.RPL)(事實上因為RPL被清0,所以RPL≤CPL總能滿足,因此RPL與CPL的關系在此不檢查)。若不滿足要求則引起異常。
??? 轉跳后程序的CPL(NewCS.RPL)?=?DestinationDescriptorCode.DPL
??? 因為前提是CPL=DPL,所以轉跳后程序的 CPL(NewCS.RPL)?=?DestinationDescriptorCode.DPL不會改變CPL的值,特權級也沒有發生變化。如果訪問時不滿足前提CPL=DPL,則引發異常。
(2.2)當用CALL指令跳轉時:
??? 要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL(RPL被清0,不檢查),若不滿足要求則程序引起異常。
??? 轉跳后程序的CPL(NewCS.RPL)?=?DestinationDescriptorCode.DPL
??? 當條件CPL=DPL時,程序跳轉后CPL=DPL,特權級不發生躍遷;當CPL>DPL時,程序跳轉后 CPL=DPL,特權級發生躍遷,這是我們當目前位置唯一見到的使程序當前執行憂先級(CPL)發生變化的跳轉方法,即用CALL指令+調用門方式跳轉,且目標代碼段是非一致代碼段。
?
??? 總結:以上介紹了兩種情況的跳轉,分別是普通跳轉和使用調用門的跳轉,其中又可細分為 JMP跳轉和CALL跳轉,跳轉成功已否是由CPL,RPL和DPL綜合決定的。所有跳轉都是從低特權級代碼向同級或更高特權級(DPL)跳轉,但保持當前執行特權級(CPL)不變,這里有點難于區別為什么說向高特權級跳轉,又說特權級沒變,這里“高特權級”是指目標代碼段描述符的DPL,它規定了可以跳轉到該段代碼的最高特權級;而后面的CPL不變才真正說明了特權級未發生躍遷。我們可以看到,只有用CALL指令+調用門方式跳轉,且目標代碼段是非一致代碼段時,才會引起CPL的變化,即引起代碼執行特權級的躍遷,這是目前得知的改變執行特權級的唯一辦法。
總結
以上是生活随笔為你收集整理的特权级——保护模式的特权级检查 DPL,RPL,CPL, 一致代码段,非一致代码段的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 3.2.3节:特权级
- 下一篇: GDT,LDT,GDTR,LDTR 详解