Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CVE-2025-24257: IOGPUFamily, iOS 18.3.2 to 18.4

This repeats the finding of the public PoC of CVE-2025-24257, discovered by Wang Yu of Cyberserval. When a resource group is created with a capacity under 64, bitmap_mask = (capacity >> 6) - 1 underflows to 0xFFFFFFFF, and the bitmap is read and written out of bounds. iOS 18.4 adds a check that the capacity is 64 or more.

Get the binaries

$ ipsw download ipsw --device iPhone15,2 --build 22D82 --kernel --output 18.3.2
$ ipsw download ipsw --device iPhone15,2 --build 22E240 --kernel --output 18.4
$ ipsw kernel extract 18.3.2/*/kernelcache.release.iPhone15,2 com.apple.iokit.IOGPUFamily --output 18.3.2
$ ipsw kernel extract 18.4/*/kernelcache.release.iPhone15,2 com.apple.iokit.IOGPUFamily --output 18.4

Compare

$ hereticus --format markdown 18.3.2/com.apple.iokit.IOGPUFamily 18.4/com.apple.iokit.IOGPUFamily
FUNCTIONCHANGESCORE+/-/~TOUCHEDMATCHED BY
sub_fffffff009a95038 -> sub_fffffff009bb22c0changed0.9396/79/1533 branch, 14 call, 17 jump, 27 mem, 34 constsimilar
…
sub_fffffff009a765c0 -> sub_fffffff009b92fa8changed0.7620/6/32 branch, 1 call, 1 jump, 4 mem, 3 const, 2 textsimilar
…

1728 matched (1304 unchanged, 424 changed), 14 gone, 8 new

43 changed only a constant, hidden. Use –show-retouched to list them.

iOS 18.4 rewrote much of the driver, so 424 functions changed. The fix is row 19.

Find the fix

--diff-all prints every diff in one run, so a search finds the new check: a compare of a count with 63 or 64.

$ hereticus --diff-all 18.3.2/com.apple.iokit.IOGPUFamily 18.4/com.apple.iokit.IOGPUFamily > diffs.txt
$ awk '/^sub_/{name=$0} /^\+ cmp [wx][0-9]+, #0x(3f|40)$/{print name": "$0}' diffs.txt
sub_fffffff009a765c0 -> sub_fffffff009b92fa8: + cmp w2, #0x3f
...

Read the fix

$ hereticus --diff sub_fffffff009a765c0 18.3.2/com.apple.iokit.IOGPUFamily 18.4/com.apple.iokit.IOGPUFamily
...
- cmp w2, #0x0
...
+ mov x21, x2
+ cmp w2, #0x3f
+ b.hi $+0x40
  mov w8, #0x40
- csel w21, w8, w2, eq
...
+ mov w2, #0x11
+ bl $-0x16727c4
+ mov x19, #0x0
+ b $+0x180
...

Before, a capacity of 0 became 64 and any other was taken as given, so a capacity of 1 to 63 went on to the bitmap. Now a capacity of 63 or less logs an error and the function returns NULL. The bitmap search that the mask bounds, sub_fffffff009a83cb0, compares it in 64 bits now too.

--view il shows the two choices plainly: the csel that took 64 for a capacity of 0, and the branch that now leaves for one of 63 or less.

$ hereticus --view il --diff sub_fffffff009a765c0 18.3.2/com.apple.iokit.IOGPUFamily 18.4/com.apple.iokit.IOGPUFamily
...
- flags = compare(w2, 0)
...
+ x21 = x2
+ flags = compare(w2, 0x3f)
+ if (w2 u> 0x3f) goto $+0x40
  w8 = 0x40
- w21 = eq ? w8 : w2
...
+ w2 = 0x11
+ call $-0x16727c4
+ x19 = 0
+ goto $+0x180
...