免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4227 | 回复: 14
打印 上一主题 下一主题

[网络子系统] 求助:nf_conntrack_netlink模块OOPS重启,帮忙分析一下出现问题的原因 [复制链接]

论坛徽章:
1
金牛座
日期:2014-03-20 09:21:07
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-03-30 16:22 |只看该作者 |倒序浏览
本帖最后由 ren0221 于 2014-03-30 16:23 编辑

我使用的是Linux-2.6.35版内核,运行时产生以下oops信息:
  1. BUG: unable to handle kernel NULL pointer dereference at 00000022
  2. IP: [<bfb2a453>] ctnetlink_dump_tuples+0xb/0x120 [nf_conntrack_netlink]
  3. *pdpt = 000000003baa5001 *pde = 0000000000000000
  4. Oops: 0000 [#1] SMP

  5. Pid: 6401, comm: conntrack Tainted: P            2.6.35.6 #15 Luna Pier CRB/Luna Pier CRB
  6. EIP: 0060:[<bfb2a453>] EFLAGS: 00210216 CPU: 0
  7. EIP is at ctnetlink_dump_tuples+0xb/0x120 [nf_conntrack_netlink]
  8. EAX: 80112300 EBX: 80112300 ECX: 00000000 EDX: 00000010
  9. ESI: 00000010 EDI: 7bf47000 EBP: be73cab4 ESP: 7bd4fd24
  10. DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
  11. Process conntrack (pid: 6401, ti=7bd4e000 task=be002940 task.ti=7bd4e000)
  12. Stack:
  13. 00000000 000003cc 7bf47000 80112300 be73c9a8 7bf47000 be73cab4 bfb2bbd1
  14. <0> 00000000 ffffffff be73ca6c bfbf1860 01000000 7bf47008 85f76d40 7bf47000
  15. <0> 00000002 bfb2bc89 7bf47000 02000000 7d6b0360 80112300 86367c00 80112300
  16. <0>Call Trace:
  17. [<bfb2bbd1>] ? ctnetlink_fill_info.clone.37+0x405/0x422 [nf_conntrack_netlink]
  18. [<bfb2bc89>] ? ctnetlink_dump_table+0x9b/0x13f [nf_conntrack_netlink]
  19. [<41244bca>] ? netlink_dump+0x47/0x18b
  20. [<41244fa1>] ? netlink_recvmsg+0x196/0x26d
  21. [<41075c31>] ? __slab_alloc+0x25e/0x4cf
  22. [<412223bb>] ? sock_recvmsg+0xb1/0xd3
  23. [<bfacd2f7>] ? e1000_alloc_rx_buffers+0x117/0x21e [e1000e]
  24. [<410675f7>] ? __do_fault+0x364/0x44a
  25. [<412237b0>] ? sys_recvfrom+0xb8/0x122
  26. [<bface4cc>] ? e1000_poll+0x403/0x4b6 [e1000e]
  27. [<41016609>] ? do_page_fault+0x0/0x347
  28. [<412314db>] ? net_rx_action+0x6d/0xea
  29. [<4104e317>] ? move_native_irq+0x9/0x42
  30. [<41223ffd>] ? sys_socketcall+0x170/0x28b
  31. [<41002990>] ? sysenter_do_call+0x12/0x26
  32. Code: c0 74 0a f0 ff 08 0f 94 c2 84 d2 75 03 31 c0 c3 90 8d b4 26 00 00 00 00 e8 4f db 71 81 31 c0 c3 55 57 56 53 83 ec 0c 89 c3 89 d6 <0f> b7 42 12 66 83 f8 25 0f 87 ef 00 00 00 0f b7 c0 01 c0 8b 84
  33. EIP: [<bfb2a453>] ctnetlink_dump_tuples+0xb/0x120 [nf_conntrack_netlink] SS:ESP 0068:7bd4fd24
  34. CR2: 0000000000000022
复制代码
根据OOPS信息,出错位置的函数内容如下:
  1. static int
  2. ctnetlink_dump_tuples(struct sk_buff *skb,
  3.                       const struct nf_conntrack_tuple *tuple)
  4. {
  5.         int ret;
  6.         struct nf_conntrack_l3proto *l3proto;
  7.         struct nf_conntrack_l4proto *l4proto;

  8.         l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
  9.         ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);

  10.         if (unlikely(ret < 0))
  11.                 return ret;

  12.         l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
  13.         ret = ctnetlink_dump_tuples_proto(skb, tuple, l4proto);

  14.         return ret;
  15. }
复制代码
将nf_conntrack_netlink模块反汇编之后出错函数的内容如下:
  1. ctnetlink_dump_tuples(struct sk_buff *skb,
  2.                       const struct nf_conntrack_tuple *tuple)
  3. {
  4.      448:        55                           push   %ebp
  5.      449:        57                           push   %edi
  6.      44a:        56                           push   %esi
  7.      44b:        53                           push   %ebx
  8.      44c:        83 ec 0c                     sub    $0xc,%esp
  9.      44f:        89 c3                        mov    %eax,%ebx
  10.      451:        89 d6                        mov    %edx,%esi
  11.        
  12.         l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
  13.      453:        0f b7 42 12                  movzwl 0x12(%edx),%eax
  14. extern struct nf_conntrack_l3proto nf_conntrack_l3proto_generic;

  15. static inline struct nf_conntrack_l3proto *
  16. __nf_ct_l3proto_find(u_int16_t l3proto)
  17. {
  18.         if (unlikely(l3proto >= AF_MAX))
  19.      457:        66 83 f8 25                  cmp    $0x25,%ax
  20.      45b:        0f 87 ef 00 00 00            ja     550 <ctnetlink_dump_tuples+0x108>
  21.                 return &nf_conntrack_l3proto_generic;
  22.         return rcu_dereference(nf_ct_l3protos[l3proto]);
  23.      461:        0f b7 c0                     movzwl %ax,%eax
  24.      464:        01 c0                        add    %eax,%eax
  25.      466:        8b 84 00 00 00 00 00         mov    0x0(%eax,%eax,1),%eax
  26.      46d:        89 44 24 04                  mov    %eax,0x4(%esp)
  27.         nfnetlink_subsys_unregister(&ctnl_exp_subsys);
  28.         nfnetlink_subsys_unregister(&ctnl_subsys);
  29. }
复制代码
麻烦大家帮忙分析以下出现此问题的原因

论坛徽章:
17
水瓶座
日期:2013-08-29 12:09:27白羊座
日期:2014-08-07 12:36:42丑牛
日期:2014-07-24 12:44:41寅虎
日期:2014-04-16 16:15:33寅虎
日期:2014-03-12 09:28:43摩羯座
日期:2014-03-06 13:22:04技术图书徽章
日期:2014-03-06 11:34:50天蝎座
日期:2014-01-09 11:31:44寅虎
日期:2013-12-27 17:01:44双子座
日期:2013-12-27 12:32:29双子座
日期:2013-12-25 09:03:33丑牛
日期:2013-12-24 16:18:44
2 [报告]
发表于 2014-03-31 11:47 |只看该作者
回复 1# ren0221

对 nf_conntract_netlink不熟,不过从汇编分析出来是你传给 ctnetlink_dump_tuples()的 tuple指针为空指针。准确的说是 tuple 为 0x10。

继续追踪得出: 传入 ctnetlink_fill_info()中的  struct nf_conn *ct为空指针。所以请检查 ctnetlink_dump_table()中获取 ct部分!

因为不是熟悉的模块,只能追踪到这里了:)

论坛徽章:
1
金牛座
日期:2014-03-20 09:21:07
3 [报告]
发表于 2014-03-31 15:10 |只看该作者
回复 2# asuka2001

非常感谢,我跟踪出的结果也是ct为空,但是我又有一些疑问
①当在访问哈希表之前,加了spin_lock_bh(&nf_conntrack_lock)锁之后,在没解锁之前,哈希表中的连接会因为超时被释放吗?
ctnetlink_dump_table代码如下:

  1. static int
  2. ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
  3. {
  4.         struct net *net = sock_net(skb->sk);
  5.         struct nf_conn *ct, *last;
  6.         struct nf_conntrack_tuple_hash *h;
  7.         struct hlist_nulls_node *n;
  8.         struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
  9.         u_int8_t l3proto = nfmsg->nfgen_family;

  10.         spin_lock_bh(&nf_conntrack_lock);
  11.         last = (struct nf_conn *)cb->args[1];
  12.         for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) {
  13. restart:
  14.                 hlist_nulls_for_each_entry(h, n, &net->ct.hash[cb->args[0]],
  15.                                          hnnode) {
  16.                         if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
  17.                                 continue;
  18.                         ct = nf_ct_tuplehash_to_ctrack(h);
  19.                         /* Dump entries of a given L3 protocol number.
  20.                          * If it is not specified, ie. l3proto == 0,
  21.                          * then dump everything. */
  22.                         if (l3proto && nf_ct_l3num(ct) != l3proto)
  23.                                 continue;
  24.                         if (cb->args[1]) {
  25.                                 if (ct != last)
  26.                                         continue;
  27.                                 cb->args[1] = 0;
  28.                         }
  29.                         if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
  30.                                                 cb->nlh->nlmsg_seq,
  31.                                                 IPCTNL_MSG_CT_NEW, ct) < 0) {
  32.                                 nf_conntrack_get(&ct->ct_general);
  33.                                 cb->args[1] = (unsigned long)ct;
  34.                                 goto out;
  35.                         }

  36.                         if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) ==
  37.                                                 IPCTNL_MSG_CT_GET_CTRZERO) {
  38.                                 struct nf_conn_counter *acct;

  39.                                 acct = nf_conn_acct_find(ct);
  40.                                 if (acct)
  41.                                         memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]));
  42.                         }
  43.                 }
  44.                 if (cb->args[1]) {
  45.                         cb->args[1] = 0;
  46.                         goto restart;
  47.                 }
  48.         }
  49. out:
  50.         spin_unlock_bh(&nf_conntrack_lock);
  51.         if (last)
  52.                 nf_ct_put(last);

  53.         return skb->len;
  54. }
复制代码
②假设传入的ct是空指针的话,在ctnetlink_fill_info()函数中,在进入ctnetlink_dump_tuples()函数之前,有一次访问ct内容的操作,
nfmsg->nfgen_family = nf_ct_l3num(ct),如果ct是NULL的话,在这里不就应该报错了吗?
  1. static int
  2. ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
  3.                     int event, struct nf_conn *ct)
  4. {
  5.         struct nlmsghdr *nlh;
  6.         struct nfgenmsg *nfmsg;
  7.         struct nlattr *nest_parms;
  8.         unsigned int flags = pid ? NLM_F_MULTI : 0;

  9.         event |= NFNL_SUBSYS_CTNETLINK << 8;
  10.         nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags);
  11.         if (nlh == NULL)
  12.                 goto nlmsg_failure;

  13.         nfmsg = nlmsg_data(nlh);
  14.         nfmsg->nfgen_family = nf_ct_l3num(ct);
  15.         nfmsg->version      = NFNETLINK_V0;
  16.         nfmsg->res_id            = 0;
  17. ......
  18. }

复制代码
nf_ct_l3num()函数代码:
  1. static inline u_int16_t nf_ct_l3num(const struct nf_conn *ct)
  2. {
  3.         return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
  4. }
复制代码

论坛徽章:
17
水瓶座
日期:2013-08-29 12:09:27白羊座
日期:2014-08-07 12:36:42丑牛
日期:2014-07-24 12:44:41寅虎
日期:2014-04-16 16:15:33寅虎
日期:2014-03-12 09:28:43摩羯座
日期:2014-03-06 13:22:04技术图书徽章
日期:2014-03-06 11:34:50天蝎座
日期:2014-01-09 11:31:44寅虎
日期:2013-12-27 17:01:44双子座
日期:2013-12-27 12:32:29双子座
日期:2013-12-25 09:03:33丑牛
日期:2013-12-24 16:18:44
4 [报告]
发表于 2014-03-31 17:03 |只看该作者
回复 3# ren0221

1). 对nf_conntrack不熟悉,个人认为那个锁应该是保护对表的并发访问而不是对表上挂载的对象的并发访问!所以表上挂载的对象其内容可以变动!
     但是要说对象释放应该不会,没从表中删除前对象至少应该有一个refcount! 不然就是 BUG了。

2). 这个的确费解了,你能把反汇编代码发来看看么!编译器重拍指令?

论坛徽章:
1
金牛座
日期:2014-03-20 09:21:07
5 [报告]
发表于 2014-03-31 17:52 |只看该作者
回复 4# asuka2001
这个nf_conntrack_netlink模块的汇编代码比较长,贴不上来,我直接附件上来了,麻烦看一下呗,我对汇编不是很熟悉,麻烦您了


nf_conntrack_netlink.rar (40.12 KB, 下载次数: 15)


   

论坛徽章:
17
水瓶座
日期:2013-08-29 12:09:27白羊座
日期:2014-08-07 12:36:42丑牛
日期:2014-07-24 12:44:41寅虎
日期:2014-04-16 16:15:33寅虎
日期:2014-03-12 09:28:43摩羯座
日期:2014-03-06 13:22:04技术图书徽章
日期:2014-03-06 11:34:50天蝎座
日期:2014-01-09 11:31:44寅虎
日期:2013-12-27 17:01:44双子座
日期:2013-12-27 12:32:29双子座
日期:2013-12-25 09:03:33丑牛
日期:2013-12-24 16:18:44
6 [报告]
发表于 2014-03-31 19:39 |只看该作者
回复 5# ren0221

看了下汇编,的确是编译器优化的厉害,让函数 ctnetlink_dump_table()在 nf_ct_l3num(ct)之前调用了!

论坛徽章:
1
金牛座
日期:2014-03-20 09:21:07
7 [报告]
发表于 2014-03-31 20:14 |只看该作者
回复 6# asuka2001

麻烦指导一下怎么能看出来呢?我在反汇编代码中从ctnetlink_fill_info()函数向后看,看到第一个nf_ct_l3num(ct)是在4427行,第一个ctnetlink_dump_tuples函数是在4463行

  1.         nfmsg = nlmsg_data(nlh);
  2.         nfmsg->nfgen_family = nf_ct_l3num(ct);
  3.     184f:        0f b7 57 22                  movzwl 0x22(%edi),%edx
  4.     1853:        88 56 10                     mov    %dl,0x10(%esi)
  5.         nfmsg->version      = NFNETLINK_V0;
  6.     1856:        c6 40 01 00                  movb   $0x0,0x1(%eax)
  7.         nfmsg->res_id            = 0;
  8.     185a:        66 c7 40 02 00 00            movw   $0x0,0x2(%eax)
  9.         nfnetlink_subsys_unregister(&ctnl_exp_subsys);
  10.         nfnetlink_subsys_unregister(&ctnl_subsys);
  11. }

  12. module_init(ctnetlink_init);
  13. module_exit(ctnetlink_exit);
  14.     1860:        8b ab a8 00 00 00            mov    0xa8(%ebx),%ebp
  15. */
  16. static inline struct nlattr *nla_nest_start(struct sk_buff *skb, int attrtype)
  17. {
  18.         struct nlattr *start = (struct nlattr *)skb_tail_pointer(skb);

  19.         if (nla_put(skb, attrtype, 0, NULL) < 0)
  20.     1866:        c7 04 24 00 00 00 00         movl   $0x0,(%esp)
  21.     186d:        31 c9                        xor    %ecx,%ecx
  22.     186f:        ba 01 80 00 00               mov    $0x8001,%edx
  23.     1874:        89 d8                        mov    %ebx,%eax
  24.     1876:        e8 fc ff ff ff               call   1877 <ctnetlink_fill_info.clone.37+0xab>
  25.     187b:        85 c0                        test   %eax,%eax
  26.     187d:        0f 88 72 02 00 00            js     1af5 <ctnetlink_fill_info.clone.37+0x329>
  27.         nfmsg->nfgen_family = nf_ct_l3num(ct);
  28.         nfmsg->version      = NFNETLINK_V0;
  29.         nfmsg->res_id            = 0;

  30.         nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED);
  31.         if (!nest_parms)
  32.     1883:        85 ed                        test   %ebp,%ebp
  33.     1885:        0f 84 6a 02 00 00            je     1af5 <ctnetlink_fill_info.clone.37+0x329>
  34.                 goto nla_put_failure;
  35.         if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
  36.     188b:        8d 57 10                     lea    0x10(%edi),%edx
  37.     188e:        89 d8                        mov    %ebx,%eax
  38.     1890:        e8 b3 eb ff ff               call   448 <ctnetlink_dump_tuples>
  39.     1895:        85 c0                        test   %eax,%eax
  40.     1897:        0f 88 58 02 00 00            js     1af5 <ctnetlink_fill_info.clone.37+0x329>
  41. *
复制代码

论坛徽章:
17
水瓶座
日期:2013-08-29 12:09:27白羊座
日期:2014-08-07 12:36:42丑牛
日期:2014-07-24 12:44:41寅虎
日期:2014-04-16 16:15:33寅虎
日期:2014-03-12 09:28:43摩羯座
日期:2014-03-06 13:22:04技术图书徽章
日期:2014-03-06 11:34:50天蝎座
日期:2014-01-09 11:31:44寅虎
日期:2013-12-27 17:01:44双子座
日期:2013-12-27 12:32:29双子座
日期:2013-12-25 09:03:33丑牛
日期:2013-12-24 16:18:44
8 [报告]
发表于 2014-04-01 22:13 |只看该作者
回复 7# ren0221

看来是我看错了,估计是看汇编看串行了。。。奇怪,记得当时我还检查了下的!

%edi里一直保存的是 ct,没变啊。。。这里也对的上,ct与 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num的偏移整好是 0x22
        nfmsg->nfgen_family = nf_ct_l3num(ct);
    184f:       0f b7 57 22             movzwl 0x22(%edi),%edx

给ctnetlink_dump_tuples()传的第2个参数也符合 %edx为 0x10的值,后来 page fault时解引用地址为 0x22也正好说明 ct为空指针!
    188b:       8d 57 10                lea    0x10(%edi),%edx
    188e:       89 d8                   mov    %ebx,%eax
    1890:       e8 b3 eb ff ff          call   448 <ctnetlink_dump_tuples>

这是寄存器的说。。。又不是内存上的值还能有其他模块踩到啥的。。。外面也有 spin_lock_bh,不存在抢占调度。x86也不存在 cpu写乱序,我只能说这是大宇宙意志的力量?:)

你可以确认下 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num的 offset是不是 0x22,ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple的 offset是不是 0x10。应该不是我汇编解析有问题吧?

论坛徽章:
3
射手座
日期:2014-08-18 12:15:53戌狗
日期:2014-08-22 09:53:36寅虎
日期:2014-08-22 14:15:29
9 [报告]
发表于 2014-04-02 10:28 |只看该作者
我感觉这个调用堆栈可能有问题,查了下内核代码, 调用堆栈前面带问号表明这个函数地址是不可靠的。

论坛徽章:
1
金牛座
日期:2014-03-20 09:21:07
10 [报告]
发表于 2014-04-03 10:03 |只看该作者
回复 8# asuka2001
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num的 offset是 0x22,ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple的 offset是 0x10,
我在ctnetlink_fill_info()函数调用ctnetlink_dump_tuples()函数前判断ct是否等于NULL:
  1. if (ct == NULL) {
  2.         printk(">>>>[BUG]:[FUNC]:%s,[LINE]:%d,ct==NULL<<<<\n", __FUNCTION__, __LINE__);
  3.         goto nla_put_failure;
  4. }
  5. if(ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
  6.         goto nla_put_failure;
复制代码
没有在这里退出,然后我在ctnetlink_dump_tuples()函数里面添加打印信息:
  1. if ((unsigned long)tuple == 0x00000010) {
  2.         printk("[BUG]:[FUNC]:%s,[LINE]:%d,invalid tuple\n", __FUNCTION__, __LINE__);
  3.         return -1;
  4. }
  5. l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
  6. ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);
复制代码
这样不会重启,但是会打印“[BUG]:[FUNC]:ctnetlink_dump_tuples,[LINE]:115,invalid tuple“
,而tuple在ct中的offset确实是0x10, 所以可以推断ct的起始地址为0x00000000

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP