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

如何解决libpng释放变量时出现的内存损坏错误

libpng加载PNG图像释放内存崩溃问题

我在C语言中使用libpng加载png图像并提取像素数据。
我的代码参考了如下示例:

http://zarb.org/~gc/html/libpng.html

我修改了代码将像素数据提取到数组中,代码如下:

int total_size = 0;
row_pointers   = (png_bytep*) malloc(sizeof(png_bytep) * image->height);
int* line_size = (int*) malloc(sizeof(int) * image->height);
for (unsigned int y = 0; y < image->height; y++) {
  int pixel_row_nb = png_get_rowbytes(png, info);
  printf("malloc %d \n", pixel_row_nb);
  total_size += pixel_row_nb;
  line_size[y]    = pixel_row_nb;
  row_pointers[y] = (png_bytep) malloc(pixel_row_nb * sizeof(png_byte));
}
printf("total %d vs %d \n", total_size, image->height * image->width * 4);

png_set_bgr(png);

png_read_image(png, row_pointers);
png_destroy_read_struct(&png, &info, NULL);
fclose(fp);

for (unsigned int y = 0; y < image->height; y++) {
  if (y == 0) {
    strncpy((void*) image->data, (void*) row_pointers[y], line_size[y]);
  } else {
    strncat((void*) image->data, (void*) row_pointers[y], line_size[y]);
  }
}

// 现在释放png相关数据
for (unsigned int y = 0; y < image->height; y++) { free(row_pointers[y]); } /// 此处崩溃
free(row_pointers);

执行到上述标记的释放行时报内存损坏错误,报错栈如下:

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51  ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) where
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff6eb48b1 in __GI_abort () at abort.c:79
#2  0x00007ffff6efd907 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff702adfa "%s\n")
    at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007ffff6f0497a in malloc_printerr (str=str@entry=0x7ffff702cad0 "double free or corruption (out)")
    at malloc.c:5350
#4  0x00007ffff6f0bee5 in _int_free (have_lock=0, p=0x555557059aa0, av=0x7ffff725fc40 <main_arena>)
    at malloc.c:4278
#5  __GI___libc_free (mem=0x555557059ab0) at malloc.c:3124
#6  0x000055555556665f in utils_image_load (image=0x55555702c190, 
    image_path=0x7fffffffc2d0 "/home/xav/data/image.png") at src/utils.c:216

我不清楚错误原因。我首先注意到行宽和计算值不一致,这是正常现象吗?我为此新增了line_size数组,起初我以为这是内存损坏的原因,但排查后发现并非如此。


问题原因与解决方案

行宽不一致问题

png_get_rowbytes返回的行实际字节数和width * 4不一致属于正常现象,libpng会默认按照字节对齐规则对行尾做填充,你通过png_get_rowbytes获取实际行大小的操作是正确的,不是内存崩溃的诱因,注意申请image->data缓冲区时要按实际累加的total_size申请,不要硬编码为height * width *4避免缓冲区不足。

内存崩溃原因

崩溃的根本原因是误用了面向字符串的操作函数处理二进制像素数据:

  • strncpy、strncat的逻辑都是以0x00作为字符串终止标记,像素数据中存在大量0x00值(比如透明通道、暗色像素),会导致函数提前终止拷贝
  • strncat执行时会先遍历目标缓冲区寻找\0结束符再追加内容,但image->data是像素缓冲区没有字符串结束标记,会直接越界写入,破坏malloc在堆上维护的内存块元数据,后续调用free时检测到元数据损坏就触发了崩溃。

修复方法

替换字符串操作函数为二进制内存拷贝函数memcpy即可,逐行拷贝时手动计算目标偏移量:

unsigned char* dst = image->data;
for (unsigned int y = 0; y < image->height; y++) {
    memcpy(dst, row_pointers[y], line_size[y]);
    dst += line_size[y];
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 18:51:04