RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核熱補(bǔ)丁安全隱患的探索

Linux閱碼場(chǎng) ? 來(lái)源:云巔論劍 ? 作者:扶風(fēng) 丁緩 雛雁 ? 2021-10-11 11:54 ? 次閱讀

Linux 內(nèi)核熱補(bǔ)丁可以修復(fù)正在運(yùn)行的 linux 內(nèi)核,是一種維持線上穩(wěn)定性不可缺少的措施,現(xiàn)在比較常見(jiàn)的比如 kpatch 和 livepatch。內(nèi)核熱補(bǔ)丁可以修復(fù)內(nèi)核中正在運(yùn)行的函數(shù),用已修復(fù)的函數(shù)替換掉內(nèi)核中存在問(wèn)題的函數(shù)從而達(dá)到修復(fù)目的。

函數(shù)替換的思想比較簡(jiǎn)單,就是在執(zhí)行舊函數(shù)時(shí)繞開(kāi)它的執(zhí)行邏輯而跳轉(zhuǎn)到新的函數(shù)中,有一種比較簡(jiǎn)單粗暴的方式,就是將原函數(shù)的第一條指令修改為“ jump 目標(biāo)函數(shù)”指令,即直接跳轉(zhuǎn)到新的函數(shù)以達(dá)到替換目的。

那么,問(wèn)題來(lái)了,這么做靠譜嗎?直接將原函數(shù)的第一條指令修改為 jump 指令,會(huì)破壞掉原函數(shù)和它的調(diào)用者之間的寄存器上下文關(guān)系,存在安全隱患!本文會(huì)針對(duì)該問(wèn)題進(jìn)行探索和驗(yàn)證。

安全性沖擊:?jiǎn)栴}呈現(xiàn)

對(duì)于函數(shù)調(diào)用,假設(shè)存在這樣兩個(gè)函數(shù) funA 和 funB,其中 funA 調(diào)用 funB 函數(shù),這里稱 funA 為 caller(調(diào)用者),funB 為 callee(被調(diào)用者),funA 和 funB 都使用了相同的寄存器 R,如下所示:

dd6f51f2-297a-11ec-82a8-dac502259ad0.png

圖1 funA 和 funB 都使用了寄存器 R,funA 再次使用 R 時(shí)已經(jīng)被 funB 修改

因此,當(dāng) funA 再次使用到 R 的數(shù)據(jù)已經(jīng)是錯(cuò)誤的數(shù)據(jù)了。如果 funA 在調(diào)用 funB 前保存寄存器 R 中的數(shù)據(jù),funB 返回后再將數(shù)據(jù)恢復(fù)到 R 中,或者 funB 先保存 R 中原有的數(shù)據(jù),然后在返回前恢復(fù),就可以解決這類問(wèn)題。

唯一的調(diào)用約定

那寄存器該由 caller 還是 callee 來(lái)保存?這就需要遵循函數(shù)的調(diào)用約定(call convention),不同的 ABI 和不同的平臺(tái),函數(shù)的調(diào)用約定是不一樣的,對(duì)于 Linux 來(lái)說(shuō),它遵循的是 System V ABI 的 call convention,x86_64 平臺(tái)下函數(shù)調(diào)用約定有且只有一種,調(diào)用者 caller 和被調(diào)用者 callee 需要對(duì)相應(yīng)的寄存器進(jìn)行保存和恢復(fù)操作:

Caller-save registers : RDI, RSI, RDX, RCX, R8, R9, RAX, R10, R11

Callee-save registers : RBX, RBP, R12, R13, R14, R15

調(diào)用約定,gcc 它遵守了嗎?

設(shè)問(wèn):當(dāng)函數(shù)實(shí)現(xiàn)很簡(jiǎn)單,只用到了少量寄存器,那沒(méi)使用到的還需要保存嗎?

答案:it depends。根據(jù)編譯選項(xiàng)決定。

眾所周知,GCC 編譯器有 -O0、-O1、-O2 和 -Ox 等編譯優(yōu)化選項(xiàng),優(yōu)化范圍和深度隨 x 增大而增大(-O0是不優(yōu)化,其中隱含的意思是,它會(huì)嚴(yán)格遵循 ABI 中的調(diào)用約定,對(duì)所有使用的寄存器進(jìn)行保存和恢復(fù))。

Linux 內(nèi)核選用的都是 -O2 優(yōu)化。GCC 會(huì)選擇性的不遵守調(diào)用約定,也就是設(shè)問(wèn)里提到的,不需要保存沒(méi)使用到的寄存器。當(dāng)【運(yùn)行時(shí)替換】撞見(jiàn)【調(diào)用約定】

GCC 之所以可以做這個(gè)優(yōu)化,是因?yàn)?GCC 高屋建瓴,了解程序的執(zhí)行流。當(dāng)它知道 callee,caller 的寄存器分配情況,就會(huì)大膽且安全地做各種優(yōu)化。

但是,運(yùn)行時(shí)替換破壞了這個(gè)假設(shè),GCC 所掌握的 callee 信息,極有可能是錯(cuò)誤的。那么這些優(yōu)化可能會(huì)引發(fā)嚴(yán)重問(wèn)題。這里以一個(gè)具體的實(shí)例進(jìn)行詳細(xì)說(shuō)明,這是一個(gè)用戶態(tài)的例子( x86_64 平臺(tái)):

//test.c 文件//編譯命令:gcc test.c -o test -O2 (kernel 采用的是 O2 優(yōu)化選項(xiàng))//執(zhí)行過(guò)程:。/test//輸入參數(shù):4

#include 《sys/mman.h》#include 《string.h》#include 《stdio.h》#include 《math.h》

#define noinline __attribute__ ((noinline)) //禁止內(nèi)聯(lián)

static noinline int c(int x){ return x * x * x;}

static noinline int b(int x){ return x;}

static noinline int newb(int x){ return c(x * 2) * x;}

static noinline int a(int x){ int volatile tmp = b(x); // tmp = 8 ** 3 * 4 return x + tmp; // return 4(not 8) + tmp}

int main(void){ int x; scanf(“%d”, &x);

if (mprotect((void*)(((unsigned long)&b) & (~0xFFFF)), 15, PROT_WRITE | PROT_EXEC | PROT_READ)) { perror(“mprotect”); return 1; }

/* 利用 jump 指令將函數(shù) b 替換為 newb 函數(shù) */ ((char*)b)[0] = 0xe9; *(long*)((unsigned long)b + 1) = (unsigned long)&newb - (unsigned long)&b - 5; printf(“%d”, a(x)); return 0;}

程序解釋:該程序是對(duì)輸入的數(shù)字進(jìn)行計(jì)算,運(yùn)行時(shí)利用 jump 指令將程序中的函數(shù) b 替換為 newb 函數(shù),即,將 y = x + x 計(jì)算過(guò)程替換為 y = x + (2x) ^ 3 * x;

程序編譯:gcc test.c -o test -O2,這里我們采用的是與編譯內(nèi)核相同的優(yōu)化選項(xiàng) -O2;

程序執(zhí)行:。/test,輸入?yún)?shù):4,輸出結(jié)果:2056;

程序錯(cuò)誤:2056 是錯(cuò)誤的結(jié)果,應(yīng)該是 2052,而且直接調(diào)用 newb 函數(shù)編譯執(zhí)行的結(jié)果是 2052。

該例子說(shuō)明,直接使用 jump 指令替換函數(shù)在 -O2 的編譯優(yōu)化下,會(huì)出現(xiàn)問(wèn)題,安全性受到了質(zhì)疑和沖擊!??!

安全性沖擊:分析問(wèn)題

上述例子中,我們將函數(shù) b 用 jump 指令替換為 newb 函數(shù),在 -O2 的編譯優(yōu)化下出現(xiàn)了計(jì)算錯(cuò)誤的結(jié)果,因此,我們需要對(duì)函數(shù)的調(diào)用執(zhí)行過(guò)程進(jìn)行仔細(xì)分析,挖掘問(wèn)題所在。首先,我們先來(lái)查看一下該程序的反匯編(指令:objdump -d test),并重點(diǎn)關(guān)注 a、b 和 newb 函數(shù):

dda37d7e-297a-11ec-82a8-dac502259ad0.png

圖2 -O2 編譯優(yōu)化的反匯編結(jié)果

匯編解釋:main:-》 將參數(shù) 4 存放到 edi 寄存器中-》 調(diào)用 a 函數(shù):-》 調(diào)用 b 函數(shù),直接跳轉(zhuǎn)到 newb 函數(shù): -》 將 edi 寄存器中的值存放到 edx 寄存器 -》 edi 寄存器與自身相加后結(jié)果放入 edi -》 調(diào)用 c 函數(shù): -》 將 edi 寄存器中的值存到 eax 寄存器 -》 edi 乘以 eax 后結(jié)果放入 eax -》 edi 乘以 eax 后結(jié)果放入 eax -》 返回到 newb 函數(shù) -》 將 edx 與 eax 相乘后結(jié)果放入 eax-》 返回到 a 函數(shù)-》 將 edi 與 eax 相加后結(jié)果放入 eax-》 返回 main 函數(shù)

(注意:b 函數(shù)中沒(méi)有對(duì) edi 寄存器進(jìn)行寫(xiě)操作,而且它的代碼段被修改為 jump 指令跳轉(zhuǎn)到 newb 函數(shù))

數(shù)據(jù)出錯(cuò)的原因在于,在函數(shù) newb 中,使用到了 a 函數(shù)中使用的 edi 寄存器,edi 寄存器中的值在 newb 函數(shù)中被修改為 8,當(dāng) newb 函數(shù)返回后,edi 的值仍然是 8,a 函數(shù)繼續(xù)使用了該值,因此,計(jì)算過(guò)程變?yōu)椋?^3 * 4 + 8 = 2056,而正確的計(jì)算結(jié)果應(yīng)該是 8^3 * 4 + 4 = 2052。

接下來(lái)不進(jìn)行編譯優(yōu)化(-O0),其輸出結(jié)果是正確的 2052,反匯編如下所示:

de1c7954-297a-11ec-82a8-dac502259ad0.png

圖3 不進(jìn)行編譯優(yōu)化的反匯編

從反匯編中可以看到,函數(shù) a 在調(diào)用 b 函數(shù)前,將 edi 寄存器的值存在了棧上,調(diào)用之后,將棧上的數(shù)據(jù)再取出,最后進(jìn)行相加。這就說(shuō)明,-O2 優(yōu)化選項(xiàng)將 edi 寄存器的保存和恢復(fù)操作優(yōu)化掉了,而在調(diào)用約定中,edi 寄存器本就該屬于 caller 進(jìn)行保存/恢復(fù)的。至于為什么編譯器會(huì)進(jìn)行優(yōu)化,我們此刻的猜想是:

a 函數(shù)本來(lái)調(diào)用的是 b 函數(shù),而且編譯器知道 b 函數(shù)中沒(méi)有使用到 edi 寄存器,因此調(diào)用者 a 函數(shù)沒(méi)有對(duì)該寄存器進(jìn)行保存和恢復(fù)操作。但是編譯器不知道的是,在程序運(yùn)行時(shí),b 函數(shù)的代碼段被動(dòng)態(tài)修改,利用 jump 指令替換為 newb 函數(shù),而在 newb 函數(shù)中對(duì) edi 寄存器進(jìn)行了數(shù)據(jù)讀寫(xiě)操作,于是出現(xiàn)了錯(cuò)誤。

這是一個(gè)典型的沒(méi)有保存 caller-save 寄存器導(dǎo)致數(shù)據(jù)出錯(cuò)的場(chǎng)景。而編譯內(nèi)核采用的也是 -O2 選項(xiàng)。如果將該場(chǎng)景應(yīng)用到內(nèi)核函數(shù)熱替換是否會(huì)出現(xiàn)這類問(wèn)題呢?于是,我們帶著問(wèn)題繼續(xù)探索。

安全性沖擊:探索問(wèn)題

不再觀察到 bug

我們構(gòu)造了一個(gè)內(nèi)核函數(shù)熱替換的實(shí)例,將上面的用戶態(tài)的例子移植到我們構(gòu)造的場(chǎng)景中,通過(guò)內(nèi)核模塊修改原函數(shù)的代碼段,用 jump 指令直接替換原來(lái)的 b 函數(shù)。然而加載模塊后,結(jié)果是正確的 2052,經(jīng)過(guò)反匯編我們發(fā)現(xiàn),內(nèi)核中 a 函數(shù)對(duì) edi 寄存器進(jìn)行了保存操作:

de732a88-297a-11ec-82a8-dac502259ad0.png

圖4 內(nèi)核中 a 函數(shù)的反匯編

內(nèi)核和模塊編譯時(shí)采用的是 -O2 優(yōu)化選項(xiàng),而此處 a 函數(shù)并沒(méi)有被優(yōu)化,仍然保存了 edi 寄存器。

此時(shí)我們預(yù)測(cè):對(duì)于內(nèi)核函數(shù)的熱替換來(lái)說(shuō),使用 jump 做函數(shù)替換是安全的。

神奇的 -pg 選項(xiàng)

我們猜想是否是內(nèi)核編譯時(shí)使用其它的編譯選項(xiàng)導(dǎo)致問(wèn)題不能復(fù)現(xiàn)。果不其然,經(jīng)過(guò)探索我們發(fā)現(xiàn)內(nèi)核編譯使用的 -pg 選項(xiàng)導(dǎo)致問(wèn)題不再?gòu)?fù)現(xiàn)。

通過(guò)翻閱 GCC 手冊(cè)得知,-pg 選項(xiàng)是為了支持 GNU 的 gprop 性能分析工具所引入的,它能在函數(shù)中增加一條 call mount 指令,去做一些分析工作。

在內(nèi)核中,如果開(kāi)啟了 CONFIG_FUNCTION_TRACER,則會(huì)使能 -pg 選項(xiàng)。

deb8d1c8-297a-11ec-82a8-dac502259ad0.png

圖5 開(kāi)啟 CONFIG_FUNCTION_TRACER 使能 -pg 選項(xiàng)

FUNCTION_TRACE 即我們常說(shuō)的 ftrace 功能,ftrace 大大提升了內(nèi)核的運(yùn)行時(shí)調(diào)試能力。ftrace 功能除了 -pg 選項(xiàng),還要求打開(kāi) -mfentry 選項(xiàng),后者的作用是將函數(shù)對(duì) mcount 的調(diào)用放到函數(shù)的第一條指令處,然后通過(guò) scripts/recordmcount.pl 腳本將該條 call 指令修改為 nop 指令。但 -mfentry 與本文主題沒(méi)有關(guān)聯(lián),不再細(xì)說(shuō)。

為了驗(yàn)證這個(gè)結(jié)論,我們回到上一節(jié)的用戶態(tài)例子,并且增加了 -pg 編譯選項(xiàng):“gcc test.c -o test -O2 -pg”,此時(shí)運(yùn)行結(jié)果果然正確了。查看其反匯編:

defef0e0-297a-11ec-82a8-dac502259ad0.png

圖6 增加 -pg 選項(xiàng)后的匯編

可以看到,每個(gè)函數(shù)都有 call mcount 指令,而且 a 函數(shù)中將 edi 寄存器保存到 ebx 中,在 newb 函數(shù)中又保存 ebx 寄存器。為什么在增加了 call mount 指令后,會(huì)做寄存器的保存操作?我們猜想,會(huì)不會(huì)是因?yàn)椋捎?call mount 操作相當(dāng)于調(diào)用了一個(gè)未知的函數(shù)( mcount 沒(méi)有定義在同一個(gè)文件中),因此,GCC 認(rèn)為這樣未知的操作可能會(huì)污染了寄存器的數(shù)據(jù),所以它才進(jìn)行了保存現(xiàn)場(chǎng)的操作。

于是我們?nèi)サ袅?-pg 選項(xiàng),手動(dòng)增加了 call mount 的行為進(jìn)行驗(yàn)證:在另一個(gè)源文件 mcount.c 中增加一個(gè)函數(shù) void mcount() { asm(“nop ”); },在 test.c 文件中增加對(duì) mcount 函數(shù)的聲明,a 函數(shù)中增加對(duì)該函數(shù)的調(diào)用:

extern void mcount(); //聲明 mcount 函數(shù)

static noinline int a(int x){ int volatile tmp = b(x); // tmp = 8 ** 3 * 4 mcount(); return x + tmp; // return 4(not 8) + tmp}

經(jīng)過(guò)編譯:gcc test.c mcount.c -O2 后運(yùn)行,發(fā)現(xiàn)計(jì)算結(jié)果正確,而且反匯編中 a 函數(shù)保存了寄存器:

df356d3c-297a-11ec-82a8-dac502259ad0.png

圖7 調(diào)用 mcount 函數(shù)后的匯編

繼續(xù)驗(yàn)證猜想,將 mcount 函數(shù)放在 test.c 文件中,計(jì)算結(jié)果錯(cuò)誤,而且,反匯編中沒(méi)有保存寄存器,于是我們得到了這樣的猜想結(jié)論:

GCC 在編譯某個(gè)源文件時(shí),如果文件內(nèi)的某個(gè)函數(shù)(比如場(chǎng)景中的函數(shù) a)調(diào)用了其它文件中的一個(gè)未知函數(shù)(比如場(chǎng)景中的 mcount 函數(shù)),則 GCC 會(huì)在該函數(shù)中保存寄存器;

開(kāi)啟 -pg 選項(xiàng),增加了對(duì) mcount 的調(diào)用,因此會(huì)在函數(shù)中增加對(duì)寄存器現(xiàn)場(chǎng)的保存操作,對(duì) -O2 選項(xiàng)的函數(shù)調(diào)用優(yōu)化起到了屏蔽作用。

神秘的 -fipa-ra 選項(xiàng):真正的幕后主使

經(jīng)過(guò)我們的探索和資料的查閱,發(fā)現(xiàn)了這個(gè) -fipa-ra 選項(xiàng),可以說(shuō)它是優(yōu)化的幕后主使。GCC 手冊(cè)中給出 -fipa-ra 選項(xiàng)的解釋是:

Use caller save registers for allocation if those registers are not used by any called function. In that case it is not necessary to save and restore them around calls. This is only possible if called functions are part of same compilation unit as current function and they are compiled before it. Enabled at levels -O2, -O3, -Os, however the option is disabled if generated code will be instrumented for profiling (-p, or -pg) or if callee’s register usage cannot be known exactly (this happens on targets that do not expose prologues and epilogues in RTL)。

這里主要是說(shuō),如果開(kāi)啟這個(gè)選項(xiàng),那么,callee 中如果沒(méi)有使用到 caller 使用的寄存器,就沒(méi)有必要保存這些寄存器,前提是,callee 與 caller 在同一個(gè)編譯單元中而且 callee 函數(shù)比 caller 先被編譯,這樣才可能出現(xiàn)前面的優(yōu)化。如果開(kāi)啟了 -O2 及以上的編譯優(yōu)化選項(xiàng),則會(huì)使能 -fipa-ra 選項(xiàng),然而,如果開(kāi)啟了 -p 或者 -pg 這些選項(xiàng),或者,無(wú)法明確 callee 所使用的寄存器,-fipa-ra 選項(xiàng)會(huì)被禁用。

這段話,其實(shí)已經(jīng)能 cover 掉我們前面大部分猜想的測(cè)試驗(yàn)證:

-O2 選項(xiàng)自動(dòng)使能 -fipa-ra 進(jìn)行優(yōu)化:在我們的場(chǎng)景中,函數(shù) a 使用的 edi 寄存器,在函數(shù) b 中沒(méi)有使用到,因此函數(shù) a 被優(yōu)化,沒(méi)有保存 edi 寄存器,但是在 newb 函數(shù)中,使用到了 edi 寄存器,且數(shù)據(jù)被修改,將 newb 函數(shù)替換函數(shù) b,則計(jì)算結(jié)果出錯(cuò);

在 -O2 中使用 -pg 選項(xiàng)會(huì)禁用 -fipa-ra:編譯時(shí)使用 -pg 選項(xiàng),計(jì)算結(jié)果是正確的,而且函數(shù) a 保存了 edi 寄存器,說(shuō)明沒(méi)有對(duì)函數(shù) a 進(jìn)行優(yōu)化;

不在同一編譯單元不會(huì)被優(yōu)化:去掉 -pg 選項(xiàng),在函數(shù) a 中手動(dòng)調(diào)用 mcount 函數(shù),將這個(gè)函數(shù)放在 test.c(與函數(shù) a 為同一編譯單元)與放在另一個(gè)文件 mcount.c(不同編譯單元)中的計(jì)算結(jié)果不同:同一編譯單元中計(jì)算結(jié)果是錯(cuò)誤的,而且函數(shù) a 沒(méi)有保存寄存器現(xiàn)場(chǎng);不在同一編譯單元中,計(jì)算結(jié)果是正確的,函數(shù) a(caller) 保存了寄存器現(xiàn)場(chǎng),因?yàn)榫幾g器無(wú)法明確函數(shù) b(callee)所使用的寄存器。

notrace:它是二度沖擊嗎?

用過(guò) ftrace 或者內(nèi)核開(kāi)發(fā)者應(yīng)該對(duì) notrace 屬性不陌生,內(nèi)核中有一些被 notrace 修飾的函數(shù)。notrace 其實(shí)就是給函數(shù)增加 no_instrument_function 屬性。例如,在 X86 的定義:

#define notrace __attribute__((no_instrument_function))

字面上來(lái)看,notrace 和 -pg 的含義可以說(shuō)完全對(duì)立,-pg 讓 jump 變得安全,是否又會(huì)在 notrace 上栽一個(gè)跟斗呢?幸運(yùn)的是,我們接下來(lái)將看到,notrace 僅僅是禁止了 instrument function,而沒(méi)有破壞安全性。

gcc 手冊(cè)中的 -pg 選項(xiàng)給出這樣的解釋:

Generate extra code to write profile information suitable for the analysis program prof (for -p) or gprof (for -pg)。 You must use this option when compiling the source files you want data about, and you must also use it when linking. You can use the function attribute no_instrument_function to suppress profiling of individual functions when compiling with these options.

這里主要是說(shuō),加上 notrace 屬性的函數(shù),不會(huì)產(chǎn)生調(diào)用 mcount 的行為,那么,是否意味著不再保護(hù)寄存器現(xiàn)場(chǎng),換句話說(shuō),notrace 的出現(xiàn)是否會(huì)繞過(guò)“-pg 選項(xiàng)對(duì) -fipa-ra 優(yōu)化的屏蔽”?于是我們又增加 notrace 屬性進(jìn)行驗(yàn)證:在 a 函數(shù)中增加 notrace 的屬性,因?yàn)?a 函數(shù)是 caller,編譯時(shí)開(kāi)啟 -pg 選項(xiàng),然后檢查計(jì)算結(jié)果及反匯編,最后發(fā)現(xiàn),計(jì)算結(jié)果正確,而且匯編代碼中保存了寄存器現(xiàn)場(chǎng)。

我們又對(duì)所有的函數(shù)追加了 notrace 屬性,計(jì)算結(jié)果正確且寄存器現(xiàn)場(chǎng)被保護(hù)。但是這些簡(jiǎn)單的驗(yàn)證不足以證明,于是我們通過(guò)閱讀 GCC 源碼發(fā)現(xiàn)

通過(guò)源碼閱讀,可以確定的是,當(dāng)使用了 -pg 選項(xiàng)后,會(huì)禁用 -fipa-rq 優(yōu)化選項(xiàng),GCC 檢查每一個(gè)函數(shù)的時(shí)候都會(huì)檢查該選項(xiàng),如果為 false,則不會(huì)對(duì)該函數(shù)進(jìn)行優(yōu)化。

由于 flag_ipa_ra 是一個(gè)全局選項(xiàng),并不是函數(shù)粒度的,notrace 也無(wú)能為力。因此,這里可以排除對(duì) notrace 的顧慮。

安全性保障:得出結(jié)論

經(jīng)過(guò)上述的探索分析以及官方資料的查閱,我們可以得出結(jié)論:

內(nèi)核函數(shù)的熱替換,利用 jump 指令直接跳轉(zhuǎn)到新函數(shù)的方式是安全的;

論據(jù):

Linux 遵循的 System V ABI 中的 call conversion 在 x86-64 下有且只有一種;

GCC -fipa-ra 選項(xiàng)會(huì)對(duì) call conversion 進(jìn)行優(yōu)化,-O2 選項(xiàng)會(huì)自動(dòng)使能該選項(xiàng),但是 -pg 選項(xiàng)會(huì)禁用 -fipa-ra 優(yōu)化選項(xiàng);

notrace 屬性無(wú)法繞過(guò)“ -pg 禁用 -fipa-ra”。

ARM64 下的探索驗(yàn)證

通過(guò)翻閱手冊(cè)得知,ARMv8 ABI 中對(duì)過(guò)程調(diào)用時(shí)通用寄存器的使用準(zhǔn)則如下

Argument registers (X0-X7)

These are used to pass parameters to a function and to return a result. They can be used as scratch registers or as caller-saved register variables that can hold intermediate values within a function, between calls to other functions. The fact that 8 registers are available for passing parameters reduces the need to spill parameters to the stack when compared with AArch32.

Caller-saved temporary registers (X9-X15)

If the caller requires the values in any of these registers to be preserved across a call to another function, the caller must save the affected registers in its own stack frame. They can be modified by the called subroutine without the need to save and restore them before returning to the caller.

Callee-saved registers (X19-X29)

These registers are saved in the callee frame. They can be modified by the called subroutine as long as they are saved and restored before returning.

Registers with a special purpose (X8, X16-X18, X29, X30)

X8 is the indirect result register. This is used to pass the address location of an indirect result, for example, where a function returns a large structure.

X16 and X17 are IP0 and IP1, intra-procedure-call temporary registers. These can be used by call veneers and similar code, or as temporary registers for intermediate values between subroutine calls. They are corruptible by a function. Veneers are small pieces of code which are automatically inserted by the linker, for example when the branch target is out of range of the branch instruction.

X18 is the platform register and is reserved for the use of platform ABIs. This is an additional temporary register on platforms that don‘t assign a special meaning to it.

X29 is the frame pointer register (FP)。

X30 is the link register (LR)。

Figure 9.1 shows the 64-bit X registers. For more information on registers, see 。 For information on floating-point parameters, see Floating-point parameters.

可見(jiàn),ARMv8 ABI 中對(duì)函數(shù)調(diào)用時(shí)的寄存器使用有了明確的規(guī)定。

我們對(duì)于前面 x86-64 下的探索驗(yàn)證過(guò)程在 arm64 平臺(tái)下重新做了測(cè)試,相同的代碼和相同的測(cè)試過(guò)程,得出的結(jié)論和 x86-64 下的結(jié)論是一致的,即,在 arm64 下,直接利用 jump 指令實(shí)現(xiàn)函數(shù)替換同樣是安全的。

其它場(chǎng)景的討論

其它語(yǔ)言不能保證其安全性

對(duì)于 C 語(yǔ)言而言,在不同的架構(gòu)和系統(tǒng)下都有固定的 ABI 和 calling conventions,但是其它的語(yǔ)言不能保證,比如 rust 語(yǔ)言,rust 自身并沒(méi)有固定的 ABI,比如社區(qū)對(duì) rust 定義 ABI 的討論,而且 rustc 編譯器的優(yōu)化和 gcc 可能會(huì)有不同,因此可能也會(huì)出現(xiàn)上述 caller/callee-save 寄存器的問(wèn)題。

kpatch 的真面目

kpatch 利用的是 ftrace 進(jìn)行函數(shù)替換的,它的原理如下所示:

ftrace 的主要作用是用來(lái)做 trace 的,會(huì)在函數(shù)頭部或者尾部 hook 一個(gè)函數(shù)進(jìn)行一些額外的處理,這些函數(shù)在運(yùn)行過(guò)程中可能會(huì)污染被 trace 的函數(shù)的寄存器上下文,因此 ftrace 定義了一個(gè) trampoline 進(jìn)行寄存器的保存和恢復(fù)操作(圖11 中的紅框),這樣從 hook 函數(shù)回來(lái)后,寄存器現(xiàn)場(chǎng)仍然是原來(lái)的模樣。

kpatch 用 ftrace 進(jìn)行函數(shù)替換,hook 的函數(shù)是 kpatch 中的函數(shù),該函數(shù)的作用是修改 regs 中的 ip 字段的值,也就是將新函數(shù)的地址給到了 ip 字段,等 trampoline 恢復(fù)寄存器現(xiàn)場(chǎng)后,就直接跳轉(zhuǎn)到新的函數(shù)函數(shù)去執(zhí)行了。所以,對(duì)于 kpatch 而言,ftrace 的保存和恢復(fù)現(xiàn)場(chǎng)操作保護(hù)的是 kpatch 中修改 ip 字段函數(shù)的過(guò)程,而不是它要替換的新函數(shù)。

如果修復(fù)的是一個(gè)熱函數(shù),那么 ftrace 的 trampoline 會(huì)對(duì)性能產(chǎn)生一定的影響。所以,若考慮到性能的場(chǎng)景,那么使用 jump 指令直接替換函數(shù)可以很大的減少額外的性能開(kāi)銷。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1372

    瀏覽量

    40276
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11292

    瀏覽量

    209323
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4327

    瀏覽量

    62569

原文標(biāo)題:內(nèi)核熱補(bǔ)丁,真的安全么?

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    安科瑞AISD智能安全配電裝置如何降低電氣火災(zāi)安全隱患

    安科瑞徐赟杰 18706165067 AISD系列智能安全配電裝置主要針對(duì)低壓配電側(cè)人員觸電安全事故、線路老化、接地故障、漏電引起電氣火災(zāi)等等常見(jiàn)安全隱患而設(shè)計(jì),具有電不起火、電不斷電、電不漏電、電
    的頭像 發(fā)表于 12-19 11:11 ?52次閱讀
    安科瑞AISD智能<b class='flag-5'>安全</b>配電裝置如何降低電氣火災(zāi)<b class='flag-5'>安全隱患</b>

    路燈漏電會(huì)導(dǎo)致哪些安全隱患

    一、 路燈漏電可能導(dǎo)致的安全隱患包括: ? 觸電事故:路燈漏電可能導(dǎo)致行人或騎行者在不知情的情況下觸電,尤其是在雨后或道路積水的情況下,水分會(huì)降低人體的電阻,增加觸電的風(fēng)險(xiǎn)。 ? 設(shè)備損壞:持續(xù)
    的頭像 發(fā)表于 11-20 13:43 ?104次閱讀
    路燈漏電會(huì)導(dǎo)致哪些<b class='flag-5'>安全隱患</b>

    杜絕安全隱患發(fā)生 SDT270超聲波檢漏儀助力蘇州熱工研究院解決閥門內(nèi)漏問(wèn)題

    閥門在管道系統(tǒng)中起著控制介質(zhì)流向、隔離或連接不同系統(tǒng)、調(diào)節(jié)流量以及保證系統(tǒng)安全運(yùn)行等關(guān)鍵作用。如果閥門發(fā)生內(nèi)漏,可能導(dǎo)致系統(tǒng)壓力失衡,產(chǎn)生安全隱患,甚至可能引發(fā)爆炸、火災(zāi)等嚴(yán)重事故。通過(guò)內(nèi)漏檢測(cè),可以及時(shí)發(fā)現(xiàn)并處理這些潛在的安全隱患
    的頭像 發(fā)表于 11-20 13:39 ?129次閱讀
    杜絕<b class='flag-5'>安全隱患</b>發(fā)生  SDT270超聲波檢漏儀助力蘇州熱工研究院解決閥門內(nèi)漏問(wèn)題

    直流充電樁使用中有哪些電氣安全隱患及解決方案

    充電樁。然而,在充電樁的日常使用中,一些潛在的安全隱患也逐漸浮出水面,這些隱患有可能對(duì)人們的生命與財(cái)產(chǎn)安全構(gòu)成嚴(yán)重威脅。因此,深刻認(rèn)識(shí)并了解這些常見(jiàn)的充電樁安全隱患,以及如何通過(guò)嚴(yán)格的
    的頭像 發(fā)表于 10-30 15:22 ?237次閱讀
    直流充電樁使用中有哪些電氣<b class='flag-5'>安全隱患</b>及解決方案

    大核桃三防對(duì)講手機(jī):鍛鑄工業(yè)安全之堅(jiān)固壁壘,令安全隱患無(wú)處遁形!

    在數(shù)字化日益深入的今天,在工業(yè)生產(chǎn)環(huán)境中,普通的智能手機(jī)往往難以滿足嚴(yán)苛的工作需求,尤其是在面對(duì)惡劣的天氣條件、復(fù)雜的工作場(chǎng)景和潛在的安全隱患時(shí)。因此,一款既具備通信功能又能在惡劣環(huán)境中穩(wěn)定運(yùn)行
    的頭像 發(fā)表于 06-20 11:31 ?268次閱讀
    大核桃三防對(duì)講手機(jī):鍛鑄工業(yè)<b class='flag-5'>安全</b>之堅(jiān)固壁壘,令<b class='flag-5'>安全隱患</b>無(wú)處遁形!

    無(wú)損檢測(cè)有哪些安全隱患

    無(wú)損檢測(cè)技術(shù)在航空、航天、核電、石油、化工、機(jī)械制造等領(lǐng)域具有重要應(yīng)用。然而,由于操作不當(dāng)、設(shè)備故障、環(huán)境因素等原因,無(wú)損檢測(cè)過(guò)程中可能存在安全隱患。本文將詳細(xì)分析無(wú)損檢測(cè)中的安全隱患,并提出相應(yīng)
    的頭像 發(fā)表于 05-27 14:58 ?1606次閱讀

    車載車庫(kù)GPS信號(hào)屏蔽器:保護(hù)隱私,還是安全隱患

    深圳特信電子|車載車庫(kù)GPS信號(hào)屏蔽器:保護(hù)隱私,還是安全隱患
    的頭像 發(fā)表于 05-16 09:00 ?662次閱讀

    XKCON祥控煤棚安全監(jiān)測(cè)系統(tǒng)能夠?qū)γ号锃h(huán)境產(chǎn)生的安全隱患進(jìn)行監(jiān)測(cè)、預(yù)警和控制

    XKCON祥控煤棚安全監(jiān)測(cè)系統(tǒng)由粉塵、煙霧、甲烷、一氧化碳、氧氣等環(huán)境檢測(cè)設(shè)備,煤堆溫度檢測(cè)設(shè)備,現(xiàn)場(chǎng)智能監(jiān)控主機(jī)和遠(yuǎn)程管理軟件組成,能夠?qū)γ号锃h(huán)境可能產(chǎn)生的安全隱患進(jìn)行監(jiān)測(cè)、預(yù)警和控制。
    的頭像 發(fā)表于 04-17 11:00 ?419次閱讀
    XKCON祥控煤棚<b class='flag-5'>安全</b>監(jiān)測(cè)系統(tǒng)能夠?qū)γ号锃h(huán)境產(chǎn)生的<b class='flag-5'>安全隱患</b>進(jìn)行監(jiān)測(cè)、預(yù)警和控制

    淺談大型綜合超市電氣火災(zāi)安全隱患及解決方案

    淺談大型綜合超市電氣火災(zāi)安全隱患及解決方案 張穎姣 安科瑞電氣股份有限公司?上海嘉定201801 摘要 :隨著社會(huì)的不斷進(jìn)步與發(fā)展,各種各樣先進(jìn)的電氣設(shè)備進(jìn)入了大眾的工作和生活,帶來(lái)方便的同時(shí),各種
    的頭像 發(fā)表于 04-15 16:38 ?386次閱讀
    淺談大型綜合超市電氣火災(zāi)<b class='flag-5'>安全隱患</b>及解決方案

    使用 PREEMPT_RT 在 Ubuntu 中構(gòu)建實(shí)時(shí) Linux 內(nèi)核

    的實(shí)時(shí)內(nèi)核補(bǔ)丁來(lái)完成。簡(jiǎn)介我們?cè)榻B過(guò)在Ubuntu22.04中啟用實(shí)時(shí)Linux內(nèi)核有多簡(jiǎn)單,因?yàn)镃anonical已將該內(nèi)核列為一個(gè)選項(xiàng)
    的頭像 發(fā)表于 04-12 08:36 ?2414次閱讀
    使用 PREEMPT_RT 在 Ubuntu 中構(gòu)建實(shí)時(shí) <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)核</b>

    微軟發(fā)布Linux內(nèi)核Rust模塊優(yōu)化補(bǔ)丁

    在此之前,Linux 內(nèi)核中要想實(shí)現(xiàn)模塊初始化,必須先創(chuàng)建一個(gè)實(shí)例,再將其移至特定內(nèi)存空間。然而,經(jīng)過(guò)新補(bǔ)丁調(diào)整后,各模塊可直接在預(yù)設(shè)定好的內(nèi)存地址上完成初始化工作。
    的頭像 發(fā)表于 04-02 15:11 ?458次閱讀

    豐田因安全隱患召回雷克薩斯LX600汽車166輛

    據(jù)公告顯示,本次召回是因?yàn)樽詣?dòng)變速器中多片式離合器結(jié)構(gòu)及控制程序設(shè)計(jì)不當(dāng),導(dǎo)致車輛掛檔至空擋時(shí),離合器不能完全分離,使得驅(qū)動(dòng)系統(tǒng)仍然工作,若此狀況下松開(kāi)剎車,車輛有發(fā)生意外移動(dòng)的風(fēng)險(xiǎn),從而帶來(lái)安全隱患。
    的頭像 發(fā)表于 03-15 16:20 ?842次閱讀

    請(qǐng)問(wèn)如何給STM32MP157上Linux5.4.31打?qū)崟r(shí)內(nèi)核補(bǔ)丁?

    我這邊想給STM32MP157芯片A7上面的Linux打?qū)崟r(shí)內(nèi)核補(bǔ)丁,從而運(yùn)行一些對(duì)實(shí)時(shí)性要求較高的應(yīng)用程序。我看到我的Linux內(nèi)核版本是
    發(fā)表于 03-11 06:09

    醫(yī)院用電安全隱患問(wèn)題的解決方案

    隨著智能配電系統(tǒng)的推廣和普及,智能配電系統(tǒng)的自愈控制將成為從根本上消除用電安全隱患的重要手段。智能配電系統(tǒng)在實(shí)現(xiàn)了運(yùn)行狀態(tài)的可視性,運(yùn)行邏輯的自發(fā)現(xiàn)和運(yùn)行過(guò)程的可控性之后,就可實(shí)現(xiàn)運(yùn)行故障的自修
    的頭像 發(fā)表于 01-30 17:55 ?329次閱讀
    醫(yī)院用電<b class='flag-5'>安全隱患</b>問(wèn)題的解決方案

    如何減少35kV煤礦變電站運(yùn)行中的安全隱患或事故

    電子發(fā)燒友網(wǎng)站提供《如何減少35kV煤礦變電站運(yùn)行中的安全隱患或事故.docx》資料免費(fèi)下載
    發(fā)表于 01-03 10:18 ?0次下載
    RM新时代网站-首页