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

CS50 recover作业valgrind内存错误排查及check50本地远程差异问题

CS50 Recover作业内存错误与本地/远程check50差异问题解析

问题概况

  • 远程check50:功能测试全部通过,但valgrind检测出多处Invalid write/read内存错误,涉及recover.c第84、88、102行
  • 本地valgrind:运行各类命令均未检测到内存泄漏或错误
  • 本地check50:仅通过文件存在和编译检查,其余功能测试因退出码不符(预期1/0,实际127)失败

代码中的内存错误根源

1. 栈上可变长度数组(VLA)溢出

在recoverJPGS函数里,你用uint8_t image[imageSize];在栈上创建了可变长度数组。JPG文件体积可能远超栈的默认容量(通常仅几MB),当imageSize过大时直接引发栈溢出——远程环境的栈限制比本地严格,所以本地没触发但远程被valgrind抓个正着。

2. findJPGS的越界访问风险

  • size_t numberJpgs = -1;:size_t是无符号类型,-1会被转成极大的正数。如果没找到任何JPG,后续jpgs[numberJpgs].end = jpgStart - 1;会直接越界访问数组,触发Invalid write错误。
  • 未检查numberJpgs是否超过传入的数组容量(50):如果找到的JPG数量超过50,会直接往jpgs数组外面写数据,导致内存越界。

3. 二进制文件处理错误

打开输出文件用了"w"模式,处理二进制文件必须用"wb"模式——虽然部分系统兼容,但会导致二进制数据写入时的换行符转换问题,可能破坏JPG文件结构。

4. fread重复调用导致数据丢失

在recoverJPGS的错误处理里,你连续调用了两次fread:第一次失败后,第二次调用会继续读取文件,导致文件指针偏移,后续数据读取完全错误。

5. 文件偏移的类型转换溢出

range.start是size_t类型,转成long int可能溢出(如果文件大小超过long int最大值),应该用fseeko和off_t类型处理大文件偏移。


本地与远程结果差异的原因

1. 环境配置差异

  • 栈大小限制:远程CS50环境的栈容量比本地小,栈上VLA的溢出在远程更容易触发。
  • 工具版本差异:远程的clang、valgrind版本更严格,能捕捉到本地版本忽略的内存问题。
  • 系统架构差异:32位/64位系统的类型长度(如size_t、long int)不同,类型转换时的溢出问题可能只在某一环境触发。

2. 本地check50退出码127问题

退出码127表示找不到要执行的命令,大概率是本地check50环境配置错误,或者你的./recover可执行文件不在check50的搜索路径里。先直接运行./recover看能否正常执行,再检查check50的本地配置。


代码修正示例

1. 替换VLA为动态内存分配

把栈上的VLA换成malloc分配的堆内存,避免栈溢出:

// 替换uint8_t image[imageSize];
uint8_t *image = malloc(imageSize);
if (image == NULL) {
    printf("malloc failed for image %lu\n", i);
    return 1;
}

// 使用完image后记得释放
free(image);

2. 修复findJPGS的无符号类型与越界问题

int findJPGS(FILE *file, struct byteRange *jpgs, size_t size)
{
    uint8_t block[512];
    size_t jpgStart = 0;
    size_t numberJpgs = 0;
    int found_first = 0; // 标记是否找到第一个JPG

    // 这里去掉&block,block本身就是指针
    while (fread(block, sizeof(uint8_t), 512, file)) {
        for (size_t i = 0; i < 512; i++) {
            if (block[i] == 0xff && i + 3 < 512) {
                if (block[i+1] == 0xd8 && block[i+2] == 0xff && (block[i+3] & 0xf0) == 0xe0) {
                    if (!found_first) {
                        found_first = 1;
                        jpgs[numberJpgs].start = jpgStart;
                    } else {
                        // 检查是否超过数组容量
                        if (numberJpgs >= size) {
                            printf("Too many JPGs, array full\n");
                            return numberJpgs;
                        }
                        jpgs[numberJpgs].end = jpgStart - 1;
                        numberJpgs++;
                        jpgs[numberJpgs].start = jpgStart;
                    }
                }
            }
            jpgStart++;
        }
    }

    // 确保找到过JPG才设置end
    if (found_first) {
        jpgs[numberJpgs].end = jpgStart - 1;
        numberJpgs++;
    }
    return numberJpgs;
}

3. 修复文件模式与fread错误处理

// 打开输出文件用wb模式
FILE *output = fopen(fileName, "wb");

// 修复fread重复调用问题
size_t read_bytes = fread(image, 1, imageSize, file);
if (read_bytes != imageSize) {
    printf("fread image %lu failed: read %lu/%lu bytes\n", i, read_bytes, imageSize);
    free(image);
    return 1;
}

4. 使用fseeko处理大文件偏移

// 替换fseek为fseeko
if (fseeko(file, (off_t)range.start, SEEK_SET) != 0) {
    printf("fseek image %lu failed\n", i);
    free(image);
    return 1;
}

内容的提问来源于stack exchange,提问作者Ican'tThinkOfAName

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:00:58