Abstract. CVE-2026-65395 is Apple’s ImageIO out-of-bounds write: “processing a maliciously crafted image may result in memory corruption,” fixed “with improved bounds checking.”1 On the iOS 26.4 (23E246) ImageIO, _JPEGFile::copyXMPProperties reassembles Adobe Extended XMP chunks into a heap buffer sized by the attacker’s TotalXMPLength. Before each copy it checks XmpOffset + chunkLength <= TotalXMPLength with a 32-bit ADD W, then forms the destination as buffer + (uint64_t)XmpOffset. An offset of 0xfffffff0 with a 0x20-byte chunk wraps the sum to 0x10, passes the check, and copies 32 attacker bytes to buffer + 0xfffffff0, almost 4 GiB past a 256-byte allocation. iOS 26.7 (23H24) does the add and the compare in 64 bits. Three JPEGs that differ only in that 32-bit field were replayed through the private file-copy entry on the iOS 26.4 VM: the in-bounds and rejected controls returned 3/3; the wrapping input faulted 10/10 inside the call. The same three inputs return normally on macOS 27.0, whose ImageIO has the 64-bit check. Scope: this is a component-level memory fault with a matching binary fix. The faulting PC was never captured because the device refused to store crash reports, no system app was shown to call this entry when “just opening” an image, and there is no code-execution claim.
1. Introduction
Apple credits Mateusz Jurczyk of Google Project Zero and Varik Matevosyan.123 The advisory says nothing about which ImageIO parser is involved. ImageIO in the 26.7 update grew several new size checks, so this note had to rank candidates and test them, not pick the first diff that looked like a bound.
The note is laid out as follows: where Apple lists the CVE (§2); the binaries (§3); the Extended XMP format and the vulnerable copy, with ARM64 from both sides (§4–5); how a file reaches it (§6); the paired-input replay on the vulnerable VM and the crash-report gap (§7); the fixed host (§8); two other JPEG crashes found by the same campaign (§9); candidates that were ruled out (§10); impact (§11); open items and artifacts (§12–13).
Evidence labels used throughout.
- Apple-confirmed — advisory text and which Apple security pages list the CVE.
- Patch-observed — ARM64 read on an exact, hashed ImageIO, with the build named on each side.
- Lab-observed — run on a named VM or host whose build and loaded ImageIO UUID were recorded.
- Hypothesis — inference not yet tested; never used in a conclusion.
2. Where Apple lists it
| Advisory (14 Sep 2026) | CVE-2026-65395 |
|---|---|
| iOS 27 and iPadOS 27 | present, ImageIO1 |
| iOS 26.7 and iPadOS 26.7 | present, ImageIO2 |
| macOS Golden Gate 27 | present, ImageIO3 |
| macOS Tahoe 26.7, Sequoia 15.8 | present |
| visionOS 27, tvOS 27 | present |
| watchOS 27 | absent |
| Item | Advisory text |
|---|---|
| Impact | Processing a maliciously crafted image may result in memory corruption |
| Description | An out-of-bounds write issue was addressed with improved bounds checking |
Apple does not name a last vulnerable build and does not claim code execution for this entry.
3. Binaries
All files are arm64e ImageIO extracted from a dyld shared cache. The vulnerable side is the ImageIO actually loaded in the lab Fuzzer VM (pulled over SSH from the guest’s cache), not a file assumed to match.
| Role | Build | Cache UUID | ImageIO UUID | ImageIO SHA-256 | Source ver. |
|---|---|---|---|---|---|
| Vulnerable, lab VM | iOS 26.4 / 23E246 | 5C0E73C3-4103-3A14-99F4-D0269D830F55 |
A7A1EDD0-F349-3E7E-8713-5AAA3995A5FB |
d03edda238273cc38b0fc9e7bf1d8ee6fd1c11f6cadbd635cc2dc8ba8612331a |
2784.4.10 |
| Fixed, iPhone17,3 OTA | iOS 26.7 / 23H24 | 5625EC35-B6D6-3C10-91B0-5B4C20FB77B7 |
5108CBD3-6DF2-30CA-A2FB-8B9277209C80 |
a1e992db6ed219a8e7f7b40d3c9df5ac0d4b19eb8dff8169b4fd056092d84122 |
2784.6.7 |
| Fixed, lab Mac | macOS 27.0 / 26A428 | EA2C265E-297C-39C2-8646-7D8A2DFF648A |
9F466BFF-CD36-33EB-9240-A9DC03202193 |
extract 65e76b7c3d0177c6beefd4aae9b0e7855c1f3bc4692088188b4de8c8bd595225 |
— |
| Search aid | iOS 27 beta 24A5390f → 24A5408d | — | CCD25E8D-… → DA08C738-… |
— | — |
The 23H24 OTA was fetched from Apple’s asset server (AEA SHA-256 9ca0ffdd66390ba91630b276e4133c3ca6506bee189e40c3e35602ce113f4cea). The iOS 27 beta pair is where the change was first spotted. The 26.4/26.7 pair is the one the conclusions rest on. Each UUID got its own IDA 9.4 database; nothing below mixes addresses across builds.
4. Extended XMP in one paragraph
A JPEG APP1 segment holds at most 64 KiB, so XMP packets larger than that are split. The standard XMP packet stays in a normal APP1, and the remainder goes into one or more Extended XMP APP1 segments that the reader stitches back together:4
FF E1 APP1 marker
LL LL segment length, big-endian, includes these 2 bytes
"http://ns.adobe.com/xmp/extension/\0" 35-byte signature
GUID 32 ASCII hex chars, same in every chunk of one packet
TotalXMPLength (u32 BE) size of the full reassembled packet
XmpOffset (u32 BE) where this chunk goes in that packet
chunk[LL - 2 - 35 - 32 - 8] payload
Every length and offset in that header comes from the file. A correct reader has to allocate TotalXMPLength once and check, per chunk, that XmpOffset + chunkLength does not exceed it, in arithmetic that cannot wrap.
ImageIO parses these segments in _APP1ExtendedXMP::processData and stores the GUID, the total, the offset, and the chunk as CFData/integers on the marker object. The reassembly happens later, in _JPEGFile::copyXMPProperties.
5. Root cause: a 32-bit check guarding a 64-bit address
5.1 Vulnerable side, 23E246
_JPEGFile::copyXMPProperties is 0x1865b2fa0..0x1865b3284 (image base 0x18637b000). Hex-Rays output, trimmed to the Extended XMP branch:
v10 = _APP1XMP::getXmpData(v7); // chunk CFData
GUID = _APP1ExtendedXMP::getGUID(v7);
v12 = CFDataGetBytePtr(GUID); // before NULL check (§9.1)
TotalXMPLength = _APP1ExtendedXMP::getTotalXMPLength(v7);
XmpOffset = _APP1ExtendedXMP::getXmpOffset(v7); // int, 32-bit
v16 = CFDataGetLength(v10); // chunkLength
if (GUID != 0 && CFDataGetLength(GUID) == 32) {
if (!have_buffer) { // first chunk wins
guid_saved = *(GUID bytes);
saved_total = TotalXMPLength;
buf = malloc_type_calloc(1, TotalXMPLength, 0x100004077774924);
}
if (guid matches && TotalXMPLength == saved_total
&& XmpOffset + v16 <= saved_total) // 32-bit add + compare
{
_TIFFmemcpy_0(&buf[XmpOffset], CFDataGetBytePtr(v10), v16);
total_copied += v16;
}
}
The ARM64 at the check and the copy:
0x1865b30cc MOV W1, W28 ; size = TotalXMPLength
0x1865b30d0 MOV W0, #1
0x1865b30d4 MOV X2, #0x100004077774924 ; malloc type id
0x1865b30e4 BL malloc_type_calloc
0x1865b30ec STR X0, [SP,#buf]
...
0x1865b312c LDR W9, [SP,#saved_total]
0x1865b3130 CMP W28, W9 ; total == first chunk's
0x1865b3134 B.NE skip
0x1865b3138 ADD W8, W25, W27 ; W25 offset + W27 len, wraps
0x1865b313c CMP W8, W9
0x1865b3140 B.HI skip
0x1865b3144 LDR X8, [SP,#buf]
0x1865b3148 ADD X21, X8, W25, UXTW ; dest = buf + (u64)offset
0x1865b314c MOV X0, X26
0x1865b3150 BL CFDataGetBytePtr ; src = chunk bytes
0x1865b3154 MOV X1, X0
0x1865b3158 MOV W2, W27 ; len = chunkLength
...
0x1865b3160 BL __TIFFmemcpy_0
The whole bug is the pair ADD W8, W25, W27 and ADD X21, X8, W25, UXTW. The bound is evaluated modulo 2^32; the address is not.
5.2 What the trigger does, in numbers
| Field | Value | Note |
|---|---|---|
TotalXMPLength |
0x00000100 |
calloc(1, 0x100): one 256-byte block |
XmpOffset |
0xfffffff0 |
|
chunkLength |
0x20 |
|
W8 = XmpOffset + chunkLength (32-bit) |
0x00000010 |
wraps |
check 0x10 <= 0x100 |
passes | |
| destination | buf + 0xfffffff0 |
4 GiB − 16 bytes past a 256-byte block |
| bytes written | 32, attacker-chosen | the chunk payload |
The offset and length are each attacker-chosen within their 32-bit fields, so the destination is anywhere in [buf, buf + 4 GiB) and the write is up to ~64 KiB per chunk (one APP1 segment). Several Extended XMP segments with the same GUID and total each get their own copy, so one file can carry several writes. What sits 4 GiB after a small calloc block depends on the malloc layout; in the runs below it was usually unmapped or protected, which is why the observable is a fault rather than silent corruption (§7).
5.3 Fixed side, 23H24
Same function, 0x1865fb33c..0x1865fb650 (image base 0x1863c3000):
0x1865fb4cc LDR W8, [SP,#saved_total]
0x1865fb4d0 CMP W26, W8
0x1865fb4d4 B.NE skip
0x1865fb4d8 MOV W21, W21 ; zero-extend chunkLength into X21
0x1865fb4dc ADD X8, X21, W28, UXTW ; 64-bit: chunkLength + XmpOffset
0x1865fb4e0 CMP X8, W26, UXTW ; 64-bit compare against total
0x1865fb4e4 B.HI skip
0x1865fb4e8 MOV W25, W28
...
0x1865fb504 BL __TIFFmemcpy_0
0xfffffff0 + 0x20 = 0x1_0000_0010 > 0x100, so the wrapped chunk is skipped before the destination is formed. 23H24 also moves the GUID/XMP-data NULL checks ahead of CFDataGetBytePtr, which is the fix for the separate NULL dereference in §9.1. The iOS 27 beta pair shows the identical change (ADD W8,W25,W27 at 0x187aa7bac in 24A5390f; ADD X8,X21,W28,UXTW at 0x187b1dc4c in 24A5408d).
6. How bytes reach it
copyXMPProperties is not on the decode path. IDALib finds two callers on 23E246:
| Caller | Site |
|---|---|
CGImageCopyFileWithParameters |
0x1865b6a84 (after _JPEGWriter::processInput at 0x1865b6690) |
IIOMetadataUpdater::createModifiedJPEGWithDateTime |
0x186615da0 |
Both are rewrite paths: ImageIO copies an existing JPEG into a new file while changing metadata, and carries the existing XMP across by reassembling it. CGImageCopyFileWithParameters is a private, exported ImageIO function; the harness resolves it with dlsym and asks it to set a keyword, which forces the XMP rewrite:
copy_file = dlsym(RTLD_DEFAULT, "CGImageCopyFileWithParameters");
keywords_key = *(CFStringRef *)dlsym(RTLD_DEFAULT, "kCGImageCopyFileSetKeywords");
params = @{ keywords_key : @[ @"cve65395" ] }; // CF in the real harness
CFDataRef input = CFDataCreate(NULL, data, size); // the JPEG under test
copy_file(input, CFSTR("/private/var/tmp/…/out.jpg"), params);
The hot path is only CFDataCreate → CGImageCopyFileWithParameters → output check. No CGImageSource decode or render runs first, so a fault cannot come from an unrelated parser.
Not shown: a system caller. A bind-symbol search of the 23G90 dyld cache found no importer of CGImageCopyFileWithParameters in Photos, Quick Look, or the import stack. createModifiedJPEGWithDateTime looks like what a “change date and time” edit would use (Hypothesis), but no consumer was traced to it. dlsym and non-cache executables could still reach either function, so “not found” is not “absent.”
7. Replay on the vulnerable VM (23E246)
7.1 Inputs
All three files are the same 5391-byte JPEG with one Extended XMP APP1 inserted right after SOI by build_extended_xmp_jpeg.py; each is 5502 bytes. GUID, total, chunk payload, and every other byte are equal. Only the 32-bit XmpOffset differs.
| Input | total | offset | chunk | 32-bit sum | SHA-256 |
|---|---|---|---|---|---|
| exact-fit | 0x100 |
0xe0 |
0x20 |
0x100 |
77d07fbe143b3c4dcc75044c2ad7868ebba8e49293211d1b084dd8293ba7e69b |
| guard-reject | 0x100 |
0xf0 |
0x20 |
0x110 |
b354b232cd95b14457909c1ca87e33ebf3cce016923960ef2f03a37bf95c0879 |
| offset-wrap | 0x100 |
0xfffffff0 |
0x20 |
0x10 |
b34c86a212cf8e27e6bd4c0e09abd2b5ffda296cae2cac510d3a1096965e78d7 |
cmp -l guard-reject.jpg trigger.jpg shows three differing bytes (78–80, 1-based); byte 81 is 0xf0 in both. Any behavioral difference between those two files is caused by the high 24 bits of the offset, which only matter to the 32-bit add.
7.2 Environment
| Item | Value |
|---|---|
| VM | vphone-based Fuzzer VM, iPhone17,3, no SpringBoard |
| OS / build | iOS 26.4 / 23E246, guest id 504edb8ca1d1dd05eacbe1ce9daa2082 |
| Loaded ImageIO | UUID A7A1EDD0-…, SHA-256 d03edda2… (§3) |
| Harness | imageio_xmp_harness.c, SHA-256 2e46ecf4488104edab6c25420b4993183d01f1fbfaef5ab19aa0b833d3dd1eed; signed build 6ca47c7c42b215fc2c9c9a00b0178edb48e66c08434611ddf3d906c43db2659e |
| Run / revision | 1876977018355856 / 89, native forkserver, 1 worker |
| Coverage module | CGImageCopyFileWithParameters@ImageIO |
Replays use a fresh loader process each time. The harness prints COPY_ENTER before the private call and COPY_RETURN after it.
7.3 Result
| Input | Fresh runs | Outcome |
|---|---|---|
| exact-fit | 3 | 3× exit 0, COPY_RETURN output_created=1 |
| guard-reject | 3 | 3× exit 0, COPY_RETURN output_created=1 |
| offset-wrap | 10 | 10× killed inside the call: 7× SIGBUS, 3× SIGSEGV; every run has COPY_ENTER and none has COPY_RETURN |
That is the expected pattern for §5: the in-bounds copy and the copy the 32-bit check rejects both complete; the one whose wrapped sum passes the check dies inside the copy. The SIGBUS/SIGSEGV split is consistent with a destination 4 GiB out that lands on a protected or unmapped region depending on layout.
7.4 Where the crash reports went
The ten corpses were not lost to tooling. The device’s raw unified log (/private/var/db/diagnostics, 625 MB, pulled by rsync) has, for each of the ten PIDs (16307, 16310–16312, 16314, 16317, 16319, 16325, 16329, 16330):
kernel Corpse allowed …
ReportCrash Parsing corpse data for pid …
ReportCrash Formulating fatal 309 report for …
osanalyticshelper … not saved because the limit of 25 logs has been reached
So ReportCrash built a fatal report every time and the publisher dropped it at a 25-report quota that earlier campaign crashes had already used. Two of the PIDs (16307, 16314) also logged libsystem_malloc: memory corruption of free block. There is still no .ips for these runs, so no fault PC, no faulting thread, and no ImageIO frame. That is the main gap in this note.
7.5 What the history taught
Earlier attempts are worth recording because each one looked like a result and was not:
- revision 44–45: the same trigger in a harness that also decoded and rendered the image faulted 5 of 6 times, then returned normally 3 of 3 after one rebuild. The mixed outcome was heap-layout noise from the extra work, not a fix.
- revisions 50–53: a standalone
malloc_type_calloc(0x100)+memcpy(base + 0xfffffff0, …, 0x20)probe faulted 2 of 3. The fuzzer’s coverage map stayed at size 1 for control and trigger alike; the cause was theNO_FORKSRVexecution mode, not the target. Coverage only became real (initial map 3887, 7.90% at stop) with the native forkserver in revision 89. - AFL artifacts: revision 89 saved 39 crash files. 23 still parse as Extended XMP and none satisfies the wrap predicate
offset + chunk > 0xffffffff && sum32 <= total. They are not counted as reproductions.
8. Fixed host: macOS 27.0 / 26A428
The lab Mac’s ImageIO (cache image, UUID 9F466BFF-…) has the 64-bit form:
0x18e13d284 MOV W21, W21
0x18e13d288 ADD X8, X21, W28, UXTW
0x18e13d28c CMP X8, W26, UXTW
0x18e13d290 B.HI reject
0x18e13d294 MOV W8, W28
0x18e13d298 LDR X9, [SP,#0x10]
0x18e13d29c ADD X25, X9, X8 ; dest formed after check
...
0x18e13d2b4 BL copy
The same three files, run five times each in fresh processes through the same private entry with DYLD_PRINT_LIBRARIES=1 confirming the ImageIO UUID: 15/15 exit 0 with source=1 copy_entry=1 copy_invoked=1 output_created=1 properties=1. This shows the trigger file is well-formed enough to go through the rewrite and that the fixed check stops it. It is not yet the same harness on iOS 26.7; that is open (§12).
9. Two other JPEG crashes from the same entry
The directed campaign (revision 91, 73 artifacts) produced two stable fingerprints with matching .ips. Neither is the out-of-bounds write Apple describes, and neither is used as evidence for this CVE. Both are recorded because they are fixed in the same 23H24 ImageIO.
9.1 Truncated Extended XMP marker: NULL dereference
One byte changed from the exact-fit control (offset 5: segment length 0x006d → 0x002a), so the GUID never gets parsed. Three fresh replays, three .ips with the same stack:
EXC_BAD_ACCESS / SIGSEGV / KERN_INVALID_ADDRESS at 0x0
CoreFoundation __CF_IS_OBJC
CoreFoundation CFDataGetBytePtr+36
ImageIO _JPEGFile::copyXMPProperties+224
ImageIO CGImageCopyFileWithParameters+1904
+224 is the return address of CFDataGetBytePtr(GUID) at +0xdc, which on 23E246 runs before the GUID != 0 check (§5.1). 23H24 moves the check first. Denial of service only.
9.2 Exif IFD: source-side out-of-bounds read in _TAG::writeToBuffer
AFL corrupted the ExifIFDPointer entry of IFD0 (count 1 → 4, value 0x1a → 0x7e040100). A minimized input that changes only those five bytes (a84399832c57db87a9748b9202946f3244f8adf0cc22df0192fd6f25338797ac) faults 3/3:
ImageIO _TAG::writeToBuffer+352 ; 0x1865bc4c0: LDR W9, [X1], #4
ImageIO _TAGList::writeToBuffer+136
ImageIO _APP1::writeToStream+1196
ImageIO _JPEGFile::writeToStream+424
ImageIO _JPEGWriter::writePropertiesAndData+124
ImageIO _JPEGWriter::writeToPath
ImageIO CGImageCopyFileWithParameters+3260
On 23E246 the source pointer is tiffBase + tiffStart + tagOffset with no upper bound. 23H24 adds one:
0x186604880 ADD X11, X9, X10
0x186604884 ADD X12, X8, X11
0x186604888 LDR X11, [X21,#0x10] ; jpegDataSize
0x18660488c CMP X12, X11
0x186604890 B.LS valid_source ; else "out-of-bounds source"
This is a clean crash-plus-fix pair, but it is a read, and Apple’s text says write.
10. Candidates ruled out
Each of these is a real 26.7/iOS 27 ImageIO change that also widens a size computation. Each was tested and failed reachability.
KTX / BC texture writer. BCWritePlugin::writeOne on 24A5390f and 23G90 sizes its output from a truncated block count:
0x187a4ea60 MUL X8, X8, X0 ; rowStride = blocksX * blockBytes
0x187a4ea64 MUL W9, W28, W26 ; blocksX * blocksY, 32-bit
0x187a4ea68 MUL X26, X9, X0 ; allocSize = truncated * blockBytes
0x187a4ea7c BL calloc
24A5408d uses UMULL X8, W28, W8 and checks UMULH X9, X8, X0 before the final multiply. A 2564 × 26801668 image is 641 × 6700417 = 2^32 + 1 blocks, so the old code would allocate one block and then write rows of 641. The writer is only reached when a caller creates a KTX destination and asks for the BC encoder. JPEG, PNG, and a Leica M8 DNG were converted to KTX/BC on the VM through public API, but every way of getting a huge image to it failed first: the DNG’s 320×240 IFD0 preview is what gets encoded, editing the RAW SubIFD or using 1,675,105 repeated strips is rejected before CGImage creation, CIRAWFilter clamps a huge DefaultCropSize, and HEIC ispe edits create a large logical image that never reaches the encoder. A viewer-equivalent decode-and-draw harness never creates a KTX destination at all.
IOSurface allocation guard. 24A5408d adds UMULH multiply checks and rowBytes/allocSize <= INT32_MAX caps in IIO_CreateSurfaceWithFormat and the planar helpers. But CopyIOSurfaceProc asks the read plugin canHandleRemoteIOSurface() first, and in all 28 bundled read-plugin vtables on both the old and fixed ImageIO that slot is the base implementation that returns 0. The remote-decoder branch is taken and IIO_CreateSurfaceWithFormat is never called; a JPEG provider control confirmed it.
JPEG marker-length over-read on the current Mac. Found during this audit on 26A428, it has no CVE mapping and is tracked as a separate lab record. It is not evidence for this CVE.
11. Impact
- What the bug gives: a write of up to one APP1 payload of attacker bytes at an attacker-chosen offset in
[0, 4 GiB)from a small, attacker-sized calloc block, repeatable per Extended XMP segment, in whatever process rewrites the JPEG’s metadata. - What limits it: the offset is relative to an unknown heap address; there is no read-back; and the destination is far from the allocation unless the offset is small, in which case the 32-bit check does not wrap. Turning this into control flow would need heap layout knowledge the bug does not supply.
- Who calls it: not established. The evidence covers the private copy entry only.
Apple’s wording (“memory corruption”) matches what was observed. Nothing here supports a stronger claim.
12. Open items
- A target-side marker on
0x1865b3138/__TIFFmemcpy_0with the live offset, length, total, and destination, and a stored.ips(fresh loader identity or external crash trace) with the fault PC and ImageIO frame. - The same harness, three inputs, and markers on 23H24, showing the trigger hits the 64-bit reject and returns.
- A system consumer of
CGImageCopyFileWithParametersorcreateModifiedJPEGWithDateTime, found dynamically rather than by static binds.
13. Record and artifacts
| Item | Result | Label |
|---|---|---|
| Fix | ADD W/CMP W → ADD X/CMP X in copyXMPProperties, 23E246 → 23H24 |
Patch-observed |
| Vulnerable replay | exact-fit 3/3 return, guard-reject 3/3 return, offset-wrap 10/10 fault in call | Lab-observed |
| Crash reports | 10 type-309 reports built, all dropped at a 25-log quota; no PC | Lab-observed (gap) |
| Fixed host | macOS 26A428: 64-bit check; 15/15 return | Patch- + Lab-observed |
| Side findings | NULL deref (§9.1), _TAG::writeToBuffer OOB read (§9.2), both fixed in 23H24 |
Lab- + Patch-observed |
| KTX/BC, IOSurface | real patches, not reachable with any input tried | Patch-observed, ruled out |
| Viewer trigger, code execution | not shown | — |
| Artifact | SHA-256 |
|---|---|
harness/imageio_xmp_harness.c |
2e46ecf4488104edab6c25420b4993183d01f1fbfaef5ab19aa0b833d3dd1eed |
| signed harness (deployed) | 6ca47c7c42b215fc2c9c9a00b0178edb48e66c08434611ddf3d906c43db2659e |
| macOS smoke binary | 36971c24a9a4def777c423a0254a8033b505737f47d7f8d2a3d78e8beab50203 |
boundary-exact-fit.jpg |
77d07fbe143b3c4dcc75044c2ad7868ebba8e49293211d1b084dd8293ba7e69b |
guard-reject.jpg |
b354b232cd95b14457909c1ca87e33ebf3cce016923960ef2f03a37bf95c0879 |
trigger-offset-wrap.jpg |
b34c86a212cf8e27e6bd4c0e09abd2b5ffda296cae2cac510d3a1096965e78d7 |
tag-oob-read-minimal.jpg |
a84399832c57db87a9748b9202946f3244f8adf0cc22df0192fd6f25338797ac |
NULL-deref .ips ×3 |
24162d3b…6e0e, c22f38c1…f83b, 5d600067…d220 |
_TAG OOB-read .ips ×3 |
f77d0a90…fa59, bc8fc3db…6dc, e0e275f9…d14e |
[1] Apple, “About the security content of iOS 27 and iPadOS 27,” 14 September 2026. https://support.apple.com/en-us/149034
[2] Apple, “About the security content of iOS 26.7 and iPadOS 26.7,” 14 September 2026. https://support.apple.com/en-us/149041
[3] Apple, “About the security content of macOS Golden Gate 27,” 14 September 2026. The same ImageIO entry is on the Tahoe 26.7, Sequoia 15.8, visionOS 27, and tvOS 27 pages. https://support.apple.com/en-us/149035
[4] Adobe, “XMP Specification Part 3: Storage in Files,” section on JPEG and extended XMP. https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart3.pdf