linux kernel crash问题分析解决

简介:

一,问题场景和环境

系统环境:

redhat6.4 kernel2.6.32-358


问题:

使用iptablesmangle表添加了一条规则,使用nfqueue做为target。当一个http请求命中这个规则之后,机器直接重启了。偶发性的出了两次问题,但是却在重启的机器上重现不了这个问题。


二,排查

1,查看messageskerneldmesg相关日志,未发现有任何异常

2,查看重启前机器的负载,cpu,内存,磁盘io,网络io都正常

3,由于是使用了nfqueue做为target才导致的重启,怀疑是系统的问题,通过现象看应该是iptablesnfqueue导致的问题,而nfqueue用于从内核读取数据包在用户态处理。故具体定位在kernel或者libnetfilter_queue上。

4,通过服务器显示屏幕来看重启的时候会有什么有用的输出,但是服务器在客户的机房,查看太麻烦

5,使用last查看服务器的重启记录,发现一个意外现象,即:机器因为nfqueue重启的那个记录里面有一个crash记录,意思即系统奔溃了,从而导致重启。那就能断定是系统或者kernel crash了。

6linux系统一般默认都安装配置了kdump,故当 linux 系统内核发生崩溃的时候,可以通过 kdump 等方式收集内核崩溃之前的内存,在/var/crash/日期 目录生成一个转储文件 vmcore。使用crash工具可以分享vmcore文件,来获取kernel crash前的一些重要信息。通过在机器上查找,果然发现了crash相关的vmcore文件。


三,分析vmcore文件

1,安装指定kerneldebuginfo包:
# yum install kernel-debuginfo-2.6.32-358.el6.x86_64


2,使用系统自带的crash命令分析vmcore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# crash /usr/lib/debug/lib/modules/2.6.32-358.el6.x86_64/vmlinux vmcore
crash 7.1.0-6.el6
Copyright (C) 2002-2014  Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010  IBM Corporation
Copyright (C) 1999-2006  Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012  Fujitsu Limited
Copyright (C) 2006, 2007  VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011  NEC Corporation
Copyright (C) 1999, 2002, 2007  Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002  Mission Critical Linux, Inc.
This program is  free  software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions.  Enter  "help copying"  to see the conditions.
This program has absolutely no warranty.  Enter  "help warranty"  for  details.
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http: //gnu.org/licenses/gpl.html>
This is  free  software: you are  free  to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type  "show copying"
and  "show warranty"  for  details.
This GDB was configured as  "x86_64-unknown-linux-gnu" ...
WARNING: kernel version inconsistency between vmlinux and dumpfile
       KERNEL: vmlinux
     DUMPFILE: vmcore  [PARTIAL DUMP]
         CPUS: 40
         DATE: Tue Oct 31 11:53:41 2017
       UPTIME: 342 days, 12:15:26
LOAD AVERAGE: 0.00, 0.02, 0.00
        TASKS: 1050
     NODENAME: web_yp_49_202.mobileztgame
      RELEASE: 2.6.32-358.el6.x86_64
      VERSION: #1 SMP Tue Jan 29 11:47:41 EST 2013
      MACHINE: x86_64  (2499 Mhz)
       MEMORY: 128 GB
        PANIC:  "BUG: unable to handle kernel NULL pointer dereference at (null)"
          PID: 0
      COMMAND:  "swapper"
         TASK: ffff882069324080  (1 of 40)  [THREAD_INFO: ffff881068896000]
          CPU: 5
        STATE: TASK_RUNNING (PANIC)


crash的输出可以看到kernel崩溃的原因为kernel遇见空指针导致崩溃



bt 命令用于查看系统崩溃前的堆栈等信息

bt命令结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
crash> bt
PID: 0      TASK: ffff882069324080  CPU: 5   COMMAND:  "swapper"
  #0 [ffff8800618a3750] machine_kexec at ffffffff81035b7b
  #1 [ffff8800618a37b0] crash_kexec at ffffffff810c0db2
  #2 [ffff8800618a3880] oops_end at ffffffff815111d0
  #3 [ffff8800618a38b0] no_context at ffffffff81046bfb
  #4 [ffff8800618a3900] __bad_area_nosemaphore at ffffffff81046e85
  #5 [ffff8800618a3950] bad_area_nosemaphore at ffffffff81046f53
  #6 [ffff8800618a3960] __do_page_fault at ffffffff810476b1
  #7 [ffff8800618a3a80] do_page_fault at ffffffff8151311e
  #8 [ffff8800618a3ab0] page_fault at ffffffff815104d5
     [exception RIP: nf_queue+152]
     RIP: ffffffff81475718  RSP: ffff8800618a3b60  RFLAGS: 00010207
     RAX: 0000000000000020  RBX: 0000000000000000  RCX: ffff8810638a3c00
     RDX: 0000000000000002  RSI: ffff880959189980  RDI: 0000000000000000
     RBP: ffff8800618a3bd0   R8: 0000000000021773   R9: 0000000000000001
     R10: 000000000000000e  R11: 0000000000000006  R12: ffff880959189980
     R13: 0000000000000000  R14: ffffffff8147e8b0  R15: 0000000000000000
     ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
  #9 [ffff8800618a3bd8] nf_hook_slow at ffffffff81474800
#10 [ffff8800618a3c58] ip_rcv at ffffffff8147ef54
#11 [ffff8800618a3c98] __netif_receive_skb at ffffffff8144819b
#12 [ffff8800618a3cf8] netif_receive_skb at ffffffff8144a578
#13 [ffff8800618a3d38] napi_skb_finish at ffffffff8144a680
#14 [ffff8800618a3d58] napi_gro_receive at ffffffff8144cc29
#15 [ffff8800618a3d78] ixgbe_poll at ffffffffa015e44c [ixgbe]
#16 [ffff8800618a3e68] net_rx_action at ffffffff8144cd43
#17 [ffff8800618a3ec8] __do_softirq at ffffffff81076fb1
#18 [ffff8800618a3f38] call_softirq at ffffffff8100c1cc
#19 [ffff8800618a3f50] do_softirq at ffffffff8100de05
#20 [ffff8800618a3f70] irq_exit at ffffffff81076d95
#21 [ffff8800618a3f80] do_IRQ at ffffffff81516c95
--- <IRQ stack> ---
#22 [ffff881068897db8] ret_from_intr at ffffffff8100b9d3
     [exception RIP: intel_idle+222]
     RIP: ffffffff812d37ae  RSP: ffff881068897e68  RFLAGS: 00000206
     RAX: 0000000000000000  RBX: ffff881068897ed8  RCX: 0000000000000000
     RDX: 00000000000e3cb1  RSI: 0000000000000000  RDI: 00000000379d13ba
     RBP: ffffffff8100b9ce   R8: 0000000000000004   R9: 0000000000000050
     R10: 0069229e5ea9dbfa  R11: 0000000000000000  R12: ffff8800618b15a0
     R13: 0000000000000000  R14: 0069229c2b297a40  R15: ffff8800618b16a0
     ORIG_RAX: ffffffffffffff62  CS: 0010  SS: 0018
#23 [ffff881068897ee0] cpuidle_idle_call at ffffffff81414ef7
#24 [ffff881068897f00] cpu_idle at ffffffff81009fc6



通过bt分析,我们从下到上来看kernel崩溃前的系统调用,定位到kernel崩溃前的一个exceptionip寄存器RIP的异常,而通过dis 命令来看一下该地址的反汇编结果:

1
2
3
crash> dis -l ffffffff81475718
/usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/net/netfilter/nf_queue.c: 221
0xffffffff81475718 <nf_queue+152>:      mov    (%rbx),%r12


故可定位到出现异常的代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# vim /usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/net/netfilter/nf_queue.c +221
215         segs = skb_gso_segment(skb, 0);
216         kfree_skb(skb);
217          if  (IS_ERR(segs))
218                  return  1;
219
220          do  {
221                  struct  sk_buff *nskb = segs->next;
222
223                 segs->next = NULL;
224                  if  (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn,
225                                 queuenum))
226                         kfree_skb(segs);
227                 segs = nskb;
228         }  while  (segs);
229          return  1;




而通过看skb_gso_segment结构体,可以判断出是因为skb_gso_segment在某些情况下会返回NULL,从而导致如上代码segs->next获取到了空指针,从而导致kernel崩溃。而既然是gso导致的问题,应该可以通过调整系统gso属性来规避这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# vim /usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/net/core/dev.c +1728
1728  /**
1729  *      skb_gso_segment - Perform segmentation on skb.
1730  *      @skb: buffer to segment
1731  *      @features: features for the output path (see dev->features)
1732  *
1733  *      This function segments the given skb and returns a list of segments.
1734  *
1735  *      It may return NULL if the skb requires no segmentation.  This is
1736  *      only possible when GSO is used for verifying header integrity.
1737  */
1738  struct  sk_buff *skb_gso_segment( struct  sk_buff *skb,  int  features)
1739 {
1740          struct  sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
1741          struct  packet_type *ptype;
1742         __be16 type = skb->protocol;
1743          int  err;


从网上找到的对应patch如下:

https://patchwork.kernel.org/patch/6615071/


四,问题重现

1,最早发现问题,想要重现的办法是通过如下url访问:curl “t.test.com”,发现重现不了。

2,之后,通过搜索相关TSO/GSO/LRO/GRO相关的资料,觉得有可能是由于发送的数据包太小,导致没有触发相关的数据包分段重组,从而没有导致重现问题。故增大了请求的数据包,通过如下url重现了问题:

# curl “t.test.com/v2/user-manage/css/bootstrap.min.css?test1=sdfsfsdfsdfa&test2_id=2234234234234234234&test_id=50129009890098&test_token=1670056402|_80_m_lxxj1298|1493196793|c726299f2d03b8462764bacf20e2395f|sdfsdfdsfsdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffsdfsdfsdfdsfsdfhgjgjghjghjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjfhjgjfghjfjfhjjjjjjjjjjjjjjjjjjjjjfffffadfsfsdfsdfsdfsdfsdfdsfdssdfsdfsdfsdfsdfsdf”

iptables相关规则如下:

1
2
3
# ipset create lee hash:ip hashsize 819200 maxelem 100000 timeout 300
# ipset add lee 1.1.1.1 timeout 300
# iptables -t mangle -I PREROUTING -p tcp -m multiport --dports 80,443 -m set --match-set lee src -m string --string t.test.com --algo kmp --from 0 --to 1480 -j NFQUEUE


五,问题结论

linux kernel bug


六,解决办法

1,升级kernel。从patch和源代码可以看出kernel 3.0以后应该fix了这个问题,看了下3.10kernel代码已经fix

2,使用drop,不再使用nfqueue这个target来添加iptables规则(建议使用这个办法)

3,调整网卡gso相关属性,发现通过关闭lro来解决这个重启问题。具体命令:

# ethtool -K eth0 lro on

LRO简介:

Linux  2.6.24 中加入了支持 IPv4 TCP 协议的 LRO (Large Receive Offload) ,它通过将多个 TCP 数据聚合在一个 skb 结构,在稍后的某个时刻作为一个大数据包交付给上层的网络协议栈,以减少上层协议栈处理 skb 的开销,提高系统接收 TCP 数据包的能力。当然,这一切都需要网卡驱动程序支持。


七,参考

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/kernel_crash_dump_guide/sect-crash-running-the-utility

https://patchwork.kernel.org/patch/6615071/

https://www.ibm.com/developerworks/cn/linux/l-cn-network-pt/index.html











本文转自 leejia1989 51CTO博客,原文链接:http://blog.51cto.com/leejia/1978729,如需转载请自行联系原作者
目录
相关文章
|
6月前
|
Linux 调度 Android开发
【系统启动】Kernel怎么跳转到Android:linux与安卓的交界
【系统启动】Kernel怎么跳转到Android:linux与安卓的交界
104 0
|
6月前
|
Linux C语言
Linux内核学习(七):linux kernel内核启动(一):概述篇
Linux内核学习(七):linux kernel内核启动(一):概述篇
112 0
|
安全 Ubuntu Linux
Linux Kernel 权限提升漏洞 (CVE-2023-32233)
Linux Netfilter 是一个在 Linux 内核中的网络数据包处理框架,也称作 iptables,它可以通过各种规则和过滤器,基于数据包的来源、目标地址、协议类型、端口号等信息,控制网络流量和数据包的转发和处理,是 Linux 系统网络安全性和可靠性的重要组成部分
252 1
Linux Kernel 权限提升漏洞 (CVE-2023-32233)
|
安全 Ubuntu Linux
Linux Kernel openvswitch模块权限提升漏洞(CVE-2022-2639)
Linux 内核模块Open vSwitch 存在越界写入漏洞,在足够多actions情况下,在为新的flow的新的action拷贝和预留内存时,如果next_offset比MAX_ACTIONS_BUFSIZE大,reserve_sfa_size并不会如期返回 -EMSGSIZE,此时会产生越界写入漏洞。攻击者可以利用该漏洞将普通权限提升至ROOT权限。
112 1
|
Linux
Linux中Too many open files 问题分析和解决
Linux中Too many open files 问题分析和解决
|
6月前
|
Linux 芯片
Linux内核学习(六):linux kernel的Kconfig分析
Linux内核学习(六):linux kernel的Kconfig分析
530 0
|
Ubuntu Linux 开发工具
嵌入式Linux系列第4篇:Kernel编译下载
嵌入式Linux系列第4篇:Kernel编译下载
|
2月前
|
存储 缓存 编译器
Linux kernel memory barriers 【ChatGPT】
Linux kernel memory barriers 【ChatGPT】
54 11
|
3月前
|
Linux 网络安全 开发工具
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
本文介绍了如何基于Linux Kernel 5.15.102版本和BusyBox创建一个自定义的迷你Linux ARM系统,并使用QEMU进行启动和调试,包括内核和BusyBox的编译配置、根文件系统的制作以及运行QEMU时的命令和参数设置。
261 0
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
|
5月前
|
运维 NoSQL Ubuntu
深入理解Linux中的"crash"命令:内核崩溃的调试利器
`crash`是Linux内核崩溃调试工具,用于分析内核崩溃转储文件,提供GDB-like的交互式CLI。通过加载`vmcore`文件和内核映像,管理员可以查看系统状态、调用栈、内存布局等。安装`crash`可使用包管理器,如`apt-get`或`yum/dnf`。尽管有学习曲线且依赖转储文件,但`crash`在系统故障排查中极其重要。