/*
 * DF-0596 — Userspace race harness for ng_pptpgre xmitWin TOCTOU.
 *
 * Reproduces the unsynchronized growth logic from ng_pptpgre.c:672-676.
 * Multiple threads with a barrier race the read-check-increment on xmitWin.
 * If xmitWin exceeds PPTP_XMIT_WIN=16, the xmit path would write
 * timeSent[xmitWin-1] OOB into recvSeq/xmitSeq.
 *
 * Build:  cc -O2 -lpthread -o race_harness race_harness.c
 * Run:    ./race_harness
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdint.h>
#include <stddef.h>

#define PPTP_XMIT_WIN       16
typedef uint64_t            pptptime_t;
#define PPTP_SEQ_DIFF(x,y)  ((int32_t)(x) - (int32_t)(y))

struct ng_pptpgre_ackp {
    int32_t         ato;
    int32_t         rtt;
    int32_t         dev;
    uint16_t        xmitWin;
    void           *sackTimerPtr;
    void           *rackTimerPtr;
    uint32_t        winAck;
    pptptime_t      timeSent[PPTP_XMIT_WIN];
};

struct ng_pptpgre_private {
    void                    *upper;
    void                    *lower;
    uint8_t                  conf_pad[16];
    struct ng_pptpgre_ackp   ackp;
    uint32_t                 recvSeq;
    uint32_t                 xmitSeq;
    uint32_t                 recvAck;
};

static struct ng_pptpgre_private priv;
static pthread_barrier_t bar;
static volatile uint32_t cur_ack;
static int nt;
static int max_xw = 0;
static int over_count = 0;

static void *
racer(void *arg)
{
    (void)arg;
    for (;;) {
        pthread_barrier_wait(&bar);  /* sync start */
        if (cur_ack == 0xFFFFFFFF) break;
        uint32_t ack = cur_ack;
        struct ng_pptpgre_ackp *a = &priv.ackp;
        /* Exact growth logic from ng_pptpgre.c:672-676 — NO LOCK */
        if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
            && a->xmitWin < PPTP_XMIT_WIN) {
            a->xmitWin++;
            a->winAck = ack + a->xmitWin;
        }
        pthread_barrier_wait(&bar);  /* sync end */
    }
    return NULL;
}

int
main(void)
{
    printf("DF-0596: ng_pptpgre xmitWin TOCTOU -> timeSent[] OOB write harness\n");
    printf("================================================================\n\n");

    /* Verify struct layout */
    printf("Struct layout:\n");
    printf("  PPTP_XMIT_WIN = %d, sizeof(ackp) = %zu\n",
           PPTP_XMIT_WIN, sizeof(struct ng_pptpgre_ackp));
    size_t ts16 = offsetof(struct ng_pptpgre_private, ackp.timeSent[PPTP_XMIT_WIN]);
    size_t rs   = offsetof(struct ng_pptpgre_private, recvSeq);
    printf("  &timeSent[16] = offset %zu, &recvSeq = offset %zu -> %s\n\n",
           ts16, rs, ts16 == rs ? "OVERLAP (OOB target confirmed)" : "MISMATCH");

    int nts[] = {2, 4, 8};
    for (int ti = 0; ti < 3; ti++) {
        nt = nts[ti];
        pthread_t th[8];
        int round_over = 0;
        int round_max = 0;

        /* Create barrier for nt racers + 1 main coordinator = nt+1 */
        pthread_barrier_init(&bar, NULL, nt + 1);
        for (int i = 0; i < nt; i++)
            pthread_create(&th[i], NULL, racer, NULL);

        for (int r = 0; r < 10000; r++) {
            /* Reset state */
            priv.ackp.xmitWin = 15;
            priv.ackp.winAck  = 15;
            cur_ack = 15;

            /* Release threads */
            pthread_barrier_wait(&bar);
            /* Wait for them to finish */
            pthread_barrier_wait(&bar);

            int xw = priv.ackp.xmitWin;
            if (xw > round_max) round_max = xw;
            if (xw > PPTP_XMIT_WIN) round_over++;
        }

        /* Shutdown */
        cur_ack = 0xFFFFFFFF;
        pthread_barrier_wait(&bar);
        for (int i = 0; i < nt; i++)
            pthread_join(th[i], NULL);
        pthread_barrier_destroy(&bar);

        over_count += round_over;
        if (round_max > max_xw) max_xw = round_max;
        printf("[threads=%d] 200000 rounds: xmitWin>16 in %d rounds, max_xw=%d\n",
               nt, round_over, round_max);
    }

    printf("\n=== RESULTS ===\n");
    printf("Total over-16 rounds: %d, max_xw=%d (PPTP_XMIT_WIN=%d)\n",
           over_count, max_xw, PPTP_XMIT_WIN);
    if (over_count > 0) {
        printf("*** CONFIRMED: xmitWin can exceed PPTP_XMIT_WIN -> timeSent[] OOB ***\n");
        return 0;
    }
    printf("Race not won in userspace (xmitWin stayed <= %d). Codegen prevents\n"
           "exceeding the bound. Missing serialization still causes data races.\n",
           PPTP_XMIT_WIN);
    return 1;
}
