UNINIT.HEAP.MUST
使用的堆未初始化
UNINIT.HEAP.MUST 检查器可以发现使用 malloc 分配的堆内存在使用前没有被初始化的情况。
漏洞与风险
使用未初始化的堆内存会导致严重的代码性能缺陷,因为分配到给定数据对象的值是从所分配的堆内存中随机选取的,并能反映先前所用对象或其他进程中的对象的状态。如果软件没有对内存进行正确的初始化,会发生意外结果,并可能存在安全隐患。
缓解与预防
在从常用分配函数 malloc() 返回后,堆内存一直未进行初始化。为了避免发生未初始化内存问题,务必确保所有变量和资源在首次使用之前全部进行显式初始化。
另外,也可以使用标准 calloc() 库函数,自动将所有分配的内存初始化为零。
漏洞代码示例 1
复制
#include<stdlib.h>
typedef struct {
int x;
} S1;
typedef struct {
S1* ptr;
} S2;
void foo(S1* ptr) {
int k = ptr ? ptr->x : -1;
}
int main() {
S2* ps = (S2*)malloc(sizeof(S2));
if( ps != NULL ) {
foo(ps->ptr);
free(ps);
}
return 0;
}
Klocwork 标记了第 18 行,指出变量 ps->ptr 从第 16 行分配的内存获取值,其在第 18 行使用时尚未进行初始化。
修正代码示例 1
复制
#include<stdlib.h>
typedef struct {
int x;
} S1;
typedef struct {
S1* ptr;
} S2;
void foo(S1* ptr) {
int k = ptr ? ptr->x : -1;
}
int main() {
S2* ps = (S2*)calloc(1, sizeof(S2));
if( ps != NULL ) {
foo(ps->ptr);
free(ps);
}
return 0;
}
在经修复的代码中,对 malloc() 的调用被简单地替换成了对 calloc() 的调用,以确保将分配的内存预初始化为零。这样,就不必在使用指针 ps 前,手动初始化 S2 结构中的每个元素。
漏洞代码示例 2
复制
#include<stdlib.h>
typedef struct {
int x, y;
} S1;
int foo(S1* ptr) {
return ptr->x * ptr->y;
}
int main() {
S1* ps = (S1*)malloc(sizeof(S1));
if( ps != NULL ) {
foo(ps);
free(ps);
}
}
Klocwork 标记了第 14 行,因为在第 12 行分配的内存在第 14 行使用时尚未进行初始化。
修正代码示例 2
复制
#include<stdlib.h>
typedef struct {
int x, y;
} S1;
int foo(S1* ptr) {
return ptr->x * ptr->y;
}
int main() {
S1* ps = (S1*)malloc(sizeof(S1));
if( ps != NULL ) {
ps->x = 32, ps->y = 64;
foo(ps);
free(ps);
}
}
在经修复的代码中,我们不使用 calloc() 函数分配内存,而是显式初始化结构 S1 中的成员,以便在指针 ps 中读取值时,已知它们是有效值。