Linux -----oops錯誤
阿新 • • 發佈:2019-01-09
An oops displays the processor status at the time of the fault, including the contents of the CPU registers and other seemingly incomprehensible information. The message is generated by printk statements in the fault handler (arch/*/kernel/traps.c) and is dispatched as described earlier in Section 4.2.1).
一條
Let's look at one such message. Here's what results from dereferencing a NULL pointer on a PC running Version 2.6 of the kernel. The most relevant information here is the instruction pointer (EIP), the address of the faulty instruction.
下面我們來看一條這樣的訊息。這是通過在一臺執行著
Unable to handle kernel NULL pointer dereference at virtual address 00000000 printing eip: d083a064 Oops: 0002 [#1] SMP CPU: 0 EIP: 0060:[] 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: [] vfs_write+0xb8/0x130 [] sys_write+0x42/0x70 [] syscall_call+0x7/0xb Code: 89 15 00 00 00 00 c3 90 8d 74 26 00 83 ec 0c b8 00 a6 83 d0
This message was generated by writing to a device owned by the faulty module, a module built deliberately to demonstrate failures. The implementation of the write method of faulty.c is trivial:
這條訊息是由一個問題模組向其裝置執行寫操作時引起的,該模組是特意為示範故障而構建的。
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;
}
As you can see, what we do here is dereference a NULL pointer. Since 0 is never a valid pointer value, a fault occurs, which the kernel turns into the oops message shown earlier. The calling process is then killed.
如你所見,我們在這裡做的就是廢棄一個NULL指標。因為0從來都不是一個可用的指標值,所以會引發一個故障,核心會簡單地將其轉換為oops訊息並顯示。然後其呼叫程序會被殺死。
The faulty module has a different fault condition in its read implementation: faulty
該示例模組在其read函式中則有著不同的故障條件:
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;
}
This method copies a string into a local variable; unfortunately, the string is longer than the destination array. The resulting buffer overflow causes an oops when the function returns. Since the return instruction brings the instruction pointer to nowhere land, this kind of fault is much harder to trace, and you can get something such as the following:
該函式將一個字串賦給一個區域性變數;不幸的是,字串的長度超出了目標陣列的範圍。當函式返回時就會導致緩衝區溢位而引起一條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:[] 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: [] sys_read+0x42/0x70 [] syscall_call+0x7/0xb Code: Bad EIP value.
In this case, we see only part of the call stack (vfs_read and faulty_read are missing), and the kernel complains about a "bad EIP value." That complaint, and the offending address (ffffffff) listed at the beginning are both hints that the kernel stack has been corrupted.
這種情況下,你只能看到部分函式呼叫的堆疊情況(vfs_read和faulty_read丟失了),而且核心會為了一個“不可用的EIP值”而抱怨。這種抱怨以及開始部分列出的討厭地址(ffffffff)都暗示了核心堆疊已經坍塌。
In general, when you are confronted with an oops, the first thing to do is to look at the location where the problem happened, which is usually listed separately from the call stack. In the first oops shown above, the relevant line is:
通常,當你面臨一個oops時,首要問題就是檢視故障的發生位置,它通常會與函式呼叫的堆疊資訊分開列出。對於上面第一個oops,與此相關的資訊是:
EIP is at faulty_write+0x4/0x10 [faulty]
Here we see that we were in the function faulty_write , which is located in the faulty module (which is listed in square brackets). The hex numbers indicate that the instruction pointer was 4 bytes into the function, which appears to be 10 (hex) bytes long. Often that is enough to figure out what the problem is.
這裡可以看出我們正位於faulty模組(方括號中的是模組名稱)的faulty_write函式中。十六進位制的數字指明瞭該函式中的指令指標長度為4位元組,而現在看起來則有10(十六進位制)位元組長。通常這足以查明問題的所在了。
If you need more information, the call stack shows you how you got to where things fell apart. The stack itself is printed in hex form; with a bit of work, you can often determine the values of local variables and function parameters from the stack listing. Experienced kernel developers can benefit from a certain amount of pattern recognition here; for example, if we look at the stack listing from the faulty_read oops:
如果你需要更多資訊,函式呼叫的堆疊資訊將會告訴你怎樣找到已崩潰的東西。堆疊資訊會以十六進位制列出;稍加分析,你就能從中辨別出局部變數以及函式引數。有經驗的核心開發者會從中獲得很大的幫助;例如,faulty_read函式的堆疊資訊如下所示:
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
The ffffffff at the top of the stack is part of our string that broke things. On the x86 architecture, by default, the user-space stack starts just below 0xc0000000; thus, the recurring value 0xbfffda70 is probably a user-space stack address; it is, in fact, the address of the buffer passed to the read system call, replicated each time it is passed down the kernel call chain. On the x86 (again, by default), kernel space starts at 0xc0000000, so values above that are almost certainly kernel-space addresses, and so on.
位於堆疊頂部的ffffffff是引發故障的字串的一部分。在x86體系中,預設使用者空間中的堆疊地址是小於0xc00000000的;因此,其中0xbfffda70很有可能是一個使用者空間的堆疊地址;實際上它就是傳遞給read系統呼叫的緩衝區的地址,它在核心呼叫鏈中每次被下傳時都會被複制。在x86中(再次說明,預設的),核心空間地址起始自0xc00000000,所以可以基本確定凡是大於該值的地址都是屬於核心空間的地址。
Finally, when looking at oops listings, always be on the lookout for the "slab poisoning" values discussed at the beginning of this chapter. Thus, for example, if you get a kernel oops where the offending address is 0xa5a5a5a5, you are almost certainly forgetting to initialize dynamic memory somewhere.
最後要注意的一點是,當你檢視oops資訊時,始終要留意本章開始時討論的“slab poisoning”的值。因此,如果一條核心oops中出現了討厭的地址值0xa5a5a5a5,那麼你肯定是在什麼地方忘記初始化動態分配的記憶體了。
Please note that you see a symbolic call stack (as shown above) only if your kernel is built with the CONFIG_KALLSYMS option turned on. Otherwise, you see a bare, hexadecimal listing, which is far less useful until you have decoded it in other ways.
請注意要想看到一條可讀的呼叫堆疊資訊(如上所示),你必須要在構建核心時啟用CONFIG_KALLSYMS選項。否則你將會看到一個原始的十六進位制列表,在你使用其他方法譯解它之前它幾乎沒什麼用處。