[Png-mng-security] potentially serious memory handling error in libpng

glennrp at comcast.net glennrp at comcast.net
Wed Feb 4 12:45:57 UTC 2009


----- Original Message ----- 
From: "Tavis Ormandy" <taviso at sdf.lonestar.org> 
To: glennrp at comcast.net, png-mng-security at simple.dallas.tx.us 
Cc: scarybeasts at gmail.com 
Sent: Tuesday, February 3, 2009 10:41:18 PM GMT -05:00 US/Canada Eastern 
Subject: potentially serious memory handling error in libpng 

Hey Glenn, Greg and others, 

I've discovered a potentially serious error in libpng, it requires an 
allocation failure at a specific time in order to work, and so probably 
explains why nobody has reported running into this by accident. 
Unfortunately, an attacker can usually create this easily (e.g. just 
preparing a highly compressable image of ridiculous size). 

The problem is in pngread.c, 

1433 if (info_ptr->row_pointers == NULL) 
1434 { 
1435 info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, 
1436 info_ptr->height * png_sizeof(png_bytep)); 
1437 #ifdef PNG_FREE_ME_SUPPORTED 
1438 info_ptr->free_me |= PNG_FREE_ROWS; 
1439 #endif 
1440 for (row = 0; row < (int)info_ptr->height; row++) 
1441 { 
1442 info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, 
1443 png_get_rowbytes(png_ptr, info_ptr)); 
1444 } 
1445 } 

Here you can see (info_ptr->free_me & PNG_FREE_ROWS) is set after the space for 
the pointers has been allocated, but _before_ the rows themselves have 
been allocated. If any of row allocations fail, a portion of the 
pointers in info_ptr->row_pointers are going to be unintialised junk, 


Rats. I liked the version number 1.2.34 as the final stable version number. 

Would this cure the defect? 

insert between 1439 and 1440 

for (row = 0; row < (int)info_ptr->height; row++) 
info_ptr->row_pointers[row] = NULL; 

Oh, never mind, your "memset" solution is more compact. 

Glenn 

(this is easy to control if an application will render two images an 
attacker will supply, the first one can poison the heap and succeed, the 
second triggers the fault, resulting in free(attacker_controlled). These 
can be exploitable (although its tricky) by setting up "fake" heap 
chunks, and pointing the system allocator at it. 

I've got a demo that works on my machine, I'm using setrlimit() to make 
allocation failure more reliable, but unless you're using the same 
distribution as me it may need tweaking, 3 required files attached. 

(note, i modified the pngs by hand so im cheating and using 
png_set_crc_action() instead of fixing them up, sorry!) 

$ gcc -ggdb3 -O0 pngdecode.c libpng-1.2.34/.libs/libpng.a -o pngdecode -lm -lz 
$ gdb -q ./pngdecode 
(gdb) r 
Breakpoint 1, png_default_error (png_ptr=0x80642d8, 
error_message=0x4b0d2bd2 "invalid literal/lengths set") at pngerror.c:218 
218 if (*error_message == '#') 
(gdb) c 
Breakpoint 1, png_default_error (png_ptr=0x80642d8, 
error_message=0x8061450 "Out of Memory!") at pngerror.c:218 
218 if (*error_message == '#') 
(gdb) bt 
#0 png_default_error (png_ptr=0x80642d8, error_message=0x8061450 "Out 
of Memory!") at pngerror.c:218 
#1 0x08056ff3 in png_error (png_ptr=0x80642d8, error_message=0x8061450 
"Out of Memory!") at pngerror.c:80 
#2 0x08056cea in png_malloc (png_ptr=0x80642d8, size=33132) at 
pngmem.c:442 
#3 0x0804c8c8 in png_read_png (png_ptr=0x80642d8, info_ptr=0x8064008, 
transforms=0, params=0x0) at pngread.c:1442 
(gdb) frame 3 
#3 0x0804c8c8 in png_read_png (png_ptr=0x80642d8, info_ptr=0x8064008, 
transforms=0, params=0x0) at pngread.c:1442 
1442 info_ptr->row_pointers[row] = 
(png_bytep)png_malloc(png_ptr, 
(gdb) p row 
$2 = 3993 
(gdb) p info_ptr->height 
$3 = 15173 

Memory allocation failed here when row < info_ptr->height, so the rest of the 
pointers in this array are unintialised junk. 

(gdb) c 
libpng error: Out of Memory! 
Program received signal SIGSEGV, Segmentation fault. 
0x48997fe9 in free () from /lib/tls/libc.so.6 
(gdb) bt 
#0 0x48997fe9 in free () from /lib/tls/libc.so.6 
#1 0x08056d97 in png_free_default (png_ptr=0x80642d8, ptr=0x5080) at pngmem.c:525 
#2 0x08056d76 in png_free (png_ptr=0x80642d8, ptr=0x5080) at pngmem.c:509 
#3 0x080494d9 in png_free_data (png_ptr=0x80642d8, info_ptr=0x8064008, mask=32767, num=-1) at png.c:571 
#4 0x080495a2 in png_info_destroy (png_ptr=0x80642d8, info_ptr=0x8064008) at png.c:598 
#5 0x0804c20b in png_read_destroy (png_ptr=0x80642d8, info_ptr=0x8064008, end_info_ptr=0x0) at pngread.c:1166 
#6 0x0804c12c in png_destroy_read_struct (png_ptr_ptr=0xffffce54, info_ptr_ptr=0xffffce50, end_info_ptr_ptr=0x0) 
at pngread.c:1107 
... 
(gdb) frame 3 
#3 0x080494d9 in png_free_data (png_ptr=0x80642d8, info_ptr=0x8064008, mask=32767, num=-1) at png.c:571 
571 png_free(png_ptr, info_ptr->row_pointers[row]); 
(gdb) p row 
$4 = 4028 
(gdb) p info_ptr->row_pointers[row] 
$5 = (png_byte *) 0x5080 <Address 0x5080 out of bounds> 

row is > 3993, where the allocation failure happened. 

I suppose the easiest fix is just to png_memset(foo, 0, n) row_pointers as soon as 
they're allocated, before free_me gets the ROW_POINTERS flag set. png_free 
handles free(NULL), so you dont have rely on the system allocator being sane. 
Oneliner below. 

$ diff -up pngread.c.orig pngread.c 
--- pngread.c.orig 2009-02-04 03:33:27.605190000 +0000 
+++ pngread.c 2009-02-04 03:34:25.383958000 +0000 
@@ -1434,6 +1434,8 @@ png_read_png(png_structp png_ptr, png_in 
{ 
info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, 
info_ptr->height * png_sizeof(png_bytep)); 
+ png_memset(info_ptr->row_pointers, 0, 
+ info_ptr->height * png_sizeof(png_bytep)); 
#ifdef PNG_FREE_ME_SUPPORTED 
info_ptr->free_me |= PNG_FREE_ROWS; 
#endif 

I'm not sure if you need additional logic to handle 16bit machines, like the 
unused logic in png_zalloc()? 

Dunno. I think my version (explicitly setting the pointers one by one) 
would be immune to pointer-size problems. 

Glenn 

Thanks, Tavis. 

-- 
------------------------------------- 
taviso at sdf.lonestar.org | finger me for my gpg key. 
------------------------------------------------------- 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osuosl.org/pipermail/png-mng-security-archive/attachments/20090204/a787ad8d/attachment.html>


More information about the png-mng-security-archive mailing list