Skip to content

Fix GH-22121: double-free in gdImageSetStyle() after overflow early return#22125

Open
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/gh-22121-gd-set-style-double-free
Open

Fix GH-22121: double-free in gdImageSetStyle() after overflow early return#22125
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:fix/gh-22121-gd-set-style-double-free

Conversation

@iliaal
Copy link
Copy Markdown
Contributor

@iliaal iliaal commented May 22, 2026

gdImageSetStyle (ext/gd/libgd/gd.c:2880) frees im->style before calling overflow2(). When the overflow check trips and the function returns, im->style is left dangling and the next gdImageSetStyle, gdImageDestroy, or gdImageSetPixel gdStyled/gdStyledBrushed dispatch frees or dereferences 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 divergence was an oversight in 77ba248 when the overflow check was ported from libgd 2.0.29.

Fixes #22121

@devnexen
Copy link
Copy Markdown
Member

nice, you probably can target lower branches though.

@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented May 22, 2026

nice, you probably can target lower branches though.

@devnexen 100%, given that looks like 8.4 RC is already in-flight should I backport to 8.5 or 8.4 is still ok?

@devnexen
Copy link
Copy Markdown
Member

PHP-8.4 please

@iliaal iliaal changed the base branch from master to PHP-8.4 May 22, 2026 13:21
@iliaal iliaal changed the base branch from PHP-8.4 to master May 22, 2026 13:21
@iliaal iliaal changed the base branch from master to PHP-8.4 May 22, 2026 13:23
@iliaal iliaal force-pushed the fix/gh-22121-gd-set-style-double-free branch from e45f5f6 to 5277fc6 Compare May 22, 2026 13:23
@devnexen
Copy link
Copy Markdown
Member

devnexen commented May 22, 2026

Your change is correct but because the array building is very large (i.e. stylearr) it crashes on CI arm. Can you make the test works without such number ? if not, I see there is this possibility eventually

  if (getenv('CIRRUS_CI') && strpos($arch, 'aarch64') !== false) {
      die('xfail Broken on Cirrus + arm');
  }

@iliaal iliaal force-pushed the fix/gh-22121-gd-set-style-double-free branch from 5277fc6 to d123ddc Compare May 22, 2026 14:56
@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented May 22, 2026

Reused get_system_memory() from file_get_contents_file_put_contents_5gb.phpt in the SKIPIF with an 11 GB MemFree threshold. Couldn't shrink the test below that: overflow2 needs noOfPixels > INT_MAX/sizeof(int), and the wrapper's safe_emalloc adds 2 GB on top before the check fires. CircleCI ARM will skip; runners with the headroom still trigger it.

@devnexen
Copy link
Copy Markdown
Member

I m not sure I m liking the "workaround", probably @iluuu1994 would have a better idea how to circumvent this issue.

@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented May 22, 2026

I m not sure I m liking the "workaround", probably @iluuu1994 would have a better idea how to circumvent this issue.

Open to suggestions, but right now can't think of anything better, and aligns with other tests.

Comment thread ext/gd/tests/gh22121.phpt Outdated
}
} else {
$memInfo = @file_get_contents("/proc/meminfo");
if ($memInfo && preg_match('/MemFree:\s+(\d+) kB/', $memInfo, $matches)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MemAvailable might be a better fit (MemFree excludes reclaimable page-cache).

Comment thread ext/gd/tests/gh22121.phpt Outdated
}
return false;
}
if (get_system_memory() < 11 * 1024 * 1024 * 1024) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accounting php startup, 12 GB might be a bit more helpful, wdyt ?

@devnexen
Copy link
Copy Markdown
Member

I m not sure I m liking the "workaround", probably @iluuu1994 would have a better idea how to circumvent this issue.

Open to suggestions, but right now can't think of anything better, and aligns with other tests.

for now, let s keep trying with it.

@devnexen
Copy link
Copy Markdown
Member

I m not sure I m liking the "workaround", probably @iluuu1994 would have a better idea how to circumvent this issue.

Open to suggestions, but right now can't think of anything better, and aligns with other tests.

that was not a criticism toward you, I just wished we did not need to go to that length for such fix.

@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented May 22, 2026

I m not sure I m liking the "workaround", probably @iluuu1994 would have a better idea how to circumvent this issue.

Open to suggestions, but right now can't think of anything better, and aligns with other tests.

that was not a criticism toward you, I just wished we did not need to go to that length for such fix.

I didn't take it such, feedback is good :)

…y 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 phpGH-22121
@iliaal iliaal force-pushed the fix/gh-22121-gd-set-style-double-free branch from d123ddc to a149dce Compare May 22, 2026 16:31
@iliaal iliaal requested a review from devnexen May 22, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Double free in gdImageSetStyle()

2 participants