/*
 * DF-1818 source-confirmation harness (fill_rect24 slow path over-advances).
 *
 * sys/dev/misc/syscons/sckmsrndr.c:437-446 slow path:
 *   for (i = 0; i < height; i++) {
 *     for (j = 0; j < width; j++) { writeb(3 bytes); draw_pos += 3; }   // +3*width
 *     draw_pos += line_width;                                           // BUG: should be += d
 *   }
 * where d = line_width - width*3 is the per-row right-padding (line 420).
 * The fast path (423-436) correctly does draw_pos += d at line 435.
 * Slow path is taken when (draw_pos|line_width|width) & 3 != 0, e.g.
 * 1366x768@24bpp right border (width=6, 6&3=2).  Drift per row =
 * (height-1)*3*width.  This harness reproduces the arithmetic.
 *
 * Build:  cc -O2 -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Model of the framebuffer: vaddr + stride*height bytes. */
static unsigned char *fb;
static size_t fb_size;
static size_t write_count;

static void writeb_model(size_t pos, unsigned char v) {
    if (pos < fb_size) { fb[pos] = v; write_count++; }
    /* else: OOB — in the kernel this is an unchecked volatile store */
}

/* Verbatim slow-path logic from sckmsrndr.c:437-446. */
static size_t fill_rect24_slow(size_t draw_pos, int width, int height, int line_width) {
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            writeb_model(draw_pos,     0xAA);
            writeb_model(draw_pos + 1, 0xBB);
            writeb_model(draw_pos + 2, 0xCC);
            draw_pos += 3;
        }
        draw_pos += line_width;   /* BUG (sckmsrndr.c:445) — should be += d */
    }
    return draw_pos;
}

/* Fixed slow-path: draw_pos += d (= line_width - width*3). */
static size_t fill_rect24_slow_fixed(size_t draw_pos, int width, int height, int line_width) {
    int d = line_width - width * 3;
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            writeb_model(draw_pos,     0xAA);
            writeb_model(draw_pos + 1, 0xBB);
            writeb_model(draw_pos + 2, 0xCC);
            draw_pos += 3;
        }
        draw_pos += d;            /* FIXED */
    }
    return draw_pos;
}

int main(void) {
    /* 1366x768@24bpp, stride = 1366*3 = 4098, but rounds up to 4096+? use 4096 stride
     * The right border: width = 1366 - xsize*blk_width.  With scp->xsize=170 (default
     * 80-col*8px*... approx) the right border is small.  Use the finding's worked eg:
     * rightpixel=6, height=768, line_width=4096. */
    int line_width = 4096, width = 6, height = 768;
    size_t vaddr = 0;
    fb_size = (size_t)line_width * 768;
    fb = calloc(1, fb_size + 8192);   /* give headroom so we can SEE the overrun */

    write_count = 0;
    size_t end_bug   = fill_rect24_slow(vaddr, width, height, line_width);
    size_t inbounds_bug = (end_bug + 2 < fb_size) ? end_bug : fb_size;
    /* number of bytes the slow path wrote PAST the framebuffer mapping */
    long overflow_bug = (long)end_bug - (long)fb_size;

    /* Reset and run the FIXED version */
    memset(fb, 0, fb_size + 8192);
    write_count = 0;
    size_t end_fix   = fill_rect24_slow_fixed(vaddr, width, height, line_width);
    long overflow_fix = (long)end_fix - (long)fb_size;

    printf("DF-1818: fill_rect24 slow path (sckmsrndr.c:437-446)\n");
    printf("  width=%d height=%d line_width=%d  d=%d (per-row padding)\n",
           width, height, line_width, line_width - width*3);
    printf("  BUGGY (+=line_width at :445): final draw_pos=%zu, %ld bytes PAST fb end\n",
           end_bug, overflow_bug);
    printf("  FIXED (+=d):                 final draw_pos=%zu, %ld bytes past fb end\n",
           end_fix, overflow_fix);
    printf("  Per-row drift = 3*width = %d bytes; total drift = (height-1)*3*width = %d bytes\n",
           3*width, (height-1)*3*width);
    free(fb);
    return 0;
}
