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 中读取值时,已知它们是有效值。

相关检查器