[fw]Understanding a Kernel Oops!

An “Oops” is what the kernel throws at us when it finds something faulty, or an exception, in the kernel code. It’s somewhat like the segfaults of user-space. An Oops dumps its message on the console; it contains the processor status and the CPU registers of when the fault occurred. The offending process that triggered this Oops gets killed without releasing locks or cleaning up structures. The system may not even resume its normal operations sometimes; this is called an unstable state. Once an Oops has occurred, the system cannot be trusted any further.

Let’s try to generate an Oops message with sample code, and try to understand the dump.

Setting up the machine to capture an Oops

The running kernel should be compiled with CONFIG_DEBUG_INFO, and syslogd should be running. To generate and understand an Oops message, Let’s write a  sample kernel module,oops.c:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

static void create_oops() {

        *(int *)0 = 0;

}

static int __init my_oops_init(void) {

        printk("oops from the module\n");

        create_oops();

       return (0);

}

static void __exit my_oops_exit(void) {

        printk("Goodbye world\n");

}

module_init(my_oops_init);

module_exit(my_oops_exit);

The associated Makefile for this module is as follows:

obj-m   := oops.o

KDIR    := /lib/modules/$(shell uname -r)/build

PWD     := $(shell pwd)

SYM=$(PWD)

all:

        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

Once executed, the module generates the following Oops:

BUG: unable to handle kernel NULL pointer dereference at (null)

IP: [<ffffffffa03e1012>] my_oops_init+0x12/0x21 [oops]

PGD 7a719067 PUD 7b2b3067 PMD 0

Oops: 0002 [#1] SMP

last sysfs file: /sys/devices/virtual/misc/kvm/uevent

CPU 1

Pid: 2248, comm: insmod Tainted: P           2.6.33.3-85.fc13.x86_64

RIP: 0010:[<ffffffffa03e1012>]  [<ffffffffa03e1012>] my_oops_init+0x12/0x21 [oops]

RSP: 0018:ffff88007ad4bf08  EFLAGS: 00010292

RAX: 0000000000000018 RBX: ffffffffa03e1000 RCX: 00000000000013b7

RDX: 0000000000000000 RSI: 0000000000000046 RDI: 0000000000000246

RBP: ffff88007ad4bf08 R08: ffff88007af1cba0 R09: 0000000000000004

R10: 0000000000000000 R11: ffff88007ad4bd68 R12: 0000000000000000

R13: 00000000016b0030 R14: 0000000000019db9 R15: 00000000016b0010

FS:  00007fb79dadf700(0000) GS:ffff880001e80000(0000) knlGS:0000000000000000

CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b

CR2: 0000000000000000 CR3: 000000007a0f1000 CR4: 00000000000006e0

DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000

DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400

Process insmod (pid: 2248, threadinfo ffff88007ad4a000, task ffff88007a222ea0)

Stack:

ffff88007ad4bf38 ffffffff8100205f ffffffffa03de060 ffffffffa03de060

 0000000000000000 00000000016b0030 ffff88007ad4bf78 ffffffff8107aac9

 ffff88007ad4bf78 00007fff69f3e814 0000000000019db9 0000000000020000

Call Trace:

[<ffffffff8100205f>] do_one_initcall+0x59/0x154

[<ffffffff8107aac9>] sys_init_module+0xd1/0x230

[<ffffffff81009b02>] system_call_fastpath+0x16/0x1b

Code: <c7> 04 25 00 00 00 00 00 00 00 00 31 c0 c9 c3 00 00 00 00 00 00 00

RIP  [<ffffffffa03e1012>] my_oops_init+0x12/0x21 [oops]

RSP <ffff88007ad4bf08>

CR2: 0000000000000000

Understanding the Oops dump

Let’s have a closer look at the above dump, to understand some of the important bits of information.

BUG: unable to handle kernel NULL pointer dereference at (null)

The first line indicates a pointer with a NULL value.

IP: [<ffffffffa03e1012>] my_oops_init+0x12/0x21 [oops]

IP is the instruction pointer.

Oops: 0002 [#1] SMP

This is the error code value in hex. Each bit has a significance of its own:

  • bit 0 == 0 means no page found, 1 means a protection fault
  • bit 1 == 0 means read, 1 means write
  • bit 2 == 0 means kernel, 1 means user-mode
  • [#1] — this value is the number of times the Oops occurred. Multiple Oops can be triggered as a cascading effect of the first one.

CPU 1

This denotes on which CPU the error occurred.

Pid: 2248, comm: insmod Tainted: P           2.6.33.3-85.fc13.x86_64

The Tainted flag points to P here. Each flag has its own meaning. A few other flags, and their meanings, picked up from kernel/panic.c:

  • P — Proprietary module has been loaded.
  • F — Module has been forcibly loaded.
  • S — SMP with a CPU not designed for SMP.
  • R — User forced a module unload.
  • M — System experienced a machine check exception.
  • B — System has hit bad_page.
  • U — Userspace-defined naughtiness.
  • A — ACPI table overridden.
  • W — Taint on warning.

RIP: 0010:[<ffffffffa03e1012>]  [<ffffffffa03e1012>] my_oops_init+0x12/0x21 [oops]

RIP is the CPU register containing the address of the instruction that is getting executed. 0010comes from the code segment register. my_oops_init+0x12/0x21 is the <symbol> + the offset/length.

RSP: 0018:ffff88007ad4bf08  EFLAGS: 00010292

RAX: 0000000000000018 RBX: ffffffffa03e1000 RCX: 00000000000013b7

RDX: 0000000000000000 RSI: 0000000000000046 RDI: 0000000000000246

RBP: ffff88007ad4bf08 R08: ffff88007af1cba0 R09: 0000000000000004

R10: 0000000000000000 R11: ffff88007ad4bd68 R12: 0000000000000000

R13: 00000000016b0030 R14: 0000000000019db9 R15: 00000000016b0010

This is a dump of the contents of some of the CPU registers.

Stack:

ffff88007ad4bf38 ffffffff8100205f ffffffffa03de060 ffffffffa03de060

 0000000000000000 00000000016b0030 ffff88007ad4bf78 ffffffff8107aac9

 ffff88007ad4bf78 00007fff69f3e814 0000000000019db9 0000000000020000

The above is the stack trace.

Call Trace:

[<ffffffff8100205f>] do_one_initcall+0x59/0x154

[<ffffffff8107aac9>] sys_init_module+0xd1/0x230

[<ffffffff81009b02>] system_call_fastpath+0x16/0x1b

The above is the call trace — the list of functions being called just before the Oops occurred.

Code: <c7> 04 25 00 00 00 00 00 00 00 00 31 c0 c9 c3 00 00 00 00 00 00 00

The Code is a hex-dump of the section of machine code that was being run at the time the Oops occurred.

Debugging an Oops dump

The first step is to load the offending module into the GDB debugger, as follows:

[[email protected] oops]# gdb oops.ko

GNU gdb (GDB) Fedora (7.1-18.fc13)

Reading symbols from /code/oops/oops.ko...done.

(gdb) add-symbol-file oops.o 0xffffffffa03e1000

add symbol table from file "oops.o" at

    .text_addr = 0xffffffffa03e1000

Next, add the symbol file to the debugger. The add-symbol-file command’s first argument isoops.o and the second argument is the address of the text section of the module. You can obtain this address from /sys/module/oops/sections/.init.text (where oops is the module name):

(gdb) add-symbol-file oops.o 0xffffffffa03e1000

add symbol table from file "oops.o" at

    .text_addr = 0xffffffffa03e1000

(y or n) y

Reading symbols from /code/oops/oops.o...done.

From the RIP instruction line, we can get the name of the offending function, and disassemble it.

(gdb) disassemble my_oops_init

Dump of assembler code for function my_oops_init:

   0x0000000000000038 <+0>:    push   %rbp

   0x0000000000000039 <+1>:    mov    $0x0,%rdi

   0x0000000000000040 <+8>:    xor    %eax,%eax

   0x0000000000000042 <+10>:    mov    %rsp,%rbp

   0x0000000000000045 <+13>:    callq  0x4a <my_oops_init+18>

   0x000000000000004a <+18>:    movl   $0x0,0x0

   0x0000000000000055 <+29>:    xor    %eax,%eax

   0x0000000000000057 <+31>:    leaveq

   0x0000000000000058 <+32>:    retq

End of assembler dump.

Now, to pin point the actual line of offending code, we add the starting address and the offset. The offset is available in the same RIP instruction line. In our case, we are adding0x0000000000000038 + 0x012 =  0x000000000000004a. This points to the movl instruction.

(gdb) list *0x000000000000004a

0x4a is in my_oops_init (/code/oops/oops.c:6).

1    #include <linux/kernel.h>

2    #include <linux/module.h>

3    #include <linux/init.h>

4    

5    static void create_oops() {

6        *(int *)0 = 0;

7    }

This gives the code of the offending function.

References

The kerneloops.org website can be used to pick up a lot of Oops messages to debug. The Linux kernel documentation directory has information about Oops — kernel/Documentation/oops-tracing.txt. This, and numerous other online resources, were used while creating this article.

Related Posts:

[fw]Understanding a Kernel Oops!,布布扣,bubuko.com

时间: 2024-10-13 16:33:41

[fw]Understanding a Kernel Oops!的相关文章

Linux 段错误详解

By Falcon of TinyLab.org 2015/05/12 背景 笔者早年写过一篇:<可恶的"Segmentation faults"之初级总结篇>,网络转载甚多.多年下来,关于段错误的讨论依旧很热烈,该问题也还是很常见.所以打算在这里再系统地梳理一下该问题的来龙去脉. 什么是段错误 下面是来自 Answers.com 的定义: A segmentation fault (often shortened to segfault) is a particular

Kernel boot options

There are three ways to pass options to the kernel and thus control its behavior: When building the kernel. Most of this book discusses these options. When starting the kernel. Usually, parameters are passed to the kernel when it is invoked from a bo

依据linux Oops信息准确定位错误代码所在行

在linux下调tvp5150am1的过程中,遇到了一kernel oops,内容如下: [   66.714603] Unable to handle kernel paging request at virtual address 00100104 [   66.721658] pgd = d37d0000 [   66.724344] [00100104] *pgd=950c2831, *pte=00000000, *ppte=00000000 [   66.730587] Interna

kset学习demo以及Oops反汇编调试例子【原创】

demo1: obj-m := kmod-demo1.o MAKEOPT := ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- ARM_LINUX_KERNEL := /home/zhangbh/5830/zhangbh_svn_si_update/firmware/build_dir/linux-brcm5830_sxxx-prolin2/linux-3.6.5 PWD = $(shell pwd) all: $(MAKE) $(MAKEOPT)

关于call_rcu在内核模块退出时可能引起kernel panic的问题

http://paulmck.livejournal.com/7314.html RCU的作者,paul在他的blog中有提到这个问题,也明确提到需要在module exit的地方使用rcu_barrier来等待保证call_rcu的回调函数callback能够执行完成,然后再正式卸载模块,方式快速卸载之后call_back回调发现空指针的问题,从而导致kernel panic的问题. RCU and unloadable modules Jun. 8th, 2009 at 1:38 PM Th

由于uvc驱动函数缺少return语句而导致内核Oops一例

一.实验环境 1.软件 a) Vmware版本:Vmware Workstation 12.5.7 b) Ubuntu版本:9.10 c) 内核版本:2.6.31.14 d) gcc版本:4.4.1 e) gdb版本:7.0 2.摄像头硬件 百问网自制uvc摄像头 3.排查过程中,使用到的工具 a) printk b) objdump c) strace d)gdb 二.前言 用C语言写程序时,如果定义一个带返回值的函数,但在函数体最后却缺少了return 语句, 程序编译并运行起来后,有时会产

由于uvc驱动函数缺少return语句而导致内核oops的一例

一.实验环境 1.软件 a) Vmware版本:Vmware Workstation 12.5.7 b) Ubuntu版本:9.10 c) 内核版本:2.6.31.14 d) gcc版本:4.4.1 e) gdb版本:7.0 2.摄像头硬件 百问网自制uvc摄像头 3.排查过程中,使用到的工具 a) printk b) objdump c) strace d)gdb 二.前言 用C语言写程序时,如果定义一个带返回值的函数,但在函数体最后却缺少了return 语句, 程序编译并运行起来后,有时会产

分析Linux内核创建一个新进程的过程【转】

转自:http://www.cnblogs.com/MarkWoo/p/4420588.html 前言说明 本篇为网易云课堂Linux内核分析课程的第六周作业,本次作业我们将具体来分析fork系统调用,来分析Linux内核创建新进程的过程 关键词:fork, 系统调用,进程 *运行环境:** Ubuntu 14.04 LTS x64 gcc 4.9.2 gdb 7.8 vim 7.4 with vundle 分析 分析方法说明 PCB包含了一个进程的重要运行信息,所以我们将围绕在创建一个新进程时

虚拟机 ubuntu 安装 Mercury MW150U 无线网卡(AR9271芯片组)

1. 确认无线无卡的芯片组 xp下(前提是已经安装了其xp驱动),将无线网卡插进PC,设备管理器中,选无线网卡,在详细信息中,选服务,知其芯片组为AR9271 2. 确认需要下载的无线网卡驱动 根据以下网站 https://wireless.wiki.kernel.org/en/users/drivers/atheros 我们选择ath9k_htc https://wireless.wiki.kernel.org/en/users/drivers/ath9k_htc 在其网页中,看到如下信息 说