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,此时实际图像大小由宽高和位深计算得出,无需强制非0header->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
相关产品推荐
相关产品推荐

