Linux内核转储---空指针触发kdump


在分析转储时,需要了解到X86寄存器的相关知识,因此在解析kdump转储到的信息之前,需要先对具有代表性的X86寄存器有所了解。

X86常用寄存器

1、数据寄存器:数据寄存器主要用来保存操作数和运算结果等信息,从而节省读取操作数所需占用总线和访问存储器的时间。

64位数据寄存器(通用寄存器):RAX、RBX、RCX、RDX 
32位数据寄存器(通用寄存器):EAX、EBX、ECX、EDX 
16位数据寄存器(通用寄存器):AX、BX、CX、DX

2、变址寄存器:变址寄存器主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。
变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。
变址寄存器它们可作一般的存储器指针使用。在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能。

64位变址寄存器:RSI、RDI
32位变址寄存器:ESI、EDI
16位变址寄存器:SI、DI

3、指针寄存器:指针寄存器主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。
指针寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。

64位指针寄存器:RBP、RSP
32位指针寄存器:EBP、ESP
16位指针寄存器:BP、SP

指令寄存器它们主要用于访问堆栈内的存储单元,并且规定:

(1)BP为基指针(BasePointer)寄存器,用它可直接存取堆栈中的数据;
(2)SP为堆栈指针(StackPointer)寄存器,用它只可访问栈顶。

4、段寄存器:段寄存器是根据内存分段的管理模式而设置的。内存单元的物理地址由段寄存器的值一个偏移量值组合而成的,这样可用两个较少位数的值组合成一个可访问较大物理空间的内存地址。

CS——代码段寄存器(CodeSegmentRegister),其值为代码段的段值;
DS——数据段寄存器(DataSegmentRegister),其值为数据段的段值;
ES——附加段寄存器(ExtraSegmentRegister),其值为附加数据段的段值;
SS——堆栈段寄存器(StackSegmentRegister),其值为堆栈段的段值;
FS——附加段寄存器(ExtraSegmentRegister),其值为附加数据段的段值;
GS——附加段寄存器(ExtraSegmentRegister),其值为附加数据段的段值。

5、指令指针寄存器:指令指针寄存器是存放下次将要执行的指令在代码段的偏移量。在具有预取指令功能的系统中,下次要执行的指令通常已被预取到指令队列中,除非发生转移情况。所以,在理解它们的功能时,不考虑存在指令队列的情况。

64位指令指针寄存器:RIP
32位指令指针寄存器:EIP
16位指令指针寄存器:IP

空指针驱动程序

驱动程序编写:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>

static __init int init_oopsdemo(void)
{
        printk(KERN_INFO "enter oopsdemo\n");
        *((int *)0x00) = 0x19760817;
        return 0;
}

static __exit exit_oopsdemo(void)
{
        printk(KERN_INFO "leave oopsdemo\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangb");

module_init(init_oopsdemo);
module_exit(exit_oopsdemo);

​编译为内核模块后,在系统中直接 insmod 会触发内核崩溃,然后系统会系统第二个内核进行dump文件的转储,转储成功后,系统自动重启,重启后可在/var/crash/目录下找到dump文件。

crash分析

root@root-PC:/var/crash/202204091451# crash /data/linux-source-5.3.0/vmlinux dump.202204091451
  KERNEL: /data/linux-source-5.3.0/vmlinux
    DUMPFILE: dump.202204091451  [PARTIAL DUMP]
        CPUS: 8
        DATE: Sat Apr  9 14:50:59 2022
      UPTIME: 00:08:00
LOAD AVERAGE: 1.03, 0.82, 0.57
       TASKS: 328
    NODENAME: root-PC
     RELEASE: 5.3.18+
     VERSION: #2 SMP Sat Apr 9 14:26:36 CST 2022
     MACHINE: x86_64  (2494 Mhz)
      MEMORY: 16 GB
       PANIC: "Oops: 0002 [#1] SMP PTI" (check log for details)
         PID: 1869
     COMMAND: "insmod"
        TASK: ffff986178c0dc00  [THREAD_INFO: ffff986178c0dc00]
         CPU: 5
       STATE: TASK_RUNNING (PANIC)

log命令查看原系统奔溃时的内核打印:

......
[  126.085316] device eth1 entered promiscuous mode
[ 1020.885933] enter oopsdemo
[ 1020.885941] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 1020.892975] #PF: supervisor write access in kernel mode
[ 1020.898270] #PF: error_code(0x0002) - not-present page
[ 1020.903477] PGD 0 P4D 0
[ 1020.906054] Oops: 0002 [#1] SMP PTI
[ 1020.909592] CPU: 5 PID: 1869 Comm: insmod Kdump: loaded Tainted: P          IOE     5.3.18+ #2
[ 1020.918289] Hardware name: To be filled by O.E.M. To be filled by O.E.M./QM87, BIOS 4.6.5 11/10/2015
[ 1020.927524] RIP: 0010:init_oopsdemo+0x1a/0x1000 [demo_oops]
[ 1020.933149] Code: Bad RIP value.
[ 1020.936411] RSP: 0018:ffffbe37cd127c60 EFLAGS: 00010286
[ 1020.941715] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
[ 1020.948926] RDX: 0000000000000000 RSI: ffff98618db57448 RDI: ffff98618db57448
[ 1020.956137] RBP: ffffbe37cd127c60 R08: 00000000000003ba R09: 0000000000000004
[ 1020.963357] R10: 0000000000000000 R11: 0000000000000001 R12: ffffffffc19ce000
[ 1020.970611] R13: ffff986129846c20 R14: ffffffffc1b90000 R15: ffffbe37cd127e68
[ 1020.977795] FS:  00007f3ba1af8540(0000) GS:ffff98618db40000(0000) knlGS:0000000000000000
[ 1020.985977] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1020.991774] CR2: ffffffffc19cdff0 CR3: 00000003d248a002 CR4: 00000000001606e0
[ 1020.999001] Call Trace:
[ 1021.001487]  do_one_initcall+0x4a/0x1fa
[ 1021.005376]  ? _cond_resched+0x19/0x40
[ 1021.009189]  ? kmem_cache_alloc_trace+0x208/0x220
[ 1021.013950]  do_init_module+0x5f/0x227
[ 1021.017742]  load_module+0x2791/0x2ca0
[ 1021.021530]  __do_sys_finit_module+0xfc/0x120
[ 1021.025904]  ? __do_sys_finit_module+0xfc/0x120
[ 1021.030457]  __x64_sys_finit_module+0x1a/0x20
[ 1021.034875]  do_syscall_64+0x5a/0x130
[ 1021.038569]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 1021.043655] RIP: 0033:0x7f3ba1621959
[ 1021.047285] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ff f4 2c 00 f7 d8 64 89 01 48
[ 1021.066280] RSP: 002b:00007fff48e01f58 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 1021.073942] RAX: ffffffffffffffda RBX: 000055789fe577a0 RCX: 00007f3ba1621959
[ 1021.081180] RDX: 0000000000000000 RSI: 000055789df70d2e RDI: 0000000000000003
[ 1021.088406] RBP: 000055789df70d2e R08: 0000000000000000 R09: 00007f3ba18f4000
[ 1021.095584] R10: 0000000000000003 R11: 0000000000000246 R12: 0000000000000000
[ 1021.102769] R13: 000055789fe57770 R14: 0000000000000000 R15: 0000000000000000
[ 1021.109937] Modules linked in: demo_oops(OE+) nvidia_uvm(OE) usb_sono(OE) rt2800usb rt2x00usb rt2800lib rt2x00lib mac80211 intel_rapl_common x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm snd_hda_codec_hdmi irqbypass snd_hda_codec_realtek crct10dif_pclmul snd_hda_codec_generic crc32_pclmul ledtrig_audio ghash_clmulni_intel aesni_intel aes_x86_64 snd_hda_intel crypto_simd nvidia_drm(POE) nvidia_modeset(POE) cryptd snd_intel_dspcfg glue_helper snd_hda_codec snd_hwdep rapl nvidia(POE) snd_hda_core intel_cstate snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi e1000e snd_seq snd_seq_device snd_timer snd soundcore psmouse ie31200_edac lpc_ich rtc_cmos sch_fq_codel ip_tables x_tables
[ 1021.172092] CR2: 0000000000000000

可以清楚的看到内核崩溃错误原因为:BUG: kernel NULL pointer dereference, address: 0000000000000000,为 insmod 命令导致的内核崩溃。

​ 通过 bt 命令 和 dis 命令可以看到是哪一行代码是直接诱因:

crash> bt -l
PID: 1869   TASK: ffff986178c0dc00  CPU: 5   COMMAND: "insmod"
 #0 [ffffbe37cd1278c0] machine_kexec at ffffffffa146b833
    /data/linux-source-5.3.0/arch/x86/kernel/machine_kexec_64.c: 441
 #1 [ffffbe37cd127920] __crash_kexec at ffffffffa154bb82
    /data/linux-source-5.3.0/kernel/kexec_core.c: 957
 #2 [ffffbe37cd1279f0] crash_kexec at ffffffffa154ca21
    /data/linux-source-5.3.0/./include/linux/compiler.h: 225
 #3 [ffffbe37cd127a10] oops_end at ffffffffa143219d
    /data/linux-source-5.3.0/arch/x86/kernel/dumpstack.c: 334
 #4 [ffffbe37cd127a38] no_context at ffffffffa147b2b9
    /data/linux-source-5.3.0/arch/x86/mm/fault.c: 848
 #5 [ffffbe37cd127aa8] __bad_area_nosemaphore at ffffffffa147b6b0
    /data/linux-source-5.3.0/arch/x86/mm/fault.c: 935
 #6 [ffffbe37cd127af0] bad_area at ffffffffa147b946
    /data/linux-source-5.3.0/arch/x86/mm/fault.c: 962
 #7 [ffffbe37cd127b18] __do_page_fault at ffffffffa147c392
    /data/linux-source-5.3.0/arch/x86/mm/fault.c: 1426
 #8 [ffffbe37cd127b80] do_page_fault at ffffffffa147c51c
    /data/linux-source-5.3.0/arch/x86/mm/fault.c: 1554
 #9 [ffffbe37cd127bb0] page_fault at ffffffffa2201204
    /data/linux-source-5.3.0/arch/x86/entry/entry_64.S: 1203
    [exception RIP: _MODULE_INIT_START_demo_oops+26]
    RIP: ffffffffc19ce01a  RSP: ffffbe37cd127c60  RFLAGS: 00010286
    RAX: 0000000000000000  RBX: 0000000000000000  RCX: 0000000000000000
    RDX: 0000000000000000  RSI: ffff98618db57448  RDI: ffff98618db57448
    RBP: ffffbe37cd127c60   R8: 00000000000003ba   R9: 0000000000000004
    R10: 0000000000000000  R11: 0000000000000001  R12: ffffffffc19ce000
    R13: ffff986129846c20  R14: ffffffffc1b90000  R15: ffffbe37cd127e68
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
#10 [ffffbe37cd127c68] do_one_initcall at ffffffffa140294a
    /data/linux-source-5.3.0/init/main.c: 941
#11 [ffffbe37cd127ce0] do_init_module at ffffffffa1549628
    /data/linux-source-5.3.0/kernel/module.c: 3522
#12 [ffffbe37cd127d08] load_module at ffffffffa15486a1
    /data/linux-source-5.3.0/kernel/module.c: 3872
#13 [ffffbe37cd127e48] __do_sys_finit_module at ffffffffa1548e9c
    /data/linux-source-5.3.0/kernel/module.c: 3962
#14 [ffffbe37cd127f20] __x64_sys_finit_module at ffffffffa1548eda
    /data/linux-source-5.3.0/kernel/module.c: 3938
#15 [ffffbe37cd127f30] do_syscall_64 at ffffffffa14043ba
    /data/linux-source-5.3.0/arch/x86/entry/common.c: 296
#16 [ffffbe37cd127f50] entry_SYSCALL_64_after_hwframe at ffffffffa220008c
    /data/linux-source-5.3.0/arch/x86/entry/entry_64.S: 184
    RIP: 00007f3ba1621959  RSP: 00007fff48e01f58  RFLAGS: 00000246
    RAX: ffffffffffffffda  RBX: 000055789fe577a0  RCX: 00007f3ba1621959
    RDX: 0000000000000000  RSI: 000055789df70d2e  RDI: 0000000000000003
    RBP: 000055789df70d2e   R8: 0000000000000000   R9: 00007f3ba18f4000
    R10: 0000000000000003  R11: 0000000000000246  R12: 0000000000000000
    R13: 000055789fe57770  R14: 0000000000000000  R15: 0000000000000000
    ORIG_RAX: 0000000000000139  CS: 0033  SS: 002b

​ 从调用堆栈可以看出:

​ 当前进程ID为1869,task_struct数据结构地址为ffff986178c0dc00,当前运行在CPU5上,当前进程运行命令为insmod。

​ 造成内核崩溃的指令为:[exception RIP: _MODULE_INIT_START_demo_oops+26]],即_MODULE_INIT_START_demo_oops 函数的第 26 字节的地方,存放在RIP寄存器(RIP寄存器存放当前指令的地址)中。RDI寄存器存放着函数第一个参数的地址,RSI寄存器存放着函数第二个参数的地址。

​ 然后反汇编内核崩溃的地址:

crash> dis -l ffffffffc19ce01a
0xffffffffc19ce01a <_MODULE_INIT_START_demo_oops+26>:   movl   $0x19760817,(%rax)
crash>

​ 可以看到是在将0x19760817值移动到rax寄存器中时出现了异常。对应代码来看可找到错误的位置:

 *((int *)0x00) = 0x19760817;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
kdump是一个在Linux内核崩溃时收集dump信息的工具。它的设计目标是在遇到内核崩溃时,能够提供完整的内核转储信息,以便开发人员进行分析和调试。 kdump的工作原理是在系统启动时,设置一个保护内存区域,用于在内核崩溃时存储dump信息。当系统出现崩溃时,kdump触发一个内核崩溃的路径,将所有的内核状态信息存储在这个保护区域中。然后,kdump会加载一个独立的小内核,这个小内核只包含了最小的功能,仅仅用于将之前存储的内核状态信息写入磁盘。这样,即使主内核发生崩溃,kdump仍然能够将dump信息保存下来。 kdump所收集的dump信息包含了内核的堆栈、寄存器的状态、内核模块列表、内核代码和数据段等。这些信息对于开发人员分析和调试内核问题非常有帮助。无论是内核中的软件错误、硬件故障还是系统配置错误,都能够通过kdump的信息来定位和解决问题。 为了使用kdump,我们首先需要安装kexec工具,然后对系统进行一些配置,如设置内存保护区域的大小、crashkernel参数等。配置完成后,重新启动系统,当系统崩溃时,kdump就会自动工作。 总结来说,kdump是一个非常有用的Linux内核调试工具,能够在内核崩溃时提供完整的dump信息,为开发人员提供了方便的分析和调试手段。它能够帮助我们快速定位和解决各种内核问题,提高系统的稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值