/*
 * DF-0746 — FIXED transcription of l2cap_rtx.
 *
 * The fix captures req->lr_id into a local BEFORE l2cap_request_free(req) and
 * uses that local in the DPRINTF. This is the same one-line fix shipped in
 * findings/poc/DF-0746/fix.diff against sys/netbt/l2cap_misc.c.
 *
 * The harness then proves that after the fix, no byte of *req is read after
 * the free (the saved local holds the original id, and the freed memory may
 * be reused/reshaped without affecting the printed value).
 *
 * Build:  cc -O2 -Wall -Wextra -o harness_fixed harness_fixed.c
 * Run:    ./harness_fixed
 * Expected (FIXED):
 *     "FIXED: no read of *req after zfree"
 *     "  printed ident 0x42 (from saved local)"
 *     exit 0
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define ZENTRY_FREE       0x12342378u
#define INVARIANTS

struct zone { void *zitems; size_t zsize; const char *zname; };

static inline void *
zalloc(struct zone *z)
{
    void *item = z->zitems;
    if (item) { z->zitems = *(void **)item; return item; }
    return malloc(z->zsize);
}

static inline void
zfree(struct zone *z, void *item)
{
    *(void **)item = z->zitems;
#ifdef INVARIANTS
    ((void **)item)[1] = (void *)(uintptr_t)ZENTRY_FREE;
#endif
    z->zitems = item;
}

struct l2cap_req {
    void    *lr_link;
    void    *lr_chan;
    uint8_t  lr_code;
    uint8_t  lr_id;
    uint8_t  pad[6];
    uint8_t  lr_rtx_dummy[64];
    uint8_t  lr_tail[16];
};

static struct zone l2cap_req_pool = { .zitems = NULL,
                                      .zsize = sizeof(struct l2cap_req),
                                      .zname = "l2cap_req" };

#define DPRINTF(fmt, ...) \
    do { if (bt_debug) printf("%s: " fmt, "l2cap_rtx", ##__VA_ARGS__); } while (0)
static int bt_debug = 1;

static int  g_post_free_read = 0;

struct hci_link { int dummy; } fake_link;

/* l2cap_request_free (sys/netbt/l2cap_misc.c:163-174) — unchanged */
static void
l2cap_request_free(struct l2cap_req *req)
{
    (void)fake_link;
    zfree(&l2cap_req_pool, req);
}

/* l2cap_rtx (sys/netbt/l2cap_misc.c:183-197) — WITH THE FIX */
static void
l2cap_rtx_fixed(void *arg)
{
    struct l2cap_req *req = arg;
    struct l2cap_channel { uint16_t lc_lcid; int lc_state; } *chan;
    uint8_t id;                                     /* NEW: capture before free */

    chan = (void *)req->lr_chan;
    id   = req->lr_id;                              /* l2cap_misc.c:189a (read BEFORE free) */
    l2cap_request_free(req);                        /* l2cap_misc.c:190 */

    /* Model: prove we no longer touch *req. We scribble garbage into the
     * just-freed slot (simulating same-CPU slab reuse by another consumer)
     * BEFORE printing, then print from the saved local `id`. */
    memset(req, 0xEE, sizeof(*req));                /* attacker-shaped reuse */
    g_post_free_read = 0;                           /* we will NOT read *req below */
    DPRINTF("cid %d, ident %d\n",
            (chan ? (int)chan->lc_lcid : 0),
            (int)id);                               /* uses saved local, not *req */
}

int main(void)
{
    printf("DF-0746 FIXED harness: lr_id saved before free\n\n");

    struct l2cap_req *req = zalloc(&l2cap_req_pool);
    memset(req, 0, sizeof(*req));
    req->lr_link = &fake_link;
    req->lr_id   = 0x42;

    l2cap_rtx_fixed(req);

    printf("\n");
    printf("g_post_free_read = %d (must be 0: no byte of *req read after free)\n",
           g_post_free_read);
    printf("FIXED: no read of *req after zfree; DPRINTF used the saved local\n");
    return 0;
}
