linux空指针异常能捕获到吗,一次kernel panic分析--空指针in handle_IRQ_event
一、故障現(xiàn)象
內(nèi)核panic,打印如下:
點(diǎn)擊(此處)折疊或打開(kāi)
Unable to handle kernel NULL pointer dereference at 0000000000000039 RIP:
[] handle_IRQ_event+0x44/0xa6
PGD 61df63067 PUD 61ea5a067 PMD 0
Oops: 0000 [1] SMP
last sysfs file: /devices/system/cpu/cpu0/cpufreq/scaling_max_freq
CPU 8
Modules linked in: ossmod(U) tipc(U) bsp_smbus_ctrl(U) bonding autofs4 hidp rfcomm l2cap bluetooth lockd sunrpc ipv6 xfrm_nalgo crypto_api cpufreq_ondemand acpi_cpufreq freq_table dm_mirror d
m_multipath scsi_dh video hwmon backlight sbs i2c_ec button battery asus_acpi acpi_memhotplug ac lp floppy esh(U) snd_hda_intel snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_de
vice sg snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_page_alloc snd_hwdep snd igb i2c_i801 i2c_core soundcore 8021q serio_raw parport_pc parport e1000(U) pcspkr dm_raid45 dm_message dm_reg
ion_hash dm_log dm_mod dm_mem_cache ata_piix libata shpchp mptsas mptscsih mptbase scsi_transport_sas sd_mod scsi_mod ext3 jbd uhci_hcd ohci_hcd ehci_hcd
Pid: 0, comm: swapper Tainted: G 2.6.18-164.el5 #1
RIP: 0010:[] [] handle_IRQ_event+0x44/0xa6
RSP: 0018:ffff81032fca7f28 EFLAGS: 00010202
RAX: 0000000000000001 RBX: 0000000000000000 RCX: 00000000000000b2
RDX: ffff810009028520 RSI: ffff81032fc98000 RDI: ffff81032b1c7180
RBP: 0000000000000001 R08: ffff81032fca0000 R09: ffffffff800967c5
R10: ffff81032fca7f30 R11: ffff81032fca1ee8 R12: 00000000000000b2
R13: 0000000000000001 R14: ffff81032fca1df8 R15: ffff81032fca1df8
FS: 0000000000000000(0000) GS:ffff81010b373b40(0000) knlGS:0000000000000000
CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 0000000000000039 CR3: 000000061f37c000 CR4: 00000000000006e0
Process swapper (pid: 0, threadinfo ffff81032fca0000, task ffff81033ab0f7e0)
Stack: ffffffff803e3580 000000000000b200 00000000000000b2 ffff81032acdee40
ffffffff803e35bc ffffffff800b9808 ffffffff8001235a 00000000000000b2
ffff81032fca1df8 00000000009c1418 ffff81062dc4d000 0000000000000800
Call Trace:
[] __do_IRQ+0xa4/0x103
[] __do_softirq+0x89/0x133
[] do_IRQ+0xe7/0xf5
[] ret_from_intr+0x0/0xa
[] acpi_processor_idle+0x274/0x463
[] acpi_processor_idle+0x26a/0x463
[] acpi_processor_idle+0x0/0x463
[] acpi_processor_idle+0x0/0x463
[] cpu_idle+0x95/0xb8
[] start_secondary+0x45a/0x469
從打印看,是在handle_IRQ_event函數(shù)中遇到了空指針
二、Vmcore分析
1、反匯編handle_IRQ_event,獲取RIP所在的指令
點(diǎn)擊(此處)折疊或打開(kāi)
crash> dis -l handle_IRQ_event
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 134
0xffffffff80010b30 : push %r14
include/trace/irq.h: 7
0xffffffff80010b32 : cmpl $0x0,3859215(%rip) # 0xffffffff803bee48 <__tracepoint_irq_entry.10785>
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 134
0xffffffff80010b39 : mov %rsi,%r14
0xffffffff80010b3c : push %r13
0xffffffff80010b3e : push %r12
0xffffffff80010b40 : mov %edi,%r12d
0xffffffff80010b43 : push %rbp
0xffffffff80010b44 : mov %rdx,%rbp
0xffffffff80010b47 : push %rbx
include/trace/irq.h: 7
0xffffffff80010b48 : je 0xffffffff80010b68
0xffffffff80010b4a : mov 3859199(%rip),%rbx # 0xffffffff803bee50 <__tracepoint_irq_entry.10785>
0xffffffff80010b51 : test %rbx,%rbx
0xffffffff80010b54 : je 0xffffffff80010b68
0xffffffff80010b56 : mov %r14,%rsi
0xffffffff80010b59 : mov %r12d,%edi
0xffffffff80010b5c : callq *(%rbx)
0xffffffff80010b5e : add $0x8,%rbx
0xffffffff80010b62 : cmpq $0x0,(%rbx)
0xffffffff80010b66 : jmp 0xffffffff80010b54
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 142
0xffffffff80010b68 : testb $0x20,0x8(%rbp)
0xffffffff80010b6c : jne 0xffffffff80010b6f
include/asm/irqflags.h: 80
0xffffffff80010b6e : sti
0xffffffff80010b6f : xor %r13d,%r13d
0xffffffff80010b72 : xor %ebx,%ebx
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 146
0xffffffff80010b74 : mov 0x38(%rbp),%rsi
0xffffffff80010b78 : mov %r14,%rdx
從之前的堆棧可以看出RIP為:handle_IRQ_event+0x44/0xa6
0x44折算成十進(jìn)制為68,所以需要關(guān)注handle_IRQ_event+68行:
0xffffffff80010b74 : ? ? ? mov ? ?0x38(%rbp),%rsi
從之前的匯編分析,rsi的值應(yīng)該是上級(jí)函數(shù)傳入,不太可能在其中出現(xiàn)空指針,問(wèn)題應(yīng)該出在0x38(%rbp)上,也就是rbp上。
2、結(jié)合handle_IRQ_event源代碼分析
handle_IRQ_event代碼如下:
點(diǎn)擊(此處)折疊或打開(kāi)
132 irqreturn_t handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
133 struct irqaction *action)
134 {
135 irqreturn_t ret, retval = IRQ_NONE;
136 unsigned int status = 0;
137
138 trace_irq_entry(irq, regs);
139
140 handle_dynamic_tick(action);
141
142 if (!(action->flags & IRQF_DISABLED))
143 local_irq_enable_in_hardirq();
144
145 do {
146 ret = action->handler(irq, action->dev_id, regs);
147 if (ret == IRQ_HANDLED)
148 status |= action->flags;
149 retval |= ret;
150 action = action->next;
151 } while (action);
152
153 if (status & IRQF_SAMPLE_RANDOM)
154 add_interrupt_randomness(irq);
155 local_irq_disable();
156
157 trace_irq_exit(irq, retval);
158
159 return retval;
160 }
之前反匯編中有代碼行提示:
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 146
可以看出問(wèn)題出在146行:
146 ? ? ? ? ret = action->handler(irq, action->dev_id, regs);
可以看出0x38(%rbp)應(yīng)該對(duì)應(yīng)action->dev_id(因?yàn)橥ㄟ^(guò)偏移訪問(wèn),結(jié)構(gòu)體訪問(wèn)基本如此),如此可見(jiàn)rbp中存放的即action變量的的值,
分析源代碼,action是通過(guò)入?yún)魅氲?#xff0c;但是在146-151行的循環(huán)中,150行,對(duì)action重新賦值了。
150 ? ? ? ? action = action->next;
如此,可以推測(cè)action->next出問(wèn)題了,也就是通過(guò)入?yún)魅氲腶ction的next成員出問(wèn)題了,of course,也可能是一開(kāi)始的傳入的如此就有問(wèn)題了。
繼續(xù)分析action入?yún)⒌闹怠?/p>
3、入?yún)?action)分析
action被放入了rbp寄存器,看如下匯編行:
0xffffffff80010b44 : ? ? ? mov ? ?%rdx,%rbp
可以看出,action入?yún)⑹峭ㄟ^(guò)rdx寄存器傳入的,并不是我們通常認(rèn)為的通過(guò)堆棧。需要進(jìn)一步分析上級(jí)行數(shù)中的rdx寄存器的值。
4、上級(jí)函數(shù)__do_IRQ分析
點(diǎn)擊(此處)折疊或打開(kāi)
crash> bt
PID: 0 TASK: ffff81033ab0f7e0 CPU: 8 COMMAND: "swapper"
#0 [ffff81032fca7c80] crash_kexec at ffffffff800ac5b9
#1 [ffff81032fca7d40] __die at ffffffff80065127
#2 [ffff81032fca7d80] do_page_fault at ffffffff80066da7
#3 [ffff81032fca7e70] error_exit at ffffffff8005dde9
[exception RIP: handle_IRQ_event+68]
RIP: ffffffff80010b74 RSP: ffff81032fca7f28 RFLAGS: 00010202
RAX: 0000000000000001 RBX: 0000000000000000 RCX: 00000000000000b2
RDX: ffff810009028520 RSI: ffff81032fc98000 RDI: ffff81032b1c7180
RBP: 0000000000000001 R8: ffff81032fca0000 R9: ffffffff800967c5
R10: ffff81032fca7f30 R11: ffff81032fca1ee8 R12: 00000000000000b2
R13: 0000000000000001 R14: ffff81032fca1df8 R15: ffff81032fca1df8
ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018
#4 [ffff81032fca7f20] handle_IRQ_event at ffffffff80010b81
#5 [ffff81032fca7f50] __do_IRQ at ffffffff800b9808
#6 [ffff81032fca7f90] do_IRQ at ffffffff8006c997
--- ---
可見(jiàn),上級(jí)函數(shù)為_(kāi)_do_IRQ。
對(duì)照__do_IRQ源代碼,可以看出調(diào)用handle_IRQ_event的地方為240行,所以關(guān)注匯編中的相關(guān)行附件代碼即可
點(diǎn)擊(此處)折疊或打開(kāi)
174 fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
175 {
176 struct irq_desc *desc = irq_desc + irq;
177 struct irqaction *action;
178 unsigned int status;
...
235 for (;;) {
236 irqreturn_t action_ret;
237
238 spin_unlock(&desc->lock);
239
240 action_ret=handle_IRQ_event(irq,regs,action);
241 if (!noirqdebug)
242 note_interrupt(irq, desc, action_ret, regs);
243
244 spin_lock(&desc->lock);
245 if (likely(!(desc->status & IRQ_PENDING)))
246 break;
247 desc->status &= ~IRQ_PENDING;
248 }
...
260 }
繼續(xù)反匯編__do_IRQ
點(diǎn)擊(此處)折疊或打開(kāi)
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 222
0xffffffff800b97f1 <__do_irq>: je 0xffffffff800b9845 <__do_irq>
include/asm/spinlock.h: 62
0xffffffff800b97f3 <__do_irq>: movl $0x1,0x3c(%rbx)
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 240
0xffffffff800b97fa <__do_irq>: mov %r13,%rdx
0xffffffff800b97fd <__do_irq>: mov %r15,%rsi
0xffffffff800b9800 <__do_irq>: mov %r12d,%edi
0xffffffff800b9803 <__do_irq>: callq 0xffffffff80010b30
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 241
0xffffffff800b9808 <__do_irq>: cmpl $0x0,3358161(%rip) # 0xffffffff803ed5e0
0xffffffff800b980f <__do_irq>: jne 0xffffffff800b9821 <__do_irq>
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 242
0xffffffff800b9811 <__do_irq>: mov %r15,%rcx
0xffffffff800b9814 <__do_irq>: mov %eax,%edx
0xffffffff800b9816 <__do_irq>: mov %rbx,%rsi
0xffffffff800b9819 <__do_irq>: mov %r12d,%edi
可以看出edx的值來(lái)源于r13,再回看handle_IRQ_event函數(shù)的反匯編,在該函數(shù)的開(kāi)始對(duì)r13寄存器進(jìn)行了壓棧
點(diǎn)擊(此處)折疊或打開(kāi)
0xffffffff80010b30 : push %r14
include/trace/irq.h: 7
0xffffffff80010b32 : cmpl $0x0,3859215(%rip) # 0xffffffff803bee48 <__tracepoint_irq_entry.10785>
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 134
0xffffffff80010b39 : mov %rsi,%r14
0xffffffff80010b3c : push %r13
所以,可以從handle_IRQ_event函數(shù)的棧幀中找到action的值。r13應(yīng)該位于該棧幀中的第3個(gè)值,因?yàn)榈谝粋€(gè)必然為函數(shù)的返回地址,第二個(gè)值為r14(在r13之前壓入)。
看看handle_IRQ_event函數(shù)的棧
點(diǎn)擊(此處)折疊或打開(kāi)
crash> bt -f
PID: 0 TASK: ffff81033ab0f7e0 CPU: 8 COMMAND: "swapper"
#0 [ffff81032fca7c80] crash_kexec at ffffffff800ac5b9
ffff81032fca7c88: ffff81032fca1df8 ffff81032fca1df8
ffff81032fca7c98: 0000000000000001 00000000000000b2
ffff81032fca7ca8: 0000000000000001 0000000000000000
ffff81032fca7cb8: ffff81032fca1ee8 ffff81032fca7f30
ffff81032fca7cc8: ffffffff800967c5 ffff81032fca0000
ffff81032fca7cd8: 0000000000000001 00000000000000b2
ffff81032fca7ce8: ffff810009028520 ffff81032fc98000
ffff81032fca7cf8: ffff81032b1c7180 ffffffffffffffff
ffff81032fca7d08: ffffffff80010b74 0000000000000010
ffff81032fca7d18: 0000000000010202 ffff81032fca7f28
ffff81032fca7d28: 0000000000000018 ffff81032fca7e78
ffff81032fca7d38: ffffffff802a4d0d ffffffff80065127
.......
#4 [ffff81032fca7f20] handle_IRQ_event at ffffffff80010b81
ffff81032fca7f28: ffffffff803e3580 000000000000b200
ffff81032fca7f38: 00000000000000b2 ffff81032acdee40
ffff81032fca7f48: ffffffff803e35bc ffffffff800b9808
#5 [ffff81032fca7f50] __do_IRQ at ffffffff800b9808
ffff81032fca7f58: ffffffff8001235a 00000000000000b2
ffff81032fca7f68: ffff81032fca1df8 00000000009c1418
ffff81032fca7f78: ffff81062dc4d000 0000000000000800
ffff81032fca7f88: ffffffff803ea360 ffffffff8006c997
可以看出action的值應(yīng)該為ffff81032acdee40,看看其中的內(nèi)容,順便驗(yàn)證下。
action為irqaction類型的結(jié)構(gòu)體:
點(diǎn)擊(此處)折疊或打開(kāi)
crash> struct irqaction ffff81032acdee40
struct irqaction {
handler = 0xffffffff8827628b,
flags = 0,
mask = {
bits = {0, 0, 0, 0}
},
name = 0xffff81032b1c7000 "eth0-rx-3",
dev_id = 0xffff81032bef56b8,
next = 0x1,
irq = 178,
dir = 0xffff810328db2180
}
可以看出該irqaction結(jié)構(gòu)體對(duì)應(yīng)于eth0網(wǎng)卡的接收中斷,中斷號(hào)為178,注意其中的next,為0x1,這就是問(wèn)題所在。
從log信息可以看出空指針具體的值為:0000000000000039 ,正好=0x1+38,所以說(shuō)明分析是正確的。
分析代碼邏輯和irqaction的定義:
點(diǎn)擊(此處)折疊或打開(kāi)
crash> struct irqaction
struct irqaction {
irqreturn_t (*handler)(int, void *, struct pt_regs *);
long unsigned int flags;
cpumask_t mask;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
}
SIZE: 88
這里的next應(yīng)該在有中斷共享時(shí)使用,即同一個(gè)irq號(hào)對(duì)應(yīng)多個(gè)ISR,如果有共享,那么這里的next指向共享該irq的下一個(gè)action.
從/proc/interrupts信息,可以看出178號(hào)中斷并沒(méi)有共享的情況,所以,這里的next應(yīng)該為null,為0x1顯然有問(wèn)題了。
從irqaction中的值分析,next前后的數(shù)據(jù)都是正常的,只有next從0突變成1了,基本可以排除由于內(nèi)存越界導(dǎo)致內(nèi)存寫(xiě)壞的情況。
分析設(shè)置next指針的所有代碼,只有設(shè)置地址和刪除的代碼,并沒(méi)有發(fā)現(xiàn)有直接設(shè)置為1的地方。
那為什么如此呢?要繼續(xù)分析就很困難了。
5、分析irq_desc
沒(méi)有其它分析思路了,嘗試分析action所在的結(jié)構(gòu)體irq_desc,看看其中的內(nèi)容,該結(jié)構(gòu)體的值需要從上級(jí)函數(shù)__do_IRQ分析,見(jiàn)其代碼176行:
176 ? ? struct irq_desc *desc = irq_desc + irq;
其中irq_desc為全局變量(數(shù)組),存放所有的irq_desc,按編號(hào)排列。
對(duì)照其匯編:
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 176
0xffffffff800b9784 : ? ? ? lea ? ?0xffffffff803d8380(%rbp),%rbx
lea指令的意思是將0xffffffff803d8380(%rbp)地址(即rbp+0xffffffff803d8380),賦值給rbx,這里顯然0xffffffff803d8380就是irq_desc全局變量的地址。
確認(rèn)一下:
點(diǎn)擊(此處)折疊或打開(kāi)
crash> struct irq_desc 0xffffffff803d8380
struct irq_desc {
handle_irq = 0,
chip = 0xffffffff803ec7e0,
handler_data = 0x0,
chip_data = 0x0,
action = 0xffffffff803018a0,
status = 0,
depth = 0,
wake_depth = 0,
irq_count = 22386,
irqs_unhandled = 0,
lock = {
raw_lock = {
slock = 1
}
},
affinity = {
bits = {1, 0, 0, 0}
},
cpu = 0,
pending_mask = {
bits = {0, 0, 0, 0}
},
move_irq = 0,
dir = 0xffff81062ddbd180
}
crash> struct irqaction 0xffffffff803018a0
struct irqaction {
handler = 0xffffffff8006e9da ,
flags = 32,
mask = {
bits = {0, 0, 0, 0}
},
name = 0xffffffff802b3dfa "timer",
dev_id = 0x0,
next = 0x0,
irq = 0,
dir = 0x0
}
可以看出位于該全局變量的第一個(gè)元素為時(shí)鐘中斷對(duì)應(yīng)的描述符,說(shuō)明分析正確。但我們這里需要分析的是178號(hào)中斷(irq好前面已經(jīng)分析得到了),應(yīng)該怎么查找呢。
繼續(xù)看匯編代碼,可以看出178號(hào)中斷對(duì)應(yīng)的描述符在irq_desc數(shù)組中的偏移量存放在rbp中了,查看rbp后續(xù)的匯編代碼,并沒(méi)有對(duì)進(jìn)行壓棧處理,且當(dāng)前函數(shù)不是最頂級(jí)的堆棧函數(shù),所以無(wú)法直接從vmcore中獲取rbp的值,只能想其它辦法了。
繼續(xù)看匯編代碼:
/usr/src/debug/kernel-2.6.18/linux-2.6.18.x86_64/kernel/irq/handle.c: 176
0xffffffff800b977c : ? ? ? mov ? ?%rcx,%rbp
0xffffffff800b977f : ? ? ? shl ? ?$0x8,%rbp
這里的rcx應(yīng)該存放的是irq的編號(hào),也就是178,將rcx左移8位后即得到rbp(shl ? ?$0x8,%rbp),即178*256=rbp=0xb200
0xffffffff803d8380+0xb200=0xffffffff803e3580,該地址即為178號(hào)中斷對(duì)應(yīng)的描述符的地址了:
點(diǎn)擊(此處)折疊或打開(kāi)
crash> struct irq_desc FFFFFFFF803E3580
struct irq_desc {
handle_irq = 0,
chip = 0xffffffff80323c40,
handler_data = 0x0,
chip_data = 0x0,
action = 0xffff81032acdee40,
status = 65536,
depth = 0,
wake_depth = 0,
irq_count = 78785,
irqs_unhandled = 0,
lock = {
raw_lock = {
slock = 1
}
},
affinity = {
bits = {256, 0, 0, 0}
},
cpu = 0,
pending_mask = {
bits = {32768, 0, 0, 0}
},
move_irq = 1,
dir = 0xffff810328db2380
}
crash> struct irqaction 0xffff81032acdee40
struct irqaction {
handler = 0xffffffff8827628b,
flags = 0,
mask = {
bits = {0, 0, 0, 0}
},
name = 0xffff81032b1c7000 "eth0-rx-3",
dev_id = 0xffff81032bef56b8,
next = 0x1,
irq = 178,
dir = 0xffff810328db2180
exactly,正是我們需要的,于是找到了描述符,看看其內(nèi)容,其中
irq_count = 78785,
irq_count用于監(jiān)測(cè)中斷是否掛起的計(jì)數(shù),不為0表明之前已處理過(guò)該中斷,說(shuō)明該中斷之前正在被正常處理,突然直接出了問(wèn)題。
至此,確實(shí)無(wú)法繼續(xù)分析了,從故障現(xiàn)象看,內(nèi)核問(wèn)題可能性不大,更像是是內(nèi)存的1bit跳變導(dǎo)致的問(wèn)題,但是通常的服務(wù)器環(huán)境中,內(nèi)存(控制器)硬件應(yīng)該有ECC校驗(yàn)功能,可以自動(dòng)糾正1bit跳變的情況才對(duì)。
核對(duì)了一下出現(xiàn)問(wèn)題的硬件環(huán)境,dmicode命令中可以看到內(nèi)存硬件確實(shí)有ECC校驗(yàn)功能,但是發(fā)現(xiàn)CPU cache硬件中沒(méi)有包含ECC校驗(yàn)功能:
Error Correction Type: None
看了看其它類型的服務(wù)器,一些服務(wù)器有ECC校驗(yàn)功能:
Error Correction Type: Single-bit ECC
因此,沒(méi)有其它疑點(diǎn),只能懷疑是內(nèi)存(或者說(shuō)是cache)的1bit跳變硬件故障導(dǎo)致。
但沒(méi)有進(jìn)一步證據(jù)了,有其它想法歡迎討論。
總結(jié)
以上是生活随笔為你收集整理的linux空指针异常能捕获到吗,一次kernel panic分析--空指针in handle_IRQ_event的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 优盘文件打不开怎么修复 优盘文件打不开的
- 下一篇: linux ntp 'ntp_reque