华为欧拉服务器操作系统软件 V2.0 管理员指南 05

常见问题分析方法

常见问题分析方法

踩堆栈问题

  1. 编写代码模拟stack被踩。
    #include <linux/kernel.h>
    #include <linux/module.h>
    
    
    static int __init a_init(void)
    {
            char msg[10] = {0};
    
            printk("a init\n");
    
            strcpy(msg, "this modules is testing module, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    
    
            return 0;
    }
    
    static void __exit a_exit(void)
    {
            printk("a exit\n");
    }
    
    module_init(a_init)
    module_exit(a_exit)
    MODULE_LICENSE("GPL");
  2. 分析方法:

    使用“bt”命令显示出问题进程堆栈,发现无法解析堆栈。

    根据RSP: ffff8801fddabf20,读取stack的内存,根据内存内容规律推出代码位置。

踩页表问题

  1. 编写代码模拟页表被踩。
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/kprobes.h>
    
    #include <linux/kallsyms.h>
    #include <linux/syscalls.h>
    #include <linux/slab.h>
    #include <linux/kdebug.h>
    #include <asm/apic.h>
    #include <asm/pgalloc.h>
    
    static int __init jprobe_init(void)
    {
            unsigned long address;
            pgd_t *pgd;
            pud_t *pud;
            pmd_t *pmd;
    
            printk(KERN_INFO "zk--- in \n");
    
            address = (unsigned long)kmalloc(512 ,GFP_KERNEL);
            pgd = pgd_offset(current->active_mm, address);
            pud = pud_offset(pgd, address);
            pmd = pmd_offset(pud, address);
    
            memset(pmd, 0, 16);
    
            printk("test: %x \n", *(int*)address);
    
            return 0;
    }
    
    static void __exit jprobe_exit(void)
    {
            printk(KERN_INFO "zk--- out \n");
    }
    
    module_init(jprobe_init)
    module_exit(jprobe_exit)
    MODULE_LICENSE("GPL");
  2. 分析方法:

    观察出错的堆栈,可以发现是一个地址错误。

    观察RIP: ffffffff8108af68的回报指令,该指令用到(RAX+0x10)的地址。

    观察这个地址的页表,可以发现PMD指向了0,说明这个地址的页表有问题。

    由此,初步分析出页表被踩后,可以通过被非法访问内存空间的内容,推出可能的模块;或者通过crash时间点的附近日志分析可疑模块。

踩静态变量问题

分析方法:

通过“sym -l”命令可以看到内核符号,先找到被踩静态变量的地址,然后观察其附近地址还有哪些静态变量。常见的场景是其前面的某个变量,操作过程中溢出,造成周围变量都被踩。

硬件DMA非法访问内存空间问题

  1. 问题现象

    堆栈没有任何规律,唯一的相同点就是code区域是一样的,而且数值是有规律的,全部是“fe 0b ad ca”。

    [ 3051.054204] Call Trace:
    [ 3051.057035]  [<ffffffff8115faa9>] path_openat+0xd9/0x420
    [ 3051.063054]  [<ffffffff8115ff2c>] do_filp_open+0x4c/0xc0
    [ 3051.069103]  [<ffffffff81150b71>] do_sys_open+0x171/0x1f0
    [ 3051.075223]  [<ffffffff8144fc53>] ia32_do_call+0x13/0x13
    [ 3051.081261] Code: fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad <ca> fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca fe 0b ad ca
  2. 分析方法:

    由于内核的code区是只读的,通过线性地址无法去修改,因此可以得出造成这个修改的必然使用的是物理地址,硬件DMA有这个可能。但是目标地址的内容与硬件设备的DMA没有直接联系,除了硬件DMA之外,BIOS有直接操作物理内存的能力,需要分析BIOS代码。

    最终发现驱动代码错误,DMA过程踩到BIOS,造成BIOS运行过程踩到内核内存。

死锁问题

  1. 分析方法

    死锁问题,关键点是当时每个CPU的堆栈是什么。 通过“bt -a”命令可以打印出系统当时所有CPU运行的堆栈,通过解析锁的数据结构,可以分析出哪个线程持有锁。

  2. 常见死锁类型:
    • spinlock保护区中执行了非原子性的流程,如sleep schedule流程。
    • spinlock保护区中执行逻辑消耗的时间太长。
    • AB-BA死锁。
    • AA死锁,重复上锁。
    • 环形锁,在某些复杂架构设计中,模块间的等待可能出现环形,这会造成死锁。
    • 上锁和解锁不对称,锁指针运行中会被修改。
翻译
收藏
下载文档
更新时间:2021-08-11
文档编号:EDOC1000099509
浏览量:287130
下载量:4181
平均得分:3.47