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

BMP文件头读取函数宽高校验未通过自动化测试求助

BMP头读取函数宽高合法性测试失败排查

我定义了BMP文件头结构体bmp_header,并实现了从FILE流读取BMP头的函数read_bmp_header。函数中已添加图像宽高的合法性检查,但在自动化测试的「传入有效BMP流时,图像宽高必须合法」校验中未通过,恳请协助排查问题。相关代码如下:

BMP头结构体定义

#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

读取BMP头的函数实现

#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->planes != 1 || (header->bpp != 1 && header->bpp != 4 && header->bpp != 8 && header->bpp != 24) || header->compression != 0 || header->image_size == 0 || header->x_ppm != 0 || header->y_ppm != 0 || header->num_colors != 0 || header->important_colors != 0){
        free(header);
        return NULL;
    }

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

    return header;
}

问题排查与修复

1. 多余的宽高上限检查

width和height是uint32_t类型,其取值范围本身就是0到UINT32_MAX,所以header->width > UINT32_MAX这个条件永远为假,完全没必要保留,直接删除这段代码即可。

2. 过于严格的字段校验导致误判

代码对很多非强制字段做了严格等于0的检查,这些检查会把合法的BMP头判定为无效,进而导致测试失败:

  • header->offset !=54:BMP规范允许offset大于54(比如存在颜色表时),只需保证offset >=54即可
  • header->image_size ==0:image_size可以为0,此时实际图像大小由宽高和位深计算得出,无需强制非0
  • header->x_ppm !=0 || header->y_ppm !=0:这两个字段表示像素密度,完全可以是非0值
  • header->num_colors !=0 || header->important_colors !=0:对于8位及以下位深的BMP,这两个字段会有合法值,不需要强制为0

3. 字节序问题(关键)

BMP文件采用小端字节序存储多字节字段,但如果程序运行在大端字节序的机器上,直接读取的width、height等字段会因为字节序反转变成非法值(比如原本的正数变成超大数或0)。需要将读取的字段转换为主机字节序:

  • 对于uint16_t类型字段(type、reserved1、reserved2、planes、bpp),用le16toh()转换
  • 对于uint32_t类型字段(size、offset、dib_size、width、height等),用le32toh()转换

修复后的核心代码示例

// 读取后先处理字节序
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->dib_size != 40 || 
    header->planes != 1 || 
    !(header->bpp == 1 || header->bpp == 4 || header->bpp == 8 || header->bpp == 24) || 
    header->compression != 0 ||
    header->offset < 54) {
    free(header);
    return NULL;
}

内容的提问来源于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:25:55