[crash分析]数组越界导致的导致的系统crash

公司设备在项目中遇到了crash问题,查看coredump,是最长前缀匹配的模块中处理异常导致。

现象1:查询异常

"general protection fault: 0000 [#1] SMP "

    [exception RIP: _test_ipset_seg_lpm_query+93]
    RIP: ffffffffa058ce9d  RSP: ffff88016951f578  RFLAGS: 00010207
    RAX: 80000010241c2865  RBX: ffff88102b96c008  RCX: 0000000000000008
    RDX: ffff88016951f6fc  RSI: 80000010241c2be9  RDI: 000000000000004b
    RBP: ffff88016951f598   R8: ffffffffa058ce40   R9: ffffffffa057fc7d
    R10: ffff88102d319a60  R11: ffffea004073fb80  R12: ffff88016951f6fc
    R13: 000000000a174bc0  R14: 0000000000000000  R15: ffff88102c2d2668
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
crash> dis _test_ipset_seg_lpm_query+93
0xffffffffa058ce9d <_test_ipset_seg_lpm_query+93>:      cmpb   $0x0,(%rsi) 

经分析是seg指针地址异常,seg->seg_array_next 操作导致系统crash。这是query查询操作,起初以为是并发处理保护没有做好,seg指针在其他地方被释放导致。
查阅代码后,发现add/del/query 有对应的锁保护,没有发现错误。

随后出现问题现象2:提示地址访问异常。

BUG: unable to handle kernel paging request at 0000000000497ff0
    [exception RIP: _test_ipset_seg_lpm_del_rule+175]
    RIP: ffffffffa053801f  RSP: ffff880feb6bbc08  RFLAGS: 00010206
    RAX: ffff88102800c008  RBX: ffff881026f09808  RCX: 0000000000000000
    RDX: 00000000000000c8  RSI: 000000000ac80100  RDI: ffff881026f0980c
    RBP: ffff880feb6bbc50   R8: 00000000000ac801   R9: 00000000004973f0
    R10: 000000001f308101  R11: ffffea00407cc200  R12: ffff881028bdb008
    R13: 000000000000001d  R14: ffff881027411008  R15: 000000000ac80100
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 
crash> dis _test_ipset_seg_lpm_del_rule+175
0xffffffffa053801f <_test_ipset_seg_lpm_del_rule+175>:   mov    0xc00(%r9),%rax

经分析是指针数组seg_array_next[200] = 中指针地址错误, 导致异常地址访问,系统crash。
这个是delete操作,怀疑方向转到了重复释放上,然而查阅代码,没找重复释放问题。

增加add/del相关log,并对内存的申请和释放增加了wrapper函数,打印对应地址。
查看相关log并没有发现问题地址的释放。

思路一度陷入僵局,随后增加了更多的log,crash后,查看log文件,发现一个诡异问题,在某个时刻,seg->seg_array_next指针发生了改变,并且只是第三个字节被置0。如

 //为了好理解 小端字节序 从小到大排列
 08 40 93 a8 00 88 ff ff 被改成
 08 40 00 a8 00 88 ff ff

这个很明显是地址被篡改,回过头看seg的格式,发现如果Ele数组越界,depth正好对应seg_array_next指针的第三个字节。于是推断是update Ele操作时,数组index为256,修改depth=0 导致的问题。

typedef struct _TEST_SET_LPM_DIR
{
    struct {
        u8  ref;
        u8  seg;
        u8  depth;	//注意这里
        u32 ipaddr;
        u32 data;
    }   Ele[256];
    TEST_SET_LPM_DIR   **seg_array_next;
} TEST_SET_LPM_DIR; 

有了方向后,增加对应的log,重现问题。发现确实存在index=256的情况。仔细审视代码发现是一个手误,本来应该是index= var + i;结果写成了 index = var + 1;在代码中存在var=255的可能,于是导致了数组越界。

[  324.149807] [_test_ipset_seg_lpm_create:199] Seg:ffff880035820008 Seg->seg_array_next:ffff880035826008
...//省略无关
[  325.668351] [_test_ipset_seg_lpm_update:484] Seg:ffff880035820008 var:255 Seg->seg_array_next:ffff880035006008

修复后,验证通过,问题解决。

但这个问题确实比较隐蔽,只是修改了对应地址的一个字节,导致初期没有怀疑到数组越界,浪费了不少时间。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮沉飘摇

码字不易,打赏随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值