內(nèi)存保護單元(MPU)是一種硬件機制,通過只允許代碼訪問需要的內(nèi)存和外設(shè)來提高嵌入式設(shè)備的安全性。應(yīng)用程序可以組織為進程(process),每個進程訪問自己的內(nèi)存和外設(shè)。MPU不僅阻止應(yīng)用程序代碼訪問其指定區(qū)域以外的內(nèi)存或外設(shè),而且還可以用于檢測堆棧溢出。
我們基于ARM Cortex-M MCU中的MPU,討論一下MPU所提供的一些特性。
MPU是什么?
內(nèi)存保護單元(MPU)是一種硬件機制,只允許需要訪問某些資源的代碼訪問相應(yīng)的內(nèi)存和外設(shè)。MPU常用于安全關(guān)鍵應(yīng)用,如醫(yī)療設(shè)備、航空電子設(shè)備、工業(yè)控制、核電站等,提高嵌入式應(yīng)用的穩(wěn)定性和安全性。
在IoT應(yīng)用中,也可以通過MPU限制對內(nèi)存和外設(shè)的訪問,提高產(chǎn)品的安全性。例如,可以通過MPU機制隱藏加密密鑰以拒絕攻擊者訪問;使用MPU隔離Flash控制器也可以防止攻擊者更改應(yīng)用程序,只允許受信任的代碼執(zhí)行代碼更新。
通過MPU將RTOS任務(wù)劃分為進程,如圖1所示。每個進程可以包含任意數(shù)量的任務(wù)。進程內(nèi)的任務(wù)可以訪問分配給該進程的內(nèi)存和外設(shè)。增加MPU時,從任務(wù)的角度幾乎不用更改,除非任務(wù)之間存在交互。
進程間可以通過共享內(nèi)存進行通信,兩個進程的MPU配置表中將出現(xiàn)相同的區(qū)域。
應(yīng)用中也可以包含具有完全權(quán)限的系統(tǒng)級任務(wù)和ISR,允許它們訪問所有內(nèi)存、外設(shè)或CPU。
當(dāng)違規(guī)發(fā)生時,系統(tǒng)的行為取決于應(yīng)用程序,是哪個任務(wù)違規(guī)。例如,如果違規(guī)是由圖形用戶界面(GUI)造成的,可以終止并重新啟動GUI,并且不會影響系統(tǒng)的其它部分。但是,如果違規(guī)任務(wù)控制一個制動器,則異常處理程序可能需要在重啟任務(wù)之前立即停止制動器。
理想情況下,在產(chǎn)品開發(fā)過程中會捕獲并糾正訪問違規(guī),否則,系統(tǒng)設(shè)計人員將需要評估所有可能的結(jié)果,并決定發(fā)生這種情況時該做什么。
使用MPU檢測堆棧溢出
在基于RTOS的應(yīng)用中,每個任務(wù)都需要單獨的堆棧空間。堆棧溢出可能是基于RTOS的系統(tǒng)開發(fā)人員所面臨的最常見的問題之一。如果沒有硬件幫助,堆棧溢出檢測可以由軟件實現(xiàn),但軟件方式不能及時捕獲溢出,可能導(dǎo)致產(chǎn)品不穩(wěn)定。MPU可以幫助防止堆棧溢出。
MPU域可用于堆棧溢出檢測,使用一個小的域(RedZone)來覆蓋每個任務(wù)堆棧的底部。配置MPU屬性,如果有任何代碼嘗試寫入該區(qū)域,將觸發(fā)MPU異常。域的大小決定了該方法在捕獲堆棧溢出方面的效率。
區(qū)域越大,堆棧溢出捕獲的可能性就越大,同時堆棧可用的RAM就越少。換言之,RedZone域被認為是不可用的內(nèi)存,它被用來檢測非法寫入。開始時可以將RedZone大小設(shè)置為32個字節(jié),如果任務(wù)堆棧為512個字節(jié),那么32個字節(jié)約占用6%,剩余480個字節(jié)的可用堆棧空間。
另一種檢測方法是將整個任務(wù)堆棧封裝為一個MPU區(qū)域,允許讀寫操作。該方法有兩個問題:首先,在Cortex-M(ARMv7M架構(gòu))上,堆棧的大小需為2的冪,并且必須在冪邊界(即32、64、128、256、512等)上對齊。
如果嵌入式應(yīng)用有足夠的RAM,那么這不是問題;然而,在資源受限的應(yīng)用 (如Cortex-M MCU)中,在減少浪費的同時設(shè)置合適的堆棧內(nèi)存會很復(fù)雜。其次,該方法不允許寫同一進程中的其它任務(wù)堆棧,不允許進程中的任務(wù)通過堆棧傳遞信息。
進程表
進程表中的條目數(shù)量取決于MPU。Cortex-M架構(gòu)MCU可以有8個或16個MPU區(qū)域。由于可用的區(qū)域數(shù)量有限,通常更多設(shè)置區(qū)域保護RAM中的數(shù)據(jù)訪問。但是,如果應(yīng)用程序沒有用完所有區(qū)域,也可以通過區(qū)域限制對代碼的訪問來提高安全性。
1、一個MPU域提供對代碼空間的讀取和執(zhí)行訪問。因為常量通常存儲在Flash中(ASCII字符串、查找表、常量等),所以讀訪問是必須的。
2、一個區(qū)域用于提供進程相關(guān)的外設(shè)訪問,例如,以太網(wǎng)控制器、USB控制器等。MPU區(qū)域設(shè)置為讀寫訪問,但不允許執(zhí)行代碼。如果進程不需要訪問外設(shè),則不需要此MPU區(qū)域。
3、一個MPU區(qū)域封裝進程全局變量以及堆(heap)空間。MPU區(qū)域?qū)⒃O(shè)置為讀寫權(quán)限,但同樣不允許執(zhí)行代碼。
4、一個MPU區(qū)域用于檢測堆棧溢出。此方法假設(shè)進程中的任務(wù)不會通過其堆棧共享數(shù)據(jù)。同樣,在此區(qū)域中不允許執(zhí)行代碼。RTOS負責(zé)選擇運行哪個任務(wù),相應(yīng)任務(wù)的堆棧將被封裝在MPU區(qū)域中。
5、該區(qū)域顯示了由于MPU區(qū)域大小和對齊限制而可能導(dǎo)致的RAM浪費。資源受限的嵌入式應(yīng)用中應(yīng)嘗試盡量減少浪費。
6、此MPU區(qū)域用于建立多個進程共享的RAM。如果進程不需要共享數(shù)據(jù),則不需要此MPU區(qū)域。
進程表由“N”個條目組成,每個條目包含兩個字段:區(qū)域的基地址和指定區(qū)域?qū)傩裕▍^(qū)域大小,允許讀、寫或執(zhí)行等)。
進程表在創(chuàng)建任務(wù)時被分配給任務(wù)。RTOS只是在任務(wù)的控制塊(TCB)中保留一個指向進程表的指針。RTOS在上下文切換時增加更新MPU進程表的代碼,如圖4所示。在切換任務(wù)時不需要保存MPU配置。
Cortex-M 特權(quán)等級
上電后,Cortex-M運行在特權(quán)模式,可以訪問CPU的所有資源,訪問任何內(nèi)存或I/O地址,啟用/禁用中斷,設(shè)置嵌套向量中斷控制器(NVIC),配置FPU和MPU等。
為了保證系統(tǒng)的安全,特權(quán)模式代碼保留給經(jīng)過完全測試并受信任的代碼。由于大多數(shù)RTOS都經(jīng)過了完整的測試,通常被認為是值得信任的,而應(yīng)用代碼是不可信的。也有例外,例如,只要ISR保持盡可能短而不被濫用,ISR通常被認為是受信任的,因此也以特權(quán)模式運行。這是大多數(shù)RTOS供應(yīng)商的建議。
應(yīng)用代碼在非特權(quán)模式下運行,從而限制了代碼可以做的事情。具體來說,非特權(quán)模式可以防止代碼關(guān)中斷、更改嵌套向量中斷控制器(NVIC)的設(shè)置、將運行模式更改為特權(quán)等級、修改MPU設(shè)置等。這是一個理想的特性,因為我們不希望不受信任的代碼賦予自己特權(quán),從而更改系統(tǒng)設(shè)計者提供的保護。
由于CPU總是以特權(quán)模式開始運行,任務(wù)需要從創(chuàng)建時就以非特權(quán)模式運行,或者在啟動后通過API調(diào)用,切換到非特權(quán)模式。一旦進入非特權(quán)模式,CPU只有在中斷或異常服務(wù)中才能切換回特權(quán)模式。
在用戶模式訪問RTOS服務(wù)
由于非特權(quán)代碼不能禁用中斷,因此應(yīng)用代碼被迫使用RTOS服務(wù)來訪問共享資源。由于RTOS服務(wù)在特權(quán)模式下運行,因此非特權(quán)任務(wù)必須通過Cortex-M提供的SVC機制切換回特權(quán)模式。SVC的行為類似中斷,但由CPU指令觸發(fā)。
在Cortex-M上,SVC指令使用一個8位參數(shù)來指定256個可能的RTOS服務(wù)。設(shè)計者決定非特權(quán)代碼可以使用的RTOS服務(wù)。例如,你可能不希望允許非特權(quán)任務(wù)終止其它任務(wù)(或其本身)。此外,這些服務(wù)都不允許禁用中斷,因為這將破壞在非特權(quán)模式下運行代碼的目的。一旦調(diào)用,SVC指令跳轉(zhuǎn)到SVC異常處理程序。
在Cortex-M3上,SVC處理程序?qū)⒃黾蛹s1k字節(jié)的代碼,需要執(zhí)行75~125條CPU指令。因此,相同RTOS服務(wù),在非特權(quán)代碼中調(diào)用比特權(quán)模式調(diào)用需要更多的處理時間。
在非特權(quán)模式下運行代碼還可以防止用戶代碼禁用中斷,從而減少了鎖定系統(tǒng)的機會。當(dāng)然,如果用戶代碼進入無限循環(huán),特別是在高優(yōu)先級任務(wù)或ISR中時,鎖定仍然可能發(fā)生。在這種情況下,通過使用看門狗可以恢復(fù)。
進程間通信
1、互斥量用于確保兩個進程不同時訪問相同的數(shù)據(jù)。注意,互斥量駐留在RTOS內(nèi)存空間中,通過RTOS API,所有進程可以訪問該互斥量。
2、需要訪問受保護資源的任務(wù)必須首先獲取互斥量。當(dāng)任務(wù)完成共享資源訪問后,互斥量將被釋放。沙漏表示可選超時,當(dāng)任務(wù)不希望永久等待所有者釋放互斥量時,可以使用超時機制。
3、信號量也可以用來指示數(shù)據(jù)可用。
4、進程中的任務(wù)將數(shù)據(jù)存儲到共享內(nèi)存中,然后發(fā)出信號。
5、進程B中的任務(wù)等待進程A的信號。沙漏表示一個可選的超時,以避免永遠等待信號。如果該信號未在規(guī)定的時間內(nèi)發(fā)生,則RTOS將恢復(fù)該任務(wù)。在這種情況下,任務(wù)知道共享區(qū)域中沒有存入任何東西。
6、如果沒有發(fā)生超時,進程B確認數(shù)據(jù)處理完成。
7、進程A發(fā)信號量后,等待具有可選超時的確認。
8、通信也可以使用RTOS的消息隊列機制。此時,從共享RAM區(qū)域動態(tài)分配緩沖區(qū),進程A中的發(fā)送任務(wù)填充緩沖區(qū),并將指針發(fā)送給進程B中的任務(wù)。
9、與信號量情況類似,等待進程B中的任務(wù)確認,并指定一個可選的超時。
內(nèi)存和I/O訪問錯誤處理
進程間可以通過共享內(nèi)存進行通信,兩個進程的MPU配置表中將出現(xiàn)相同的區(qū)域。
應(yīng)用中也可以包含具有完全權(quán)限的系統(tǒng)級任務(wù)和ISR,允許它們訪問所有內(nèi)存、外設(shè)或CPU。
當(dāng)違規(guī)發(fā)生時,系統(tǒng)的行為取決于應(yīng)用程序,是哪個任務(wù)違規(guī)。例如,如果違規(guī)是由圖形用戶界面(GUI)造成的,可以終止并重新啟動GUI,并且不會影響系統(tǒng)的其它部分。
但是,如果違規(guī)任務(wù)控制一個制動器,則異常處理程序可能需要在重啟任務(wù)之前立即停止制動器。理想情況下,在產(chǎn)品開發(fā)過程中會捕獲并糾正訪問違規(guī),否則,系統(tǒng)設(shè)計人員將需要評估所有可能的結(jié)果,并決定發(fā)生這種情況時該做什么。
MPU的工作是確保進程中的任務(wù)只能訪問分配給它的內(nèi)存和外設(shè)。但是,如果任務(wù)試圖訪問允許區(qū)域以外的數(shù)據(jù)呢?MPU會觸發(fā)一個稱為內(nèi)存管理(MemManage)故障的CPU異常。
當(dāng)故障發(fā)生時,系統(tǒng)行為取決于應(yīng)用程序,但如何處理故障可能是很難確定的事情。首先,這些類型的故障應(yīng)該在開發(fā)過程中被檢測和糾正。然而,使用MPU的原因之一是為了防止發(fā)生的無效內(nèi)存或外設(shè)訪問,要么是因為系統(tǒng)驗證期間未捕獲某些偶發(fā)情況,或者是未經(jīng)授權(quán)的訪問。
MemManage故障通常由RTOS處理。理想情況下,嵌入式系統(tǒng)有一些機制可以記錄和報告故障,以便在產(chǎn)品的下一個版本中修正。文件系統(tǒng)是記錄這些故障的好地方,當(dāng)然,還取決于故障處理程序。
發(fā)生故障時,故障處理程序可以執(zhí)行以下操作序列(偽代碼):
void OS_MPU_FaultHandler (void){// Terminate the offending task/process (1)// Release resources owned by the task/process (2)// Run a user provided ‘callback’ (based on the offending task) (3)// If we have a file system: (4)// Store information about the cause// Do we restart the task/process? (5)// Yes, Restart the task/process// Alert a user (6)// No, Reset the system (7)}
(1)當(dāng)故障發(fā)生時,設(shè)計者需要確定如何操作。至少必須終止違規(guī)的任務(wù),但我們是否還需要終止此進程中的其他任務(wù)?沒有一個確定的答案,事實上,這可能取決于是哪個任務(wù)造成了故障。因此,MPU故障處理程序應(yīng)根據(jù)觸發(fā)它的任務(wù)或進程執(zhí)行不同的操作。
(2)被終止的違規(guī)任務(wù)(或進程)可能擁有內(nèi)核對象、緩沖區(qū)、I/O等資源。這些資源需要被釋放,以避免影響其他任務(wù)/進程。
(3)導(dǎo)致故障的任務(wù)可能會控制制動器或其他類型的輸出,需將任務(wù)置于安全狀態(tài),以避免對人員或資產(chǎn)造成傷害。嵌入式系統(tǒng)設(shè)計者應(yīng)提供用戶定義的回調(diào)函數(shù),以處理系統(tǒng)特定的操作。
在任務(wù)創(chuàng)建過程中,將回調(diào)函數(shù)存儲在任務(wù)的控制塊(TCB)中。為提高系統(tǒng)安全性,只能在系統(tǒng)啟動時創(chuàng)建任務(wù),此時CPU處于特權(quán)模式;運行時只能在故障時刪除任務(wù)。由于TCB位于RTOS空間中,因此無法從用戶代碼訪問回調(diào)函數(shù),從而防止?jié)撛诘牟话踩筒豢煽康拇a無意中或惡意地調(diào)用回調(diào)函數(shù)。
(4)如果嵌入式系統(tǒng)具有數(shù)據(jù)存儲功能,則可以記錄故障相關(guān)的信息,如違規(guī)任務(wù)的性質(zhì)、CPU寄存器的值、所采取的操作等。
(5)根據(jù)導(dǎo)致故障的任務(wù),可以重新啟動,使系統(tǒng)可以錯誤中恢復(fù)。
(6)如果系統(tǒng)能夠恢復(fù),并且如果系統(tǒng)包含顯示,則警告提示非常有用。此外,如果系統(tǒng)具有網(wǎng)絡(luò)連接,則通知服務(wù)部門和開發(fā)團隊可以在將來的版本中避免此問題。
(7)如果系統(tǒng)無法恢復(fù),除了重置系統(tǒng)之外,可能沒有其他選擇。
可以更改MPU進程表,使其包含每個任務(wù)的回調(diào)函數(shù),以便檢測到故障時從RTOS上下文切換代碼調(diào)用。
如果所有的任務(wù)都需要對故障執(zhí)行相同的操作,那么可以不使用此功能,或者讓所有MPU進程表都指向相同的回調(diào)函數(shù)。后一種選擇更最靈活,因為它為未來的版本提供了更大的靈活性。但你可能需要咨詢RTOS供應(yīng)商,以確定此功能是否可用。
使用建議
以非特權(quán)模式運行用戶代碼
使用MPU時,以特權(quán)模式運行應(yīng)用代碼。這意味著應(yīng)用程序代碼將能夠更改MPU設(shè)置,并破壞使用MPU的目的。以特權(quán)模式運行應(yīng)用可能更容易地遷移應(yīng)用代碼。在某些時候,大多數(shù)應(yīng)用程序代碼將需要在非特權(quán)模式下運行,用戶需要添加SVC處理程序。
ISR具有完全訪問權(quán)限
當(dāng)識別到中斷并且啟動ISR時,處理器將切換到特權(quán)模式。由于MPU控制寄存器的PRIVDEFENA為1,因此ISR可以訪問所有I/O內(nèi)存。
此外,ISR應(yīng)該盡可能短,并簡單地發(fā)信號給任務(wù),由任務(wù)執(zhí)行中斷設(shè)備所需的大部分工作。當(dāng)然,這假設(shè)ISR是內(nèi)核感知的ISR,并且該中斷設(shè)備有相當(dāng)多的工作處理。例如,不應(yīng)在ISR中上處理以太網(wǎng)數(shù)據(jù)包。然而,閃爍LED或更新脈沖寬度調(diào)制(PWM)計時器的占空比可以直接在ISR中完成。
防止在RAM中執(zhí)行代碼
大多數(shù)MPU可以防止從RAM執(zhí)行代碼,從而限制代碼注入攻擊。防止外設(shè)執(zhí)行代碼可能看起來很奇怪,但可以防止想方設(shè)法進入系統(tǒng)的黑客。
限制進程對外設(shè)的訪問
應(yīng)該留出一個或多個MPU區(qū)域,以限制進程只能訪問自己的外設(shè)。換言之,如果一個進程管理USB端口,那么它應(yīng)該只能訪問USB外設(shè)或與USB控制器的需求相關(guān)的外設(shè),例如DMA。
可用的RTOS API
設(shè)計者必須確定應(yīng)用代碼可使用哪些RTOS API。例如,是否禁止應(yīng)用代碼在系統(tǒng)初始化后創(chuàng)建和刪除任務(wù)或RTOS對象?換言之,RTOS對象是否只能系統(tǒng)啟動時創(chuàng)建,而非運行時創(chuàng)建?
如果是,SVC處理程序查找表應(yīng)只包含向應(yīng)用公開的API。即使ISR在特權(quán)模式下運行,可以訪問所有的RTOS API,一個好的RTOS也會阻止從ISR中創(chuàng)建和刪除RTOS對象。
在RTOS空間中分配RTOS對象
任務(wù)堆棧位于進程空間內(nèi),但是,RTOS對象(信號量、隊列、任務(wù)控制塊等)應(yīng)在內(nèi)核空間中分配,并通過引用進行訪問。不應(yīng)在進程空間中分配RTOS對象,因為這意味著應(yīng)用代碼可以不調(diào)用RTOS API,即可有意或意外地修改內(nèi)核對象。
保護對代碼的訪問
雖然MPU區(qū)域通常用于提供或限制對RAM和外設(shè)的訪問,但如果有空閑區(qū)域,并且能夠通過進程組織代碼,那么限制對代碼的訪問非常有用,可以防止某些類型的安全攻擊,如Return-to-libc攻擊。
減少進程間的通信
正如設(shè)計時,盡可能保持任務(wù)獨立一樣,進程也應(yīng)該遵循同樣的規(guī)則。因此,進程間應(yīng)盡量不交互,或者將進程間通信保持在最低限度。如果必須與其它進程通信,只需留出一個包含Out和In緩沖區(qū)的共享區(qū)域。
發(fā)送者將其數(shù)據(jù)放入Out緩沖區(qū)中,然后觸發(fā)一個中斷來喚醒接收進程。一旦數(shù)據(jù)被處理,響應(yīng)可以放在發(fā)送者的In緩沖區(qū)中,并且可以使用中斷來通知發(fā)送進程。
確定出現(xiàn)MPU故障時應(yīng)如何操作
理想情況下,在開發(fā)過程中檢測并糾正所有MPU故障。你還需考慮由于意外故障或錯誤或系統(tǒng)受到安全攻擊而出現(xiàn)的故障處理。在大多數(shù)情況下,建議對每個任務(wù)或每個進程設(shè)置一個可控的關(guān)機順序。是否重新啟動違規(guī)任務(wù)、進程內(nèi)或整個系統(tǒng)內(nèi)的所有任務(wù)都取決于故障的嚴重程度。
記錄和報告故障
理想情況下,系統(tǒng)應(yīng)該能夠記錄(可能是文件系統(tǒng))并顯示故障原因,以便開發(fā)人員解決問題。
大多數(shù)RTOS在特權(quán)模式下運行應(yīng)用代碼,使得應(yīng)用程序完全控制CPU及其資源。所有任務(wù)和ISR都可以不受限制地訪問內(nèi)存和外設(shè)。這意味著應(yīng)用程序代碼可能會意外或故意損壞其他任務(wù)的堆棧或變量。通過MPU機制,可以限制應(yīng)用代碼的訪問權(quán)限,極大提供應(yīng)用的功能安全。
編輯:jq
-
MPU
+關(guān)注
關(guān)注
0文章
357瀏覽量
48775
原文標題:MPU如何使產(chǎn)品更安全?
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論