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后访问了空指针,导致内存访问错误。同时代码存在多个逻辑缺陷:
- 校验规则过于严苛:强制要求
offset=54、dib_size=40、bpp=24、image_size≠0,但这些字段在合法BMP中允许有其他有效值(比如带颜色表的BMP offset会大于54,image_size为0是合法的); - 无意义的宽高判断:
header->width > UINT32_MAX完全无效,因为width本身是uint32_t类型,最大值就是UINT32_MAX,不可能超过; - 字节序未处理: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
相关产品推荐
相关产品推荐

