如何解决C语言程序中检测到的heap buffer overflow问题
问题描述
程序看似运行正常,但用AddressSanitizer编译时触发heap buffer overflow(堆缓冲区溢出),问题出在findInArray函数中。该函数接收二维数组replace(replace[a][0]为待查找单词)和字符串start,功能是在start中查找是否存在replace中的单词。
函数代码如下:
const char * findInArray(const char * (*replace)[2], char * start) // function goes through a array 'start' and searches if the word is also in 'replace' { char * copy = (char *) malloc (strlen(start)+1); // I made a copy of a field, so I do not modify the one I passed to function char * total = (char *) malloc (strlen(start)+2); // Here I will add words from copy one by one memmove(copy,start,strlen(start)+1); // I fill the copy array char * tokens = strtok (copy," "); // I split copy array while (tokens != NULL) { printf("%lu ",strlen(total)); memmove(total+strlen(total)+1,tokens,strlen(tokens)); // I add words to a new array one by one memmove (total + strlen(total)," ",1); // at the end of each word I add space for (int i = 0 ; replace[i][0] != NULL; i++) // I search if word is in array or not, if yes I return its adress {const char *ptr = strstr(total,replace[i][0]); if (ptr != NULL) // If there is match - I return pointer to a word that will be replaced { free (total); return replace[i][0]; } } //printf("%s\n",tokens); tokens = strtok(NULL, " "); } free (total); return NULL; }
AddressSanitizer给出的错误信息:
==58229==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x000104d00bbb at pc 0x000102c7468c bp 0x00016d562b20 sp 0x00016d5622d8 READ of size 44 at 0x000104d00bbb thread T0 #0 0x102c74688 in wrap_strlen+0x164 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x14688) #1 0x10289e4dc in findInArray test 4.c:39 // the line of moving memory to total #2 0x10289ee14 in newSpeak test 4.c:129 #3 0x10289f880 in main test 4.c:186 #4 0x1b4df7e4c (<unknown module>) 0x000104d00bbb is located 0 bytes to the right of 43-byte region [0x000104d00b90,0x000104d00bbb) allocated by thread T0 here: #0 0x102c9eca8 in wrap_malloc+0x94 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x3eca8) #1 0x10289e480 in findInArray test 4.c:31 // the line of allocating total array #2 0x10289ee14 in newSpeak test 4.c:129 #3 0x10289f880 in main test 4.c:186 #4 0x1b4df7e4c (<unknown module>)
问题分析与修复
核心问题
- 未初始化的
total导致越界读取:malloc分配的内存未清零,strlen(total)会一直读取到内存中的第一个\0,这个位置大概率超出total的分配范围,直接触发溢出。 - 写入位置错误:
memmove(total+strlen(total)+1, tokens, strlen(tokens))中的+1是多余的,会导致写入位置跳过一个字节,后续追加空格后容易超出total的容量。 - 内存泄漏:
copy指针未被free,函数退出后会残留内存泄漏。
修复后的代码
const char * findInArray(const char * (*replace)[2], char * start) { char * copy = (char *)malloc(strlen(start)+1); if (!copy) return NULL; // 增加内存分配失败检查 char * total = (char *)malloc(strlen(start)+2); if (!total) { free(copy); return NULL; } // 分配失败时释放已分配内存 memmove(copy, start, strlen(start)+1); total[0] = '\0'; // 初始化total为空字符串,确保strlen返回正确值 char * tokens = strtok(copy, " "); while (tokens != NULL) { size_t current_len = strlen(total); // 直接追加tokens到total当前末尾 memmove(total + current_len, tokens, strlen(tokens)); current_len += strlen(tokens); // 添加空格并确保字符串以\0结尾 total[current_len] = ' '; total[current_len + 1] = '\0'; for (int i = 0 ; replace[i][0] != NULL; i++) { const char *ptr = strstr(total, replace[i][0]); if (ptr != NULL) { free(total); free(copy); // 释放copy内存 return replace[i][0]; } } tokens = strtok(NULL, " "); } free(total); free(copy); // 释放copy内存 return NULL; }
额外优化说明
- 增加了内存分配失败的检查,避免空指针访问导致的崩溃。
- 手动维护
current_len变量,减少strlen的重复调用,提升效率的同时确保total始终是合法的C字符串。 - 所有分支都确保
copy和total被正确释放,彻底消除内存泄漏风险。
内容的提问来源于stack exchange,提问作者Matouš Kovář
相关产品推荐
相关产品推荐

