【缺陷周话】第28期:被污染的内存分配
字数 1525 2025-08-18 11:38:22
被污染的内存分配 - 代码安全审计详解
1. 概念与背景
被污染的内存分配(Uncontrolled Memory Allocation)是指当内存分配函数的长度参数来自于不可信源且未经验证时,可能导致系统分配过大内存的安全缺陷。
CWE编号: CWE-789
常见内存分配函数:
malloc()kmalloc()smalloc()xmalloc()realloc()calloc()GlobalAlloc()HeapAlloc()
2. 漏洞原理
2.1 基本机制
当以下条件同时满足时,会产生被污染的内存分配漏洞:
- 内存分配函数的长度参数来自外部不可信源
- 未对输入的长度参数进行有效验证和限制
- 直接使用该参数进行内存分配
2.2 污染源类型
- 命令行参数
- 配置文件
- 网络通信数据
- 数据库记录
- 环境变量
- 注册表值
- 其他外部输入数据
3. 危害与影响
3.1 直接危害
- 分配超大内存块导致系统内存耗尽
- 引发拒绝服务(DoS)攻击
- 程序崩溃或异常终止
3.2 实际漏洞案例
| CVE编号 | 受影响组件 | 漏洞位置 | 攻击方式 |
|---|---|---|---|
| CVE-2018-6869 | ZZIPlib 0.13.68 | zzip/zip.c中的__zzip_parse_root_directory函数 |
特制zip文件 |
| CVE-2018-5783 | PoDoFo 0.9.5 | base/PdfVecObjects.h中的PoDoFo::PdfVecObjects::Reserve函数 |
特制pdf文件 |
| CVE-2018-5296 | PoDoFo 0.9.5 | base/PdfParser.cpp中的PdfParser::ReadXRefSubsection函数 |
特制pdf文件 |
4. 代码示例分析
4.1 缺陷代码示例
#include <stdio.h>
#include <stdlib.h>
int GetUntrustedInt() {
int size;
scanf("%d", &size); // 从用户输入获取size值
return size;
}
int main() {
int size = GetUntrustedInt();
size_t totBytes = size * sizeof(char);
char* buf = (char*)malloc(totBytes); // 危险:未验证size大小
if(buf != NULL) {
free(buf);
}
return 0;
}
问题分析:
size来自用户输入(不可信源)- 直接使用
size计算totBytes - 未对
totBytes进行任何限制检查 - 可能导致分配超大内存块
4.2 修复代码示例
#include <stdio.h>
#include <stdlib.h>
#define MAX_ALLOWED_BYTES 1024
int GetUntrustedInt() {
int size;
scanf("%d", &size);
return size;
}
int main() {
int size = GetUntrustedInt();
size_t totBytes = size * sizeof(char);
// 添加长度验证
if(totBytes > MAX_ALLOWED_BYTES) {
fprintf(stderr, "Error: Memory allocation size too large\n");
return -1;
}
char* buf = (char*)malloc(totBytes);
if(buf != NULL) {
free(buf);
}
return 0;
}
修复要点:
- 定义最大允许分配大小
MAX_ALLOWED_BYTES - 在分配前验证
totBytes是否超过限制 - 超过限制时返回错误而非继续分配
5. 检测与防御
5.1 检测方法
-
代码审计:
- 跟踪所有内存分配函数的参数来源
- 检查是否来自不可信源
- 检查是否有适当的验证逻辑
-
静态分析工具:
- 使用360代码卫士等专业工具
- 可自动识别未经验证的内存分配
5.2 防御措施
-
输入验证:
- 对所有来自外部的长度参数进行验证
- 设置合理的上下限
-
安全编码实践:
- 避免直接使用外部输入作为分配大小
- 使用安全的包装函数
-
资源限制:
- 设置进程内存限制
- 使用内存池技术
6. 最佳实践建议
- 对于所有内存分配函数,都应验证其大小参数
- 建立组织内部的安全编码规范,明确内存分配的安全要求
- 在代码审查中重点关注内存分配相关的代码
- 对开发人员进行安全编码培训,提高安全意识
- 在持续集成流程中加入静态分析工具检查
7. 扩展思考
-
相关漏洞类型:
- 整数溢出导致的内存分配问题
- 内存泄漏问题
- 双重释放问题
-
防御深度:
- 除了代码层面的防御,还应考虑系统层面的防护
- 如使用地址空间布局随机化(ASLR)
- 内存保护机制等
-
语言选择考量:
- 考虑使用更安全的语言(如Rust)替代C/C++
- 或使用智能指针等现代C++特性
通过全面理解被污染的内存分配问题,开发者可以编写出更安全的代码,有效预防此类安全漏洞的发生。