C内存操作错误分析

  1. 1. 一、内存申请释放不匹配
  2. 2. 二、释放后使用(UAF)+ 全局指针未置空
  3. 3. 三、内存泄漏(被调用方申请,调用方未释放)
  4. 4. 四、指针置空操作无效(仅修改形参)
  5. 5. 五、条件分支导致的内存泄漏(指针重分配未释放原内存)
  6. 6. 六、跨过程内存泄漏(分支提前return,未释放子函数申请的内存)
  7. 7. 七、全局变量导致的双重释放风险
  8. 8. 八、结构体指针操作空指针解引用风险(多层嵌套结构体,未判空)

文档中的代码均为 C/C++内存操作错误 的典型案例,覆盖 内存泄漏释放后使用UAF)、双重释放空指针解引用置空无效 等核心问题,按错误类型+代码分析+问题根源拆解如下,同时补充修复思路,便于理解问题本质:

一、内存申请释放不匹配

1
2
3
4
5
void Test(){
void *p= MemoryManagerMalloc(xxx); // 自定义内存池申请
free (p); // 直接系统free释放
p=NULL;
}

问题:自定义内存管理模块的内存,需通过 模块自身接口 释放,直接调用 free 会破坏内存池管理规则,导致内存池状态异常、后续申请/释放出错。
修复:替换为 内存管理模块 的释放接口,如 MemoryManagerFree(p)。

二、释放后使用(UAF)+ 全局指针未置空

1
2
3
4
5
6
7
8
9
10
11
12
A*g; // 全局指针
void Test(){
A*l=malloc(...);
if(l==NULL) return;
g=l; // 全局指针指向局部申请的内存
if(error){
free(l);l = NULL; // 仅置空局部指针,g未处理
}
}
void Test2(){
if(g!=NULL){ *g; } // 解引用已释放的内存(UAF)
}

问题:局部指针释放后仅置空 自身,全局指针仍指向 已释放 的内存区域,后续 Test2 解引用 g 触发释放后使用漏洞,属于严重的 时间内存问题
修复:释放局部指针时,同步置空全局指针 g=NULL;。

三、内存泄漏(被调用方申请,调用方未释放)

1
2
3
4
5
void TestHandle(void **p){ *p=malloc; } // 被调用方申请内存
void TestHandlers (){
void *p;
TestHandle(&p); // 接收申请的内存,但未释放
}

问题:子函数通过二级指针为父函数的指针分配内存,父函数未接管内存释放操作,函数结束后指针销毁,内存无引用可释放,造成 显性内存泄漏
修复:调用方使用完后释放内存 free(p); p=NULL;。

四、指针置空操作无效(仅修改形参)

1
2
3
void CustomFree2(int *p){
if(p!=NULL){ free(p); P= NULL; } // 形参置空
}

问题:C语言中函数参数为 值传递,此处仅修改形参 p 的地址,主调函数中的 实参指针 仍指向 已释放的内存,置空操作无实际防护效果,后续仍可能解引用非法指针。
修复:通过二级指针接收参数,修改实参指向:

1
2
3
void CustomFree2(int **p){ 
if(*p){free(*p);*p=NULL;}
}

五、条件分支导致的内存泄漏(指针重分配未释放原内存)

1
2
3
4
5
6
7
8
9
10
11
12
void TestLocalRemalloc(int cond) {
int *p = (int*)malloc(sizeof(int));
if (cond == 2) {
free(p); // 释放原内存后重分配,无问题
p = (int*)malloc(sizeof(int));
} else if (cond = 3) { // 注意:是赋值=,非判断==
p = (int *)malloc(sizeof(int)); // 未释放原p,直接重分配
}
memset(p, 1 , sizeof(int));
printf("%d", *p);
free(p);
}

问题

  1. 语法错误:cond=3 是 赋值 操作,非条件判断,导致该分支 恒成立
  2. 内存泄漏:cond=3 分支中,直接为 p 分配新内存,未释放malloc 的内存,原内存地址丢失,造成泄漏;
  3. 若修正为 cond==3,仍存在分支内的泄漏问题。

修复

  1. 修正判断符: cond==3;
  2. 重分配前释放原内存: free(p); p = malloc(…);。

六、跨过程内存泄漏(分支提前return,未释放子函数申请的内存)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  
void* CreateData(size_t num) { // 子函数申请内存并返回
if (num > 0){ return malloc(num * sizeof(char)); }
return NULL;
}
void TestLocalForgetFree(int data) {
void *p = malloc(10);
void *q = CreateData(20); // 接收子函数的内存
if (data > 0) {
free(p);
return; // 提前返回,q未释放,跨过程泄漏
}
free(p); free(q); // 正常分支无问题
}

问题:data>0 分支中提前 return ,未释放子函数 CreateData 申请的 q 内存,属于 跨过程的内存泄漏,简单的字符串扫描无法识别(需追踪跨函数的内存持有关系)。
修复:提前返回前释放所有申请的内存 free(q); q=NULL; return;。

七、全局变量导致的双重释放风险

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int* p_mem; // 全局指针
int cond_code;
void UAFTestCase2IncompleteFree() {
int* q = (int*) malloc(MEM_SIZE);
if (q == NULL) return;
p_mem = q; // 全局指针与局部指针指向同一块内存
if (cond_code < 0) {
free(q); q = NULL; // 仅置空局部指针,p_mem未置空
}
}
void UAFTestCase2Free() {
if (p_mem != NULL) { free(p_mem); } // 释放全局指针
}
int UAFTestCase2() {
UAFTestCase2IncompleteFree();
UAFTestCase2Free(); // 二次释放风险
return OK;
}

问题:当 cond_code<0 时,局部指针 q 被释放全局指针 p_mem 未置空,后续 UAFTestCase2Free 检测 p_mem!=NULL 并释放,触发 双重释放(DF);若 UAFTestCase2Free 是解引用而非释放,则触发 UAF
修复释放 q 时 同步置空 全局指针 p_mem=NULL; 。

八、结构体指针操作空指针解引用风险(多层嵌套结构体,未判空)

核心结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct{
int type;
int data1/2/3/5/7;
char buf[256];
char data4;
} TEST_MSG;

typedef struct{
int item1-3;
struct{
int x,y;
struct{
int d1-3;
TEST_MSG* pMsg;
}inner_data;
} data1;
} OUTTER_MSG;
OUTTER_MSG oMsg; // 全局结构体

风险代码

1
2
3
4
5
6
7
8
9
10
11
12
  
void process_globa1(int x,int y){
oMsg.data.inner_data.pMsg=(TEST_MSG*)malloc(sizeof(TEST_MSG));
if(oMsg.data.inner_data.psg==nul1ptr) return; // 拼写错误:psg≠pMsg
TEST_MSG*pMsg = oMsg.data1.inner_data.pMsg; // 直接赋值,未判空
if (pMsg->type==1) pMsg->data1 = temp;
else process_1(x,y); // 内部直接操作pMsg,无判空
}
static void process_1(int x, int y){
TEST_MSG* pMsg = oMsg.data1.inner_data.pMsg; // 直接取全局指针,未判空
for(inti=0;i<10; i++) process_2(pMsg,X,y); // 传递未判空的指针
}

核心问题

  1. 拼写错误:判空的是 psg (不存在的字段),而非实际的 pMsg, malloc 失败时 pMsg=NULL ,后续直接解引用触发空指针解引用;
  2. 多层嵌套结构体的指针字段( oMsg.data1.inner_data.pMsg )在所有操作前均未做判空校验;
  3. 全局结构体的指针字段被多个函数直接调用,一处 NULL 会导致所有调用函数崩溃。

修复

  1. 修正拼写错误: oMsg.data1.inner_data.pMsg==nullptr ;
  2. 所有使用 pMsg 的位置,先判空再操作: if(pMsg == NULL) return; ;
  3. 全局结构体指针赋值后,立即做判空防护。

所有程序的共性问题总结

1.指针管理失序:申请/释放/赋值未形成闭环,局部指针释放后未同步置空全局/关联指针;
2.分支/跨函数追踪缺失:条件分支提前return、子函数申请内存,未做全路径的释放处理;
3.语法/拼写错误: = 与 == 混淆、变量名拼写错误,导致内存操作逻辑失效;
4.值传递认知不足:直接修改函数形参指针,误以为能改变实参指向;
5.判空防护缺失:对动态分配的指针(尤其是结构体嵌套指针),未做前置判空。