Подробности
[В начало]
Проблема в реализации № S0683
Краткое описание
При сохранении изображения в формате "ico", под AND-mask'у выделяется недостаточно памяти
Подробное описание
В файле формата "ico" помимо обычного bitmap'а, содержащего информацию о пискелах изображения (т.н XOR-маска), есть bitmap (т.н AND-маска), содержащий краткую информацию о прозрачности пикселов. В последнем bitmap'е для каждого пиксела изображения отводится один бит.
Судя по коду функции (файл 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)
размер строки AND-маски устанавливается равным (width / 8), где width - ширина исходного изображения. Производится только выравнивание на 4 байта:
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);
Соответственно, при ширине изображения, не кратной 8, будет запись за границу строки. А для последней строки - выход за границу выделенной памяти.
Через функцию fill_entry реализованы следующие функции из gdk-pixbuf:
Приведенный пример показывает возможные последствия некорректного выделения памяти в функции gdk_pixbuf_save - от неверной интерпретации (посредством gdk_pixbuf_new_from_file) сохраненного "ico" файла до segmentation fault.
Раздел стандарта
Linux Standard Base Desktop Specification 3.2, section 15.2.1.1 - "Interfaces for GTK General purpose utility library", который ссылается на Gdk-pixbuf 2.6.2 API Reference, File Saving
Пример
#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;
}
Компонент
gtk-gdk-pixbuf 2.6.2 or later
Принято
Gnome Bugzilla 561669
Статус
Исправлено в gtk-gdk-pixbuf 2.19.1
[В начало]