【缺陷周话】第37期:未初始化值用于赋值操作
字数 1720 2025-08-18 11:38:41
未初始化值用于赋值操作:安全缺陷分析与防范
1. 概念与定义
未初始化值用于赋值操作是指程序中使用未被明确初始化的变量或内存区域的值进行赋值操作,可能导致未定义行为的安全缺陷。这类缺陷主要涉及:
- 局部自动变量(存储在栈中)未初始化时,默认值为栈内存中的残留值
- 动态内存分配函数(如
malloc()、aligned_alloc()等)分配的内存未被初始化
相关CWE分类:
- CWE-665: Improper Initialization(不正确的初始化)
- CWE-758: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior(依赖未定义、未指定或实现定义的行为)
2. 危害与影响
未初始化的变量或内存具有不确定的值,使用这些值可能导致:
- 非预期行为:程序逻辑出现不可预测的错误
- 安全漏洞:可能被恶意攻击者利用,造成严重安全隐患
- 内存相关问题:如内存越界读取、内存损坏等
实际漏洞案例(2018-2019):
| CVE编号 | 受影响产品 | 漏洞描述 |
|---|---|---|
| CVE-2018-8627 | Microsoft Excel | 未初始化变量导致读取越界内存 |
| CVE-2018-8378 | Microsoft Office | 未初始化变量导致读取越界内存 |
| CVE-2018-3975 | Atlantis Word Processor | 未初始化变量导致越界写入和执行代码 |
| CVE-2018-14551 | ImageMagick | 未初始化变量导致内存损坏 |
3. 代码示例分析
3.1 缺陷代码示例
#include <stdlib.h>
void bad() {
char * pointer;
char data;
pointer = (char *)malloc(sizeof(char));
if (pointer == NULL) {exit(-1);}
data = *pointer; /* 缺陷:使用未初始化的pointer指向的值赋值给data */
free(pointer);
}
问题分析:
- 第5行使用
malloc()分配内存但未初始化 - 第7行直接将未初始化的
pointer指向的值赋给data pointer指向的内存内容是未定义的,可能导致未定义行为
3.2 修复后的代码
#include <stdlib.h>
void good() {
char * pointer;
char data;
pointer = (char *)malloc(sizeof(char));
if (pointer == NULL) {exit(-1);}
*pointer = 'B'; /* 修复:先对pointer指向的内存进行初始化 */
data = *pointer;
free(pointer);
}
修复要点:
- 在使用
pointer指向的值前,先对其进行初始化(第7行) - 确保所有赋值操作都使用已初始化的值
4. 检测与防范措施
4.1 检测方法
- 静态代码分析工具:如代码卫士等专业工具可以检测此类问题
- 代码审查:重点关注:
- 所有局部变量的初始化情况
- 动态分配内存的使用情况
- 赋值操作的来源是否已初始化
4.2 防范措施
-
变量声明时初始化:
int x = 0; // 显式初始化 char *ptr = NULL; // 指针初始化为NULL -
动态内存分配后立即初始化:
int *arr = (int *)malloc(10 * sizeof(int)); if (arr != NULL) { memset(arr, 0, 10 * sizeof(int)); // 初始化分配的内存 } -
使用安全的分配函数:
- 优先使用
calloc()而非malloc(),因为calloc()会将分配的内存初始化为0 - 考虑使用安全库函数,如Windows API中的
SecureZeroMemory()
- 优先使用
-
防御性编程实践:
- 对所有变量进行显式初始化
- 在使用变量前进行有效性检查
- 对指针进行NULL检查后再解引用
-
编译器辅助:
- 开启编译器警告选项(如GCC的
-Wuninitialized) - 使用静态分析工具集成到开发流程中
- 开启编译器警告选项(如GCC的
5. 扩展知识
-
不同内存区域的初始化行为:
- 静态存储期变量(全局变量、static变量):自动初始化为0
- 自动存储期变量(局部变量):不自动初始化
- 动态分配内存:取决于分配函数(
malloc不初始化,calloc初始化为0)
-
相关C/C++函数:
malloc(): 分配内存但不初始化calloc(): 分配内存并初始化为0realloc(): 重新分配内存,可能保留或丢弃原有内容memset(): 手动初始化内存区域
-
现代语言对比:
- Java/C#等托管语言会自动初始化变量
- Rust等现代系统编程语言强制要求变量初始化
6. 总结
未初始化值用于赋值操作是一个常见但危险的安全缺陷,可能导致程序行为不可预测和安全漏洞。通过以下方法可以有效防范:
- 始终显式初始化变量
- 对动态分配的内存立即初始化
- 使用静态分析工具检测潜在问题
- 采用防御性编程实践
- 充分利用编译器警告功能
遵循这些最佳实践可以显著减少因未初始化变量导致的安全问题和程序缺陷。