zoukankan      html  css  js  c++  java
  • segfault at 7fff6d99febc ip 0000003688644323 sp 00007fff6d99fd30 error 7 in libc.so.6[3688600000+175000]

    简述:

      进程信号11段错误,出core。内核dmesg打印:*****(程序名字)[31255]: segfault at 7fff6d99febc ip 0000003688644323 sp 00007fff6d99fd30 error 7 in libc.so.6[3688600000+175000]

    直接原因:

      sp指针向下增长时,越界,访问到mmap以只读映射的地址空间,导致写操作异常。do_page_fault失败,给进程发了信号11。

    环境:

      root ~ # uname -a
      Linux localhost 2.6.30-gentoo-r8 #47 SMP Mon Sep 21 03:44:07 EDT 2015 x86_64 GNU/Linux

    根本原因:

      进程地址空间大致如下分布如下(我的环境是这个分布):

      (图片摘自《深入Linux内核架构》)

      与问题相关的关键点

      ①stack向下增长,所以系统必须给stack留下足够的地址空间。位于stack下面的是受限于mm->mmap_base的mmap地址空间。

      ②mm->mmap_base表示用于mmap的地址空间的最大值,查找方向向下,所有的mmap系统调用分配出来的地址空间位于mm->mmap_base之下。

      综合①和②,可以得到结论mm->mmap_base必须小于并远离Stack。但是2.6内核这里存在bug,不能一定满足该条件。

      mmap_base赋值分析:

      mm->mmap_base赋值在load_elf_binary()->arch_pick_mmap_layout()->mmap_base()中:

    static unsigned long mmap_base(void)
    {
    	unsigned long gap = current->signal->rlim[RLIMIT_STACK].rlim_cur;//改值为栈大小限制,系统可配,默认1M。问题环境改值为8M
    
    	if (gap < MIN_GAP)
    		gap = MIN_GAP;   //走这个逻辑,MIN_GAP值为128M,gap值更新为128M。
    	else if (gap > MAX_GAP)
    		gap = MAX_GAP;
    	//7FFFFFFFF000(TASK_SIZE) - 8000000(gap)
    	//[0,FFFFFFF000], mmap_rnd()的取值范围区间
    	//[7EFFF8000000,7FFFF7FFF000],返回的mmap_base的地址的取值范围
    	return PAGE_ALIGN(TASK_SIZE - gap - mmap_rnd());
    }
    

      从代码中看,mmap_base赋值中只考虑了当前栈大小,但并不能保证远离stack。

      极端情况下,mmap_rnd随机值为0时,mmap_base被赋值为TASK_SIZE-128M的位置,即:7FFFF7FFF000

      栈底赋值分析:

      Stack的栈底为:STACK_TOP - random_variable,经计算取值范围为:[7FFC00000000,7FFFFFFFF000]

    #define STACK_TOP TASK_SIZE
    
    retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
    				 executable_stack);
    
    static unsigned long randomize_stack_top(unsigned long stack_top)
    {
    	unsigned int random_variable = 0;
    
    	if ((current->flags & PF_RANDOMIZE) &&
    		!(current->personality & ADDR_NO_RANDOMIZE)) {
    		random_variable = get_random_int() & STACK_RND_MASK; //[0, 3fffff000]
    		random_variable <<= PAGE_SHIFT;
    	}
    #ifdef CONFIG_STACK_GROWSUP
    	return PAGE_ALIGN(stack_top) + random_variable;
    #else
    	return PAGE_ALIGN(stack_top) - random_variable; //[7FFC00000000,7FFFFFFFF000]
    #endif
    }
    

      

      综上,这里确认2.6的内核存在bug。改动方法是,给mmap_base至少再减去 STACK_RND_MASK。

      该问题内核最早于2009年修复:

      https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/arch/x86/mm/mmap.c?id=80938332d8cf652f6b16e0788cf0ca136befe0b5

      新版本的内核实现:

      https://code.woboq.org/linux/linux/arch/x86/mm/mmap.c.html#mmap_base

    问题排查过程:

      ①充分分析core文件,确认栈的增长是正常的。导致异常的地址,也确实是正常的。函数调用时,参数正常压栈的地址。

      ②怀疑内核问题(缺页中断失败?)。找到全部打印 segfault at 的内核代码,逐个分析,对可能的路径添加调试。确认错误原因在,do_page_fault的1130行,判断vm是否可写时失败。

      ③正常来说,这个vm是stack的vm_area_struct结构体,一定是有写权限的。怀疑vm的问题,增加调试,在出问题时,打印vm_area_struct的信息。根据vma中的file指针找到被mmap的文件的名字(我的环境是个字符设备)。

      至此确认问题原因:栈的增长踩到了字符设备的mmap地址空间,该mmap是只读。调试代码如下:

    /*
     * This routine handles page faults.  It determines the address,
     * and the problem, and then passes it off to one of the appropriate
     * routines.
     */
    dotraplinkage void __kprobes
    do_page_fault(struct pt_regs *regs, unsigned long error_code)
    {
        struct vm_area_struct *vma;
        struct task_struct *tsk;
        unsigned long address;
        struct mm_struct *mm;
        int write;
        int fault;
    
        tsk = current;
        mm = tsk->mm;
    
        prefetchw(&mm->mmap_sem);
    
        /* Get the faulting address: */
        address = read_cr2();
    ...
        if (unlikely(expand_stack(vma, address))) {
            bad_area(regs, error_code, address);
            return;
        }
    
        /*
         * Ok, we have a good vm_area for this memory access, so
         * we can handle it..
         */
    good_area:
        write = error_code & PF_WRITE;
    
        if (unlikely(access_error(error_code, write, vma))) {
            printk("svking vm_next info :start:%lx  end:%lx  flags:%lx
    ", vma->vm_next->vm_start, 
                vma->vm_next->vm_end, vma->vm_next->vm_flags);
            printk("svking vm info :start:%lx  end:%lx  flags:%lx
    ", vma->vm_start, vma->vm_end, vma->vm_flags);
            printk("svking vm info :vm_file: %p
    ", vma->vm_file);
            if (vma->vm_file)
            {
                printk("svking file name :%s, %s
    ", vma->vm_file->f_dentry->d_iname,
                    vma->vm_file->f_dentry->d_name.name);
            }
            bad_area_access_error(regs, error_code, address);
            return;
        }
    
        /*
         * If for any reason at all we couldn't handle the fault,
         * make sure we exit gracefully rather than endlessly redo
         * the fault:
         */
        fault = handle_mm_fault(mm, vma, address, write);
    ...
    }

      ④分析mmap机制,确认mmap的返回的地址空间受控于进程的mm->mmap_base。同时mm->mmap_base是在进程启动时执行exec函数时确定的。所以怀疑mm->mmap_base赋值时存在问题。

      ⑤分析进程地址空间和mmap_base赋值函数,确认根本原因: “mm->mmap_base必须小于并远离Stack” 未能满足。

    附:

    调试日志(***屏蔽了业务关键字):

    root ~ # tail -f /var/log/messages | grep -E "svking|segfault"
    Jul 21 16:44:16 localhost kernel: [ 5489.150802] svking vm_next info :start:7fff13d58000 end:7fff13d6d000 flags:100177
    Jul 21 16:44:16 localhost kernel: [ 5489.150990] svking vm info :start:7ffdcb558000 end:7fff13d58000 flags:400844fd
    Jul 21 16:44:16 localhost kernel: [ 5489.151155] svking vm info :vm_file: ffff880082dbbcc0
    Jul 21 16:44:16 localhost kernel: [ 5489.151260] svking file name :******, ******
    Jul 21 16:44:16 localhost kernel: [ 5489.151388] ******[22867]: segfault at 7fff13d57ffc ip 00007fff6469ae99 sp 00007fff13d57ff0 error 7 in lib****.so[7fff64681000+2a000]
    Jul 21 16:48:42 localhost kernel: [ 5754.180545] svking vm_next info :start:7fffb5202000 end:7fffb5217000 flags:100177
    Jul 21 16:48:42 localhost kernel: [ 5754.180772] svking vm info :start:7ffe6ca02000 end:7fffb5202000 flags:400844fd
    Jul 21 16:48:42 localhost kernel: [ 5754.180971] svking vm info :vm_file: ffff880082f74240
    Jul 21 16:48:42 localhost kernel: [ 5754.181136] svking file name :******, ******
    Jul 21 16:48:42 localhost kernel: [ 5754.181277] ******[32049]: segfault at 7fffb5201ff8 ip 00007ffff5b744ae sp 00007fffb5202000 error 7

    ps:个人笔记,如有错误,期待指出,不喜勿喷。

  • 相关阅读:
    redis性能优化——生产中实际遇到的问题排查总结
    Redis优化经验
    Python 发送 email 的两种方式
    封装简单的equery
    Mobiscroll的介绍【一款兼容PC和移动设备的滑动插件】
    css3毛玻璃模糊效果
    环形进度条的实现方法总结和动态时钟绘制(CSS3、SVG、Canvas)
    CSS实现圆角,三角,五角星,五边形,爱心,12角星,8角星,圆,椭圆,圆圈,八卦
    盘点8种CSS实现垂直居中水平居中的绝对定位居中技术
    CSS 去掉inline-block间隙的几种方法
  • 原文地址:https://www.cnblogs.com/svking/p/9429615.html
Copyright © 2011-2022 走看看