DF-0596 / race_harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /* * 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; } |