Skip to content

Commit a149dce

Browse files
committed
Fix GH-22121: double-free in gdImageSetStyle() after overflow early return
gdImageSetStyle freed im->style before checking overflow2(). When the overflow check tripped and the function early-returned, im->style was left dangling. The next gdImageSetStyle, gdImageDestroy, or gdImageSetPixel gdStyled/gdStyledBrushed dispatch then freed or dereferenced it. Move the overflow check above the free to match upstream libgd (libgd/libgd src/gd.c::gdImageSetStyle), which has always had the check first. The original divergence was an oversight in 77ba248 when the overflow check was ported from libgd 2.0.29. Fixes GH-22121
1 parent 27d593e commit a149dce

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

ext/gd/libgd/gd.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2854,12 +2854,12 @@ int gdCompareInt (const void *a, const void *b)
28542854

28552855
void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
28562856
{
2857-
if (im->style) {
2858-
gdFree(im->style);
2859-
}
28602857
if (overflow2(sizeof (int), noOfPixels)) {
28612858
return;
28622859
}
2860+
if (im->style) {
2861+
gdFree(im->style);
2862+
}
28632863
im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
28642864
memcpy(im->style, style, sizeof(int) * noOfPixels);
28652865
im->styleLength = noOfPixels;

ext/gd/tests/gh22121.phpt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
GH-22121 (Double free in gdImageSetStyle() after overflow-triggered early return)
3+
--EXTENSIONS--
4+
gd
5+
--INI--
6+
memory_limit=-1
7+
--SKIPIF--
8+
<?php
9+
if (!getenv('RUN_RESOURCE_HEAVY_TESTS')) die('skip resource-heavy test');
10+
if (PHP_INT_SIZE < 8) die('skip 64-bit only (allocates ~10 GiB)');
11+
function get_system_memory(): int|float|false {
12+
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
13+
@exec('wmic OS get FreePhysicalMemory', $output);
14+
if (isset($output[1])) {
15+
return ((int)trim($output[1])) * 1024;
16+
}
17+
} else {
18+
$memInfo = @file_get_contents("/proc/meminfo");
19+
if ($memInfo && preg_match('/MemAvailable:\s+(\d+) kB/', $memInfo, $matches)) {
20+
return $matches[1] * 1024;
21+
}
22+
}
23+
return false;
24+
}
25+
if (get_system_memory() < 12 * 1024 * 1024 * 1024) {
26+
die('skip Reason: Insufficient RAM (less than 12GB)');
27+
}
28+
?>
29+
--FILE--
30+
<?php
31+
$im = imagecreatetruecolor(1, 1);
32+
imagesetstyle($im, [0]);
33+
imagesetstyle($im, array_fill(0, 536870912, 0));
34+
unset($im);
35+
echo "no double free\n";
36+
?>
37+
--EXPECTF--
38+
Warning: imagesetstyle(): Product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully
39+
in %s on line %d
40+
no double free

0 commit comments

Comments
 (0)