DF-1216 / harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | /* * DF-1216 harness โ METEORSETGEO + METEORSACTPIXFMT pixel-format mismatch * * The guest has no bt848 PCI video-capture device (QEMU does not emulate it), * so /dev/bktr0 cannot be opened and the in-kernel path cannot be triggered * on this host. This harness instead extracts the *exact buggy arithmetic* * from sys/dev/video/bktr/bktr_core.c and demonstrates that the OOB read/ * write offset is real and attacker-controlled: * * METEORSETGEO line 1657: bigbuf = rows*cols*frames*2 (hardcoded *2) * METEORSACTPIXFMT line 2333: bktr->pixfmt = arg; (no realloc) * video_read line 1084: count = rows*cols*pixfmt_table[pixfmt].Bpp * video_read line 1106: uiomove(bigbuf, count, uio) (OOB read) * start_capture line 3521: bzero(bigbuf, rows*cols*frames*Bpp) (OOB write) * * It uses the genuine pixfmt_table from bktr_core.c:220-238 and the genuine * METEOR_GEO_YUV_422 -> pixfmt[10] (Bpp=2) -> METEORSACTPIXFMT(5) (Bpp=4) * sequence the in-kernel bug uses. Build with `cc -O2 -Wall -o harness * harness.c`; the bug is signalled by the program returning non-zero and * printing OOB=positive. */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* Subset of pixfmt_table from sys/dev/video/bktr/bktr_core.c:220 */ struct meteor_pixfmt { int type; int Bpp; }; static const struct meteor_pixfmt pixfmt_table[] = { { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, /* 0..3 RGB16 */ { 0, 3 }, /* 4 RGB24 */ { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, /* 5..8 RGB32 */ { 1, 2 }, { 2, 2 }, { 3, 2 } /* 9..11 YUV* */ }; #define PIXFMT_TABLE_SIZE (sizeof(pixfmt_table)/sizeof(pixfmt_table[0])) #define PAGE_SIZE 4096 #define btoc(x) (((x) + PAGE_SIZE - 1) / PAGE_SIZE) /* Minimal bktr state โ only fields touched by the cited arithmetic. */ struct bktr { int rows, cols, frames; int alloc_pages; /* in pages */ unsigned char *bigbuf; /* alloc_pages * PAGE_SIZE bytes */ int pixfmt; }; /* METEORSETGEO body from sys/dev/video/bktr/bktr_core.c:1657-1682 (verbatim * arithmetic; kmalloc/get_bktr_mem collapsed to malloc). */ static int METEORSETGEO(struct bktr *b, int rows, int cols, int frames) { int temp = rows * cols * frames * 2; /* line 1657 */ temp = btoc(temp); /* line 1663 */ b->alloc_pages = temp; b->bigbuf = calloc((size_t)temp * PAGE_SIZE, 1); if (!b->bigbuf) return -1; b->rows = rows; b->cols = cols; b->frames = frames; return 0; } /* METEORSACTPIXFMT body from sys/dev/video/bktr/bktr_core.c:2328-2337. */ static int METEORSACTPIXFMT(struct bktr *b, int idx) { if (idx < 0 || idx >= (int)PIXFMT_TABLE_SIZE) return -1; b->pixfmt = idx; /* line 2333 โ NO size check, NO realloc */ return 0; } /* video_read from sys/dev/video/bktr/bktr_core.c:1069-1114. */ static int video_read_oob_bytes(struct bktr *b) { int count = b->rows * b->cols * pixfmt_table[b->pixfmt].Bpp; /* line 1084-1085 */ size_t have = (size_t)b->alloc_pages * PAGE_SIZE; if ((size_t)count > have) return (int)((size_t)count - have); /* OOB read length */ return 0; } /* start_capture bzero from sys/dev/video/bktr/bktr_core.c:3519-3524. */ static int start_capture_oob_bytes(struct bktr *b) { size_t bytes = (size_t)b->rows * b->cols * b->frames * pixfmt_table[b->pixfmt].Bpp; /* line 3522-3523 */ size_t have = (size_t)b->alloc_pages * PAGE_SIZE; if (bytes > have) return (int)(bytes - have); /* OOB write length */ return 0; } int main(void) { struct bktr b; memset(&b, 0, sizeof(b)); /* METEOR_GEO_YUV_422 is the canonical Bpp=2 layout; with 1 frame the * METEORSETGEO alloc gives a 4 MiB buffer for a 2046x1022 image. */ if (METEORSETGEO(&b, 2046, 1022, 1) < 0) { perror("calloc"); return 2; } printf("After METEORSETGEO(YUV_422): bigbuf=%zu bytes alloc_pages=%d\n", (size_t)b.alloc_pages * PAGE_SIZE, b.alloc_pages); /* Switch pixfmt to index 5 (RGB 4Bpp) โ no realloc. */ if (METEORSACTPIXFMT(&b, 5) < 0) { fprintf(stderr, "pixfmt\n"); return 2; } int read_oob = video_read_oob_bytes(&b); int write_oob = start_capture_oob_bytes(&b); printf("video_read OOB=%d bytes start_capture OOB=%d bytes\n", read_oob, write_oob); free(b.bigbuf); if (read_oob > 0 && write_oob > 0) { printf("CONFIRMED: read() reads %d bytes past bigbuf end (heap leak); " "start_capture bzero/DMA writes %d bytes past bigbuf end " "(heap corruption)\n", read_oob, write_oob); return 0; } fprintf(stderr, "NOT CONFIRMED\n"); return 1; } |