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

为何fread会将输出指针设为NULL并触发栈溢出错误?

二进制文件读写bool数组栈溢出问题修复

问题现象

尝试向二进制文件写入表示数组长度的int值,随后写入bool数组,写入操作可正常生成文件,但读取时触发栈溢出崩溃。

示例代码

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct cutlogTag{
    int len;
    bool *parts_cut;
} Cutlog;


size_t save_cutlog(const char *path, const Cutlog *p){
    
    FILE *fp;
    size_t written = 0;

    fp = fopen(path, "wb");
    if (fp == NULL){
        fprintf(stderr, "Failed to save cutlog file\n");
        return 0;
    }
    written = fwrite(&(p->len), sizeof(p->len), 1, fp);
    written += fwrite(p->parts_cut, sizeof(bool), p->len, fp);
    if(written != 1 + p->len)
        fprintf(stderr, "error writing file\n");
    else fprintf(stdout, "cutlog written to %s\n", path);
    fclose(fp);
    return written;
}

//returns cutlog with length of -1 on failure to load log
Cutlog load_cutlog(const char *path){
    
    Cutlog ret;
    FILE *fp;
    size_t read = 0;

    ret.len = -1;
    ret.parts_cut = NULL;
    
    fp = fopen(path, "rb");
    assert(fp != NULL);

    fseek(fp, 0, SEEK_SET);
    fread(&ret.len, sizeof(ret.len), 1, fp);
    ret.parts_cut = malloc(sizeof(bool) * ret.len);
    assert(ret.parts_cut);
    read = fread(&ret.parts_cut, sizeof(bool), ret.len, fp);
    if(read != ret.len){
        fprintf(stderr, "read unexpected size of data\n");
        ret.len = -1;
    }
    if (getc(fp) != EOF){
        fprintf(stderr, "expected file end. something went wrong. \n");
        ret.len = -1;
    }
    fclose(fp);
    return ret;
}

int main(int argc, char *argv[]){
    Cutlog clog;
    const char* path = "testbinary";
//initialize cutlog struct
    clog.len = 687;
    clog.parts_cut = malloc(sizeof(bool) * clog.len );
    assert(clog.parts_cut);
    for (int i = 0; i < clog.len; i++){
        clog.parts_cut[i] = false;
    }
//save to binary file and free from memory
    save_cutlog(path, &clog);
    free(clog.parts_cut);
//load from binary file
    clog = load_cutlog(path);
    fprintf(stdout, "len is %d\n", clog.len);
    return 0;
}

错误原因

load_cutlog函数中读取bool数组的fread调用参数错误:

read = fread(&ret.parts_cut, sizeof(bool), ret.len, fp);

此处传递的是&ret.parts_cut(指针变量自身的地址,类型为bool**),而非ret.parts_cut(malloc分配的数组首地址,类型为bool*)。fread会将文件中的bool数据直接写入指针变量ret.parts_cut的内存位置,覆盖malloc返回的合法地址,导致后续对该指针的访问变为非法内存操作,最终触发栈溢出。

修复方案

修正load_cutlog中的fread调用,将第一个参数改为ret.parts_cut,同时补充运行时错误处理(替换Release模式会失效的assert):

//returns cutlog with length of -1 on failure to load log
Cutlog load_cutlog(const char *path){
    
    Cutlog ret;
    FILE *fp;
    size_t read = 0;

    ret.len = -1;
    ret.parts_cut = NULL;
    
    fp = fopen(path, "rb");
    if (fp == NULL) {
        fprintf(stderr, "Failed to open cutlog file\n");
        return ret;
    }

    fseek(fp, 0, SEEK_SET);
    if (fread(&ret.len, sizeof(ret.len), 1, fp) != 1) {
        fprintf(stderr, "Failed to read length\n");
        fclose(fp);
        ret.len = -1;
        return ret;
    }

    // 检查长度合法性,避免非法内存分配
    if (ret.len <= 0) {
        fprintf(stderr, "Invalid array length\n");
        fclose(fp);
        ret.len = -1;
        return ret;
    }

    ret.parts_cut = malloc(sizeof(bool) * ret.len);
    if (ret.parts_cut == NULL) {
        fprintf(stderr, "Malloc failed\n");
        fclose(fp);
        ret.len = -1;
        return ret;
    }

    // 修正:传递数组首地址而非指针的地址
    read = fread(ret.parts_cut, sizeof(bool), ret.len, fp);
    if(read != ret.len){
        fprintf(stderr, "read unexpected size of data\n");
        free(ret.parts_cut);
        ret.parts_cut = NULL;
        ret.len = -1;
    }
    if (getc(fp) != EOF){
        fprintf(stderr, "expected file end. something went wrong. \n");
        free(ret.parts_cut);
        ret.parts_cut = NULL;
        ret.len = -1;
    }
    fclose(fp);
    return ret;
}

额外优化说明:

  • 替换assert为运行时错误判断,保证Release模式下的异常处理能力;
  • 增加数组长度合法性检查,避免malloc分配0或负数大小的内存;
  • 错误时及时释放已分配的内存,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:35:15