/*
 * DF-1857 source-confirmation harness (splash_bmp unbounded BMP/RLE read).
 *
 * sys/dev/video/fb/bmp/splash_bmp.c bmp_DecodeRLE8/RLE4 (lines ~307-410)
 * loop reading *info->index and advancing info->index with NO check that
 * index stays within [data, data+data_size).  An RLE stream that omits the
 * end-of-bitmap escape (0x00 0x01) walks off the end of the splash image
 * into kernel memory, either (a) painting leaked kernel bytes to the
 * framebuffer (read back via /dev/fb0) or (b) hitting an unmapped page ->
 * panic during boot.
 *
 * bmp_Start (splash_bmp.c:80) only checks data_size<=0; never checks the
 * file is large enough for the BITMAPF header, let alone the declared
 * pixel array.  bmp_Init (line 516) computes bmp_info.data = data +
 * bmf->bmfh.bfOffBits with bfOffBits taken from the attacker file and no
 * range check.  width/height/depth from the header are likewise unchecked
 * (lines 519-521); only a screen-fit check at 540-547.
 *
 * Live trigger requires splash_bmp_load=YES at boot (boot-time) and
 * write access to the splash asset — not exercisable as maxx on this
 * guest.  The harness models the RLE8 walk and shows it reads past the
 * data buffer when no end-of-bitmap escape is present.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct bmp_info {
    const unsigned char *data;
    size_t data_size;
    const unsigned char *index;
};

/* Model of bmp_DecodeRLE8 (splash_bmp.c ~373-410).  The real loop runs
 * until it sees escape 0x00 0x01 (end-of-bitmap).  With no escape, it
 * walks past data+data_size into whatever follows in kernel memory. */
static size_t
rle8_walk(struct bmp_info *info) {
    size_t reads = 0;
    for (;;) {
        if ((size_t)(info->index - info->data) >= info->data_size + 4096) break;
        unsigned char cnt = info->index[0];
        if (cnt != 0) { info->index += 2; reads += 2; continue; }
        unsigned char esc = info->index[1];
        if (esc == 0) { info->index += 2; continue; }     /* end of line */
        if (esc == 1) return reads;                        /* end of bitmap */
        if (esc == 2) { info->index += 4; continue; }      /* delta */
        info->index += 2 + ((esc + 1) & ~1); reads += 2 + ((esc + 1) & ~1);
    }
    return reads;
}

int main(void) {
    /* Real data: 64 bytes, NO end-of-bitmap escape. */
    unsigned char data[64];
    memset(data, 0x80, sizeof(data));    /* run/escape pattern that never hits 0x00 0x01 */

    struct bmp_info info;
    info.data = data; info.data_size = sizeof(data); info.index = data;

    size_t consumed = rle8_walk(&info);
    long overrun = (long)(info.index - info.data) - (long)info.data_size;
    printf("DF-1857: bmp_DecodeRLE8 walk (splash_bmp.c:307-410)\n");
    printf("  data_size=%zu bytes, no end-of-bitmap escape\n", info.data_size);
    printf("  final index offset = %ld bytes past data start\n",
           (long)(info.index - info.data));
    printf("  OOB read = %ld bytes past the splash image into kernel memory\n",
           overrun);
    printf("  In the kernel this either paints leaked kmem to the framebuffer "
           "(/dev/fb0 leak) or hits an unmapped page and panics during boot.\n");
    printf("  bmp_Start (splash_bmp.c:80) never validates data_size vs header; "
           "bmp_Init (:516) trusts bmfOffBits with no range check.\n");
    return 0;
}
