You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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时未重置初始值,多次调用时残留值会导致初始内存分配、扩容逻辑混乱,加剧内存错误。

修复方案

  1. 修正内存分配的字节数计算:每次malloc/realloc时,都要乘以sizeof(int):
    int* arr = malloc(array_length * sizeof(int));
    // ...
    arr = realloc(arr, array_length * sizeof(int));
    
  2. 控制数组填充的元素数量:匹配行仅写入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++;
    }
    
  3. 重置全局变量:在print_regex函数开头,将array_length和resize重置为初始值(比如array_length初始设为2*context+1,resize设为0),避免多次调用的状态污染。

内容的提问来源于stack exchange,提问作者yad0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:20:33