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

read_bmp_header函数宽高校验失效且触发段错误求助

BMP头读取函数宽高校验测试失败(段错误排查)

问题描述

我编写的read_bmp_header函数无法通过合法BMP流的宽高合法性校验测试,测试用例要求:给定合法的BMP流,调用函数后,图像宽度和高度必须合法。执行测试时出现段错误,程序返回码不符合预期,无法定位问题根源。

bmp.h 文件内容

#ifndef _BMP_H
#define _BMP_H

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>


#define PADDING_CHAR "\0"

/**
 * Structure contains information about the type, size, layout, dimensions
 * and color format of a BMP file. Size of structure is 54 bytes.
*/
struct bmp_header{
    uint16_t type;              // "BM" (0x42, 0x4D)
    uint32_t size;              // file size
    uint16_t reserved1;         // not used (0)
    uint16_t reserved2;         // not used (0)
    uint32_t offset;            // offset to image data (54B)
    uint32_t dib_size;          // DIB header size (40B)
    uint32_t width;             // width in pixels
    uint32_t height;            // height in pixels
    uint16_t planes;            // 1
    uint16_t bpp;               // bits per pixel (1/4/8/24)
    uint32_t compression;       // compression type (0/1/2) 0
    uint32_t image_size;        // size of picture in bytes, 0
    uint32_t x_ppm;             // X Pixels per meter (0)
    uint32_t y_ppm;             // X Pixels per meter (0)
    uint32_t num_colors;        // number of colors (0)
    uint32_t important_colors;  // important colors (0)
} __attribute__((__packed__));


/**
 * Reads BMP header from input stream
 *
 * Reads and returns BMP header from opened input stream. The header is located
 * at it's beginning. If the stream is not opened or it is corrupted, function
 * returns `NULL`.
 *
 * @param stream opened stream, where the image data are located
 * @return `bmp_header` structure or `NULL`, if stream is not open or broken
 */
struct bmp_header* read_bmp_header(FILE* stream);
#endif

我的read_bmp_header实现

#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include "bmp.h"
#include <string.h>
#include <stdlib.h>
#include <math.h>

struct bmp_header* read_bmp_header(FILE* stream){
    //检查文件是否成功打开
    if(stream == NULL) return NULL;

    //创建用于存储BMP头的结构体
    struct bmp_header* header = (struct bmp_header*)malloc(sizeof(struct bmp_header));
    if(header == NULL) return NULL;

    //检查文件头数据是否读取成功
    size_t succes_read = fread(header, sizeof(struct bmp_header), 1, stream);
    
    if(succes_read != 1){
        free(header);
        return NULL;
    }
    
    if(header->type != 0x4D42 || header->width == 0 || header->height == 0 || header->offset != 54 || header->dib_size != 40 || header->bpp != 24 || header->image_size == 0){
        free(header);
        return NULL;
    }

    if(header->width > UINT32_MAX || header->height > UINT32_MAX){
        free(header);
        return NULL;
    }

    return header;
}

测试失败输出

Given the valid BMP stream When the function is invoked Then the image width must be valid And the image height must be valid
STDOUT: Running suite(s): read_bmp_header()
Reasons of failure:
Segmentation fault
Return code of executed application is different than expected.

问题分析与修复方案

核心问题

段错误的直接诱因是函数错误地将合法BMP判定为无效并返回NULL,测试代码在接收NULL后访问了空指针,导致内存访问错误。同时代码存在多个逻辑缺陷:

  1. 校验规则过于严苛:强制要求offset=54、dib_size=40、bpp=24、image_size≠0,但这些字段在合法BMP中允许有其他有效值(比如带颜色表的BMP offset会大于54,image_size为0是合法的);
  2. 无意义的宽高判断:header->width > UINT32_MAX完全无效,因为width本身是uint32_t类型,最大值就是UINT32_MAX,不可能超过;
  3. 字节序未处理:BMP文件采用小端字节序存储多字节字段,若运行在大端系统上,读取的字段值会完全错误,导致误判。

修复后的函数实现

#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include "bmp.h"
#include <string.h>
#include <stdlib.h>
#include <endian.h> // 用于字节序转换函数

struct bmp_header* read_bmp_header(FILE* stream){
    if(stream == NULL) return NULL;

    struct bmp_header* header = (struct bmp_header*)malloc(sizeof(struct bmp_header));
    if(header == NULL) return NULL;

    size_t success_read = fread(header, sizeof(struct bmp_header), 1, stream);
    if(success_read != 1){
        free(header);
        return NULL;
    }

    // 将小端字节序转换为主机字节序
    header->type = le16toh(header->type);
    header->size = le32toh(header->size);
    header->reserved1 = le16toh(header->reserved1);
    header->reserved2 = le16toh(header->reserved2);
    header->offset = le32toh(header->offset);
    header->dib_size = le32toh(header->dib_size);
    header->width = le32toh(header->width);
    header->height = le32toh(header->height);
    header->planes = le16toh(header->planes);
    header->bpp = le16toh(header->bpp);
    header->compression = le32toh(header->compression);
    header->image_size = le32toh(header->image_size);
    header->x_ppm = le32toh(header->x_ppm);
    header->y_ppm = le32toh(header->y_ppm);
    header->num_colors = le32toh(header->num_colors);
    header->important_colors = le32toh(header->important_colors);

    // 仅校验核心必要字段
    if(header->type != 0x4D42 || header->width == 0 || header->height == 0 || header->planes != 1){
        free(header);
        return NULL;
    }

    return header;
}

关键修改说明

  • 移除了对offset、dib_size、bpp、image_size的强制校验,仅保留BMP类型、宽高、planes的核心校验;
  • 添加了字节序转换,确保在任何字节序的系统上都能正确读取字段值;
  • 删除了无意义的宽高范围判断;
  • 修正了变量名拼写错误(succes_read改为success_read)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:44:54