访问链表list释放后内存(野指针):两次coredump排除经历(一)重复添加节点

问题

最近维护一套祖传代码,测试时,在遍历hash表时,概率产生了coredump。 直接原因是访问hash冲突链上的node是个非法指针。很自然的想法,是不是删除节点的时候遗漏摘除链表,导致是野指针残留在hash表上?请 2分钟 想一想还有哪些可能。

先来回顾下hash表的基础知识。

1、hash表简述

链表的结构

hash表实现千差万别,常见的是基于数组拉链法。如下图,hash桶是个数组,冲突链用双向链表实现。

双向链表

在链表中,每一个结点的结构都包括了两部分的内容:数据域和指针域

arduino 复制代码
/* Doubly linked list head or element. */
struct list_node {
    struct list_node *prev;     /* Previous list element. */
    struct list_node *next;     /* Next list element. */
};
/*  业务数据 */
struct user_data_t {
    struct list node;     /* 用来存在全局hash表的节点 */
    char name[32];       /* user data. */
    ......
};

链表遍历

查找hash节点时,首先根据hash key 计算hash index, 即可以定位到数组下表。其次遍历hash冲突链。 如果在遍历的过程中节点可能有增删(其他线程),需采用安全遍历的宏

scss 复制代码
/* 链表头 */
struct list_head{
    list_node* pstFirst; /* the first element */
} ;

#define LIST_FOREACH_SAFE_RCU(pstList, pstNode, pstNextNode) \
    for ((pstNode) = rcu_dereference((pstList)->pstFirst); \
        (null != (pstNode)) && ({(pstNextNode) = rcu_dereference((pstNode)->pstNext); true;}); \
        (pstNode) = (pstNextNode))

回顾了基本的数据结构后,再来看下hash表上残留释放后内存的几种可能。

2、问题分析

释放后内存

分析coredump文件,如下图,变量hash冲突链 3 时,A,B节点均正常。C节点查看内存头已经不对。但也能正确访问,根据C节点的获取下一个节点D(D = C->next),却不是一个有效指针,直接报非法地址错误,产生core.

原因分析

1、释放节点未摘除链表

如果存在释放内存,却没有摘除节点,问题是必现流程。 实际排除代码,所有free内存的地方都删除了节点。链表节点删除代码如下:

rust 复制代码
void list_remove(struct list_node *node){
    node->prev->next = node->next;
    node->next->prev = node->prev;
    return;
}

2、踩内存

场景一:当前业务申请的C节点内存,是第三者申请和释放后内存、但是第三者继续持有。第三者异常写操作写坏内存的next域 和 内存头。 这种场景还有个特点,因为第三者这个内存可以被不同业务随机申请走,会有一些其他异常现象。

场景二:当前内存为C节点持有,但是有其他业务飞踩,踩到C节点的next域。比如越界写,写到C内存区域。一般这种场景也有个特点,C的整个内存区域可能都不对。不太会是next(8字节)不对,其他都是正常数据。

一般踩内存,是定位问题最后怀疑的方向。踩内存出现的异常,都不是第一现场,定位难度就大多了。

结合问题本身:查看了C的整个内存区域,其他数据域内容没有明显异常。内存头也是个释放后内存特征。且概率复现了几次,都是该业务节点。 所以踩内存的概率就很低。

3、链表重复添加

该问题 最终定位原因就是同一个节点C被重复添加到hash表中。第二次添加节点时,没有检测是否已经存在。

步骤1: 正常添加节点,如上图有A--B--C--D四个节点

步骤2: 再次添加节点C,采用链表头插法。数据结构就变成下面。

有两个问题:1、B的下一个节点是C,成环了。如果遍历会出现死循环。2、D变成了游离节点。

步骤3: 再次把C节点删除和释放。B的next还记录着C节点,就是个释放后内存。就有可能业务拿到C节点内存正常写内容。就会出现异常。

总结

链表作为最简单的数据结构,如果不能正确使用,遇到坑问题就会很难定位。

上面问题解决后,又有另外一个类似问题。 遍历list时访问异常指针,coredump文件,却显示冲突链找不到该节点。当前冲突链,只有两个节点,比如A,B,B的next指针是null。 下一篇继续分析。

相关推荐
papership15 小时前
【入门级-算法-6、排序算法: 插入排序】
数据结构·算法·排序算法
HAH-HAH15 小时前
【蓝桥杯 2024 国 Java A】粉刷匠小蓝
c++·学习·数学·算法·职场和发展·蓝桥杯·组合数学
hweiyu0016 小时前
C++设计模式,高级开发,算法原理实战,系统设计与实战(视频教程)
c++·算法·设计模式
大千AI助手16 小时前
粒子群优化(PSO)算法详解:从鸟群行为到强大优化工具
人工智能·算法·优化算法·pso·粒子群优化
我叫汪枫17 小时前
C语言深度入门系列:第十一篇 - 动态内存管理与数据结构:程序世界的高效算法大师
c语言·数据结构·算法
Li_76953217 小时前
优选算法100 题 ——1 双指针
算法
77qqqiqi17 小时前
算法——数学基础
算法
啊?啊?17 小时前
7 排序算法通关指南:从 O (n²)(选择 / 冒泡)到 O (nlogn)(快排 / 归并)+ 计数排序
数据结构·算法·排序算法
张较瘦_17 小时前
[论文阅读] 算法 | 抗量子+紧凑!SM3-OTS:基于国产哈希算法的一次签名新方案
论文阅读·算法·哈希算法
芒克芒克17 小时前
LeetCode 面试经典 150 题:多数元素(摩尔投票法详解 + 多解法对比)
算法·leetcode·面试