realloc()/malloc()内存错误排查:动态数组越界导致堆损坏
内存错误排查:realloc()和malloc()崩溃问题
问题现象
尝试从文件打印特定行到控制台,用动态数组记录需打印行,内存不足时调用realloc()扩容,出现以下错误:
realloc(): invalid next size Aborted (core dumped)malloc(): unsorted double linked list corrupted
首次realloc()不崩溃,第二次必崩;仅一次realloc()时函数内无崩溃,但程序会在main()结束前崩溃。Valgrind显示存在Invalid write of size 4的越界写入,错误地址位于已分配内存块之后14字节处。
相关代码
void print_with_context(int* arr, char* file, int arr_size){ if(arr_size == 0) return; FILE* stream; char* line = NULL; size_t len = 0; int line_number = 1; stream = fopen(file, "r"); if(!stream) die("fopen"); smallest_to_biggest_sort(arr, arr_size); int i = 0; while(arr[i] <= 0 && i < arr_size) //ignore negative lines ++i; while(getline(&line, &len, stream) != -1){ if(i >= arr_size) break; if(i > 0){ if(arr[i] == arr[i - 1]){ ++i; continue; } } if(arr[i] == line_number){ print_cwd(); //print current working directory int success = printf("%s:%d:", file, line_number); if(success < 0) die("printf with context"); errno = 0; int line_length = strlen(line); int fwrite_success = fwrite(line, line_length, 1, stdout); if(fwrite_success){ if(errno != 0) die("fwrite"); } ++i; } ++line_number; } free(line); if(fclose(stream)) die("fclose"); } void print_regex(struct stat* file_info, regex_t* regex_line, char* file){ if(!S_ISREG(file_info->st_mode)) return; if(!regex_line) return; size_t nmatch = 1; regmatch_t pmatch[1]; FILE* stream; char* line = NULL; size_t len = 0; int line_number = 1; stream = fopen(file, "r"); if(!stream) die("fopen"); int* arr = malloc(array_length * sizeof(int)); if(!arr) die("malloc"); int start = 1, line_found = 0, i = 0; while(getline(&line, &len, stream) != -1){ if(regexec(regex_line, line, nmatch, pmatch, 0) != REG_NOMATCH){ ++line_found; if(line_found > resize){ ++resize; array_length += 2 * context + 1; errno = 0; arr = realloc(arr, array_length);// <= here fails with the error if(errno == ENOMEM) die("realloc in print_regex"); } start = line_number - context; for(;i < array_length; ++i) arr[i] = start++; } ++line_number; } print_with_context(arr, file, i); free(arr); free(line); if(fclose(stream)) die("fclose"); }
Valgrind调试输出
==11041== Memcheck, a memory error detector ==11041== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==11041== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==11041== Command: ./crawl . -line=print* -context=3 ==11041== ==11041== Invalid write of size 4 ==11041== at 0x109E25: print_regex (crawl.c:392) ==11041== by 0x10A4A2: crawl (crawl.c:518) ==11041== by 0x10A503: crawl (crawl.c:524) ==11041== by 0x10A503: crawl (crawl.c:524) ==11041== by 0x10A82B: main (crawl.c:579) ==11041== Address 0x4a8824c is 14 bytes after a block of size 14 alloc'd ==11041== at 0x4846CC3: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==11041== by 0x109DD6: print_regex (crawl.c:385) ==11041== by 0x10A4A2: crawl (crawl.c:518) ==11041== by 0x10A503: crawl (crawl.c:524) ==11041== by 0x10A503: crawl (crawl.c:524) ==11041== by 0x10A82B: main (crawl.c:579) ==11041== --11041-- VALGRIND INTERNAL ERROR: Valgrind received a signal 11 (SIGSEGV) - exiting --11041-- si_code=1; Faulting address: 0x3E04A88294; sp: 0x1002ca9e40 valgrind: the 'impossible' happened: Killed by fatal signal host stacktrace: ==11041== at 0x5804D09B: ??? (in /usr/lib/valgrind/memcheck-amd64-linux) ==11041== by 0x580056B2: ??? (in /usr/lib/valgrind/memcheck-amd64-linux) ==11041== by 0x5809B33D: ??? (in /usr/lib/valgrind/memcheck-amd64-linux) ==11041== by 0x580E40C0: ??? (in /usr/lib/valgrind/memcheck-amd64-linux) sched status: running_tid=1 Thread 1: status = VgTs_Runnable (lwpid 11041) ==11041== at 0x4846CC3: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==11041== by 0x109DD6: print_regex (crawl.c:385) ==11041== by 0x10A4A2: crawl (crawl.c:518) ==11041== by 0x10A503: crawl (crawl.c:524) ==11041== by 0x10A503: crawl (crawl.c:524) ==11041== by 0x10A82B: main (crawl.c:579) client stack range: [0x1FFEFFA000 0x1FFF000FFF] client SP: 0x1FFEFFFC30 valgrind stack range: [0x1002BAA000 0x1002CA9FFF] top usage: 10752 of 1048576
错误原因分析
- realloc内存大小计算错误:
realloc(arr, array_length)的第二个参数是字节数,但你仅传入了数组元素个数,未乘以sizeof(int)。以int占4字节为例,实际分配的内存只有需求的1/4,直接导致内存不足,后续写入越界。 - 数组填充范围失控:找到匹配行后,用
for(;i < array_length; ++i)循环填充数组,会把从当前i到array_length末尾的所有位置填满,完全忽略了当前匹配行仅需写入2*context+1个元素的需求,直接触发越界写入,破坏堆的链表结构。 - 全局变量未重置:
array_length和resize是全局变量,每次调用print_regex时未重置初始值,多次调用时残留值会导致初始内存分配、扩容逻辑混乱,加剧内存错误。
修复方案
- 修正内存分配的字节数计算:每次malloc/realloc时,都要乘以
sizeof(int):int* arr = malloc(array_length * sizeof(int)); // ... arr = realloc(arr, array_length * sizeof(int)); - 控制数组填充的元素数量:匹配行仅写入
2*context+1个元素,避免越界:int num_elements = 2 * context + 1; for(int j = 0; j < num_elements; ++j, ++i) { if (i >= array_length) { // 此处需触发扩容逻辑,防止越界 break; } arr[i] = start++; } - 重置全局变量:在
print_regex函数开头,将array_length和resize重置为初始值(比如array_length初始设为2*context+1,resize设为0),避免多次调用的状态污染。
内容的提问来源于stack exchange,提问作者yad0
相关产品推荐
相关产品推荐

