Details
[Home]
Issue of the Implementation # S0683
Brief
Not enough memory is allocated for AND-mask when a pixbuf is being saved in "ico" format
Detailed Description
An "ico" file contains both a pixmap with colors of pixels (XOR-mask) and also another pixmap (AND-mask) with pixels' transparency data. In the latter pixmap 1 bit is allocated for each pixel of pixbuf.
According to the source code of the function (file: gtk+-2.14.4/gdk-pixbuf/io-ico.c)
static gboolean fill_entry (IconEntry *icon, GdkPixbuf *pixbuf, gint hot_x, gint hot_y, GError **error)
rowstride of AND-mask is set up equal to (width / 8), where width is the width of the pixbuf. It is only aligned at 4 bytes boundary:
icon->and_rowstride = icon->width / 8; if ((icon->and_rowstride % 4) != 0) icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1); icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
When the width of the pixbuf is not a multiple of 8, writing past the end of the line occurs. For the last line it can result in writing outside of the allocated memory buffer for the AND-mask.
There are two functions in gdk-pixbuf implemented via fill_entry:
The example below shows possible consequences of incorrect allocation of memory in gdk_pixbuf_save - from incorrect interpretation (by means of gdk_pixbuf_new_from_file) of the saved ico-file, up to segmentation fault.
Problem location(s) in the standard
Linux Standard Base Desktop Specification 3.2, section 15.2.1.1 - "Interfaces for GTK General purpose utility library" that refers Gdk-pixbuf 2.6.2 API Reference, File Saving
Example
#include <gdk-pixbuf/gdk-pixbuf.h> #define WIDTH 33 #define HEIGHT 33 /* * Another combinations of WIDTH and HEIGHT * on particular system (Ubuntu 7.04 on x86): * 31*31, 29*31 - everything seems to be correct * (and-rowstride increased due to alignment at 4 byte boundary). * 33*31 - Segmentation fault * 35*33 - free(): invalid next size (normal) */ #define DEPTH "24" unsigned char get_transparency(int x, int y) { //"striped" image return (x & 4) ? 255 : 0; } int main() { g_type_init(); GError *error = NULL; // Create new pixbuf, GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, WIDTH, HEIGHT); // fill pixbuf, { unsigned char* pixels = gdk_pixbuf_get_pixels(pixbuf); int rowstride = gdk_pixbuf_get_rowstride(pixbuf); int x = 0; for(; x < WIDTH; x++) { int y = 0; for(; y < HEIGHT; y++) { unsigned char* pixel = &pixels[y * rowstride + x * 4]; //monochrome black pixel[0] = 0x0;//red pixel[1] = 0x0;//green pixel[2] = 0x0;//blue //but striped by transparency pixel[3] = get_transparency(x, y);//alpha } } } // save as "ico", // this call to gdk_pixbuf_save() causes problems gdk_pixbuf_save(pixbuf, "image1.ico", "ico", NULL, "depth", DEPTH, (char*)NULL); // and save as "png". gdk_pixbuf_save(pixbuf, "image1.png", "png", NULL, (char*)NULL); g_object_unref(pixbuf); // Load pixbuf from "ico" file, GdkPixbuf *pixbuf1 = gdk_pixbuf_new_from_file("image1.ico", &error); if(pixbuf1 == NULL) { printf("Cannot load pixbuf: %s. ", error->message); return 1; } // and save it as "png". gdk_pixbuf_save(pixbuf1, "image2.png", "png", NULL, (char*)NULL); // Verify that pixbuf remain striped { if(!gdk_pixbuf_get_has_alpha(pixbuf1)) { printf("Loaded pixbuf hasn't alpha channel " "- cannot verify result. "); return 2; } unsigned char* pixels = gdk_pixbuf_get_pixels(pixbuf1); int rowstride = gdk_pixbuf_get_rowstride(pixbuf1); int x = 0; for(; x < WIDTH; x++) { int y = 0; for(; y < HEIGHT; y++) { unsigned char* pixel = &pixels[y * rowstride + x * 4]; //verify transparency unsigned char et = get_transparency(x, y); if(((et >= 128) && (pixel[3] < 128)) || ((et < 128) && (pixel[3] >= 128))) { printf("Transparency of pixel " "at (%d, %d) was %d, ", x, y, et); printf("but became %d. ", pixel[3]); break; } } if(y != HEIGHT) break; } if(x == WIDTH) printf("Transparency remains correct. "); } g_object_unref(pixbuf1); return 0; }
Component
gtk-gdk-pixbuf 2.6.2 or later
Accepted
Gnome Bugzilla 561669
Status
Fixed in gtk-gdk-pixbuf 2.19.1
[Home]