oops 訊息 Unable to handle kernel NULL pointer dereference at virtual address

來源:互聯網
上載者:User

大部分 bug 以解引用 NULL 指標或者使用其他不正確指標值來表現自己的. 此類 bug 通常的輸出是一個 oops 訊息.

處理器使用的任何地址幾乎都是一個虛擬位址, 通過一個複雜的頁表結構映射為物理地址(例外是記憶體管理子系統自己使用的物理地址). 當解引用一個無效的指標, 分頁機制無法映射指標到一個物理地址, 處理器發出一個頁錯誤給作業系統. 如果地址無效, 核心無法"頁入"缺失的地址; 它(常常)產生一個 oops 如果在處理器處於管理員模式時發生這個情況.

一個 oops 顯示了出錯時的處理器狀態, 包括CPU 寄存器內容和其他看來不可理解的資訊. 訊息由錯誤處理的 printk 語句產生( arch/*/kernel/traps.c )並且如同前面 "printk" 一節中描述的被指派.

我們看一個這樣的訊息. 這是來自在運行 2.6 核心的 PC 上一個 NULL 指標導致的結果. 這裡最相關的資訊是指令指標(EIP), 錯誤指令的地址.

Unable to handle kernel NULL pointer dereference at virtual address 00000000 printing eip:  d083a064  Oops: 0002 [#1]  SMP  CPU:  0  EIP:  0060:[<d083a064>]  Not tainted  EFLAGS: 00010246  (2.6.6)  EIP is at faulty_write+0x4/0x10 [faulty]  eax: 00000000  ebx: 00000000  ecx: 00000000  edx: 00000000  esi: cf8b2460  edi: cf8b2480  ebp: 00000005  esp: c31c5f74  ds: 007b  es: 007b  ss: 0068  Process bash (pid: 2086, threadinfo=c31c4000 task=cfa0a6c0) Stack: c0150558 cf8b2460 080e9408 00000005 cf8b2480 00000000 cf8b2460 cf8b2460 fffffff7 080e9408 c31c4000 c0150682 cf8b2460 080e9408 00000005 cf8b2480 00000000 00000001 00000005 c0103f8f 00000001 080e9408 00000005 00000005 Call Trace: [<c0150558>] vfs_write+0xb8/0x130 [<c0150682>] sys_write+0x42/0x70 [<c0103f8f>] syscall_call+0x7/0xbCode: 89 15 00 00 00 00 c3 90 8d 74 26 00 83 ec 0c b8 00 a6 83 d0 

寫入一個由壞模組擁有的裝置而產生的訊息, 一個故意用來示範失效的模組. faulty.c 的 write 方法的實現是瑣細的:

ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos){        /* make a simple fault by dereferencing a NULL pointer */        *(int *)0 = 0;        return 0;}

如你能見, 我們這裡做的是解引用一個 NULL 指標. 因為 0 一直是一個無效的指標值, 一個錯誤發生, 由核心轉變為前面展示的 oops 訊息. 調用進程接著被殺掉.

錯誤模組有不同的錯誤情況在它的讀實現中:

ssize_t faulty_read(struct file *filp, char __user *buf, size_t count, loff_t *pos){    int ret;    char stack_buf[4];    /* Let's try a buffer overflow */    memset(stack_buf, 0xff, 20);    if (count > 4)        count = 4; /* copy 4 bytes to the user */    ret = copy_to_user(buf, stack_buf, count);    if (!ret)        return count;    return ret;}

這個方法拷貝一個字串到一個本地變數; 不幸的是, 字串長於目的數組. 當函數返回時導致的緩衝區溢出引起一次 oops . 因為返回指令使指令指標到不知何處, 這類的錯誤很難跟蹤, 並且你得到如下的:

EIP: 0010:[<00000000>]Unable to handle kernel paging request at virtual address ffffffff printing eip:  ffffffff  Oops: 0000 [#5]  SMP  CPU:  0  EIP:  0060:[<ffffffff>]  Not tainted  EFLAGS: 00010296  (2.6.6)  EIP is at 0xffffffff  eax: 0000000c  ebx: ffffffff  ecx: 00000000  edx: bfffda7c  esi: cf434f00  edi: ffffffff  ebp: 00002000  esp: c27fff78  ds: 007b  es: 007b  ss: 0068  Process head (pid: 2331, threadinfo=c27fe000 task=c3226150) Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7 bfffda70 c27fe000 c0150612 cf434f00 bfffda70 00002000 cf434f20 00000000 00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70 Call Trace: [<c0150612>] sys_read+0x42/0x70 [<c0103f8f>] syscall_call+0x7/0xb Code: Bad EIP value. 

這個情況, 我們只看到部分的呼叫堆疊( vfs_read 和 faulty_read 丟失 ), 核心抱怨一個"壞 EIP 值". 這個抱怨和在開頭列出的犯錯的地址 ( ffffffff ) 都暗示核心堆棧已被破壞.

通常, 當你面對一個 oops, 第一件事是查看發生問題的位置, 常常與呼叫堆疊分開列出. 在上面展示的第一個 oops, 相關的行是:

EIP is at faulty_write+0x4/0x10 [faulty] 

這裡我們看到, 我們曾在函數 faulty_write, 它位於 faulty 模組( 在方括弧中列出的 ). 16 進位數指示指令指標是函數內 4 位元組, 函數看來是 10 ( 16 進位 )位元組長. 常常這就足夠來知道問題是什麼.

如果你需要更多資訊, 呼叫堆疊展示給你如何得知在哪裡壞事的. 堆棧自己是 16 機制形式列印的; 做一點工作, 你經常可以從堆棧的列表中決定本地變數的值和函數參數. 有經驗的核心開發人員可以從這裡的某些模式識別中獲益; 例如, 如果你看來自 faulty_read oops 的堆棧列表:

Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7 bfffda70 c27fe000 c0150612 cf434f00 bfffda70 00002000 cf434f20 00000000 00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70 

堆棧頂部的 ffffffff 是我們壞事的字串的一部分. 在 x86 體系, 預設地, 使用者空間堆棧開始於 0xc0000000; 因此, 迴圈值 0xbfffda70 可能是一個使用者堆棧地址; 實際上, 它是傳遞給 read 系統調用的緩衝地址, 每次下傳過系統調用鏈時都被複製. 在 x86 (又一次, 預設地), 核心空間開始於 0xc0000000, 因此這個之上的值幾乎肯定是核心空間的地址, 等等.

最後, 當看一個 oops 列表, 一直監視本章開始討論的"slab 毒害"值. 例如,如果你得到一個核心 oops, 裡面的犯錯地址時 0xa5a5a5a5a5, 你幾乎肯定 - 某個地方在初始化動態記憶體.

請注意, 只在你的核心是開啟 CONFIG_KALLSYMS 選項而編譯時間可以看到符號的呼叫堆疊. 否則, 你見到一個裸的, 16 機制列表, 除非你以別的方式對其解碼, 它是遠遠無用的.

4.5.2. 系統掛起

儘管核心代碼的大部分 bug 以 oops 訊息結束, 有時候它們可能完全掛起系統. 如果系統掛起, 沒有訊息列印. 例如, 如果代碼進入一個無限迴圈, 核心停止調度,[15] 並且系統不會響應任何動作, 包括魔術 Ctrl-Alt-Del 按鍵組合. 你有 2 個選擇來處理系統掛起-- 或者事先阻止它們, 或者能夠事後調試它們.

你可阻止無限迴圈通過插入 schedule 引用在戰略點上. schedule 調用( 如你可能猜到的 )調度器, 因此, 允許別的進程從當前進程偷取 CPU 資料. 如果一個進程由於你的驅動的bug而在核心空間迴圈, schedule 調用使你能夠殺掉進程在跟蹤發生了什麼之後.

你應當知道, 當然, 如何對 schedule 的調用可能創造一個附加的重入調用源到你的驅動, 因為它允許別的進程運行. 這個重入正常地不應當是問題, 假定你在你的驅動中已經使用了合適的加鎖. 然而, 要確認在你的驅動持有一個自旋鎖的任何時間不能調用 schedule.

如果你的驅動真正掛起了系統, 並且你不知道在哪裡插入 schedule 調用, 最好的方式是加入一些列印訊息並且寫到控制台(如果需要, 改變 console_loglevel 值).

有時候系統可能看來被掛起, 但是沒有. 例如, 這可能發生在鍵盤以某個奇怪的方式保持鎖住的時候. 這些假掛起可通過查看你為此目的啟動並執行程式的輸出來檢測. 一個你的顯示器上的時鐘或者系統負載表是一個好的狀態監控器; 只要他繼續更新, 調度器就在工作.

對許多的上鎖一個必不可少的工具是"魔術 sysrq 鍵", 在大部分體繫上都可用. 魔鍵 sysrq 是 PC 鍵盤上 alt 和 sysrq 鍵組合來發出的, 或者在別的平台上使用其他特殊鍵(詳見 documentation/sysrq.txt), 在串口控制台上也可用. 一個第三鍵, 與這 2 個一起按下, 進行許多有用的動作中的一個:

r關閉鍵盤原始模式; 用在一個崩潰的應用程式( 例如 X 伺服器 )可能將你的鍵盤搞成一個奇怪的狀態.

k 調用"安全注意鍵"( SAK ) 功能. SAK 殺掉在當前控制台的所有啟動並執行進程, 給你一個乾淨的終端.

s 進行一個全部磁碟的緊急同步.

uumount. 試圖重新載入所有磁碟在唯讀模式. 這個操作, 常常在 s 之後馬上調用, 可以節省大量的檔案系統檢查時間, 在系統處於嚴重麻煩時.

b boot. 立刻重啟系統. 確認先同步和重新載入磁碟.

p 列印處理器訊息.

t 列印當前工作清單.

m 列印記憶體資訊.

有別的魔術 sysrq 功能存在; 完整內容看核心源碼的文檔目錄中的 sysrq.txt. 注意魔術 sysrq 必須在核心配置中顯式使能, 大部分的發布沒有使能它, 因為明顯的安全理由. 對於用來開發驅動的系統, 然而, 使能魔術 sysrq 值得為它自己建立一個新核心的麻煩. 魔術 sysrq 可能在運行時關閉, 使用如下的一個命令:

echo 0 > /proc/sys/kernel/sysrq

如果非特權使用者能夠接觸你的系統鍵盤, 你應當考慮關閉它, 來阻止有意或無意的損壞. 一些以前的核心版本預設關閉 sysrq, 因此你需要在運行時使能它, 通過向同樣的 /proc/sys 檔案寫入 1.

sysrq 操作是非常有用, 因此它們已經對不能接觸到控制台的系統管理員可用. 檔案 /proc/sysrq-trigger 是一個唯寫的進入點, 這裡你可以觸發一個特殊的 sysrq 動作, 通過寫入關聯的命令字元; 接著你可收集核心日誌的任何輸出資料. 這個 sysrq 的進入點是一直工作的, 即便 sysrq 在控制台上被關閉.

如果你經曆一個"活掛", 就是你的驅動粘在一個迴圈中, 但是系統作為一個整體功能正常, 有幾個技術值得瞭解. 經常地, sysrq p 功能直接指向出錯的函數. 如果這個不行, 你還可以使用核心剖析功能. 建立一個開啟剖析的核心, 並且用命令列中 profile=2 來啟動它. 使用 readprofile 工具複位剖析計數器, 接著使你的驅動進入它的迴圈. 一會兒後, 使用 readprofile 來看核心在哪裡消耗它的時間. 另一個更進階的選擇是 oprofile, 你可以也考慮下. 檔案 documentation/basic_profiling.txt 告訴你啟動剖析器所有需要知道的東西.

在追逐系統掛起時一個值得使用的防範措施是以唯讀方式載入你的磁碟(或者卸載它們). 如果磁碟是唯讀或者卸載的, 就沒有風險損壞檔案系統或者使它處於不一致的狀態. 另外的可能性是使用一個通過 NFS, 網路檔案系統, 來載入它的全部檔案系統的電腦, 核心的"NFS-Root"功能必須開啟, 在啟動時必須傳遞特殊的參數. 在這個情況下, 即便不依靠 sysrq 你也會避免檔案系統破壞, 因為檔案系統的一致有 NFS 伺服器來管理, 你的裝置驅動不會關閉它.

轉自 http://oss.org.cn/kernel-book/ldd3/ch04s05.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.