DF-3520 / bcopy_demo.c
/* * DF-3520 — negative-length bcopy amplifier in ng_pptpgre_recv (userspace * arithmetic demonstration of the exact kernel expression). * * ng_pptpgre.c:641 const int index = ack - priv->recvAck - 1; * ng_pptpgre.c:646 if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) -> badAck * ng_pptpgre.c:650 if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) -> badAck * ng_pptpgre.c:668 bcopy(a->timeSent + index + 1, a->timeSent, * sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1))); * * The only bound on `index` is the window invariant * xmitSeq - recvAck <= xmitWin <= PPTP_XMIT_WIN(16) * enforced by the racy read-check-increment at ng_pptpgre.c:490-495 * (the DF-0596 TOCTOU family: two concurrent xmits both pass with * diff=15 and both increment -> diff=17). A subsequent in-window ack * then yields index = 16 and: * (PPTP_XMIT_WIN - (index + 1)) == -1 * sizeof(pptptime_t) * (int)-1 -> (size_t)0xFFFFFFFFFFFFFFF8 * -> bcopy() of ~2^64 bytes: unbounded OOB read+write -> certain kernel * panic (non-recoverable page fault in bcopy), far beyond DF-0596's * "8 bytes past the array" impact ceiling. * * This program reproduces the exact arithmetic (no kernel needed) and, * if invoked with --bcopy, actually attempts the copy to show the * immediate SIGSEGV the kernel would take. * * Build: cc -O2 -o bcopy_demo bcopy_demo.c * Run: ./bcopy_demo (print arithmetic) * ./bcopy_demo --bcopy (attempt the copy -> SIGSEGV) */ #include <stdio.h> #include <stdlib.h> #include <string.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)) int main(int argc, char **argv) { uint32_t recvAck = 100, xmitSeq = 117; /* diff = 17 via DF-0596 TOCTOU */ uint32_t ack = 117; /* highest legal ack: == xmitSeq */ /* the kernel's sanity checks pass: */ if (PPTP_SEQ_DIFF(ack, xmitSeq) > 0) { puts("badAck(high)"); return 1; } if (PPTP_SEQ_DIFF(ack, recvAck) <= 0) { puts("badAck(low)"); return 1; } const int index = ack - recvAck - 1; /* = 16 */ int len_i = PPTP_XMIT_WIN - (index + 1); /* = -1 */ size_t len = sizeof(pptptime_t) * (PPTP_XMIT_WIN - (index + 1)); printf("recvAck=%u xmitSeq=%u ack=%u -> index=%d\n", recvAck, xmitSeq, ack, index); printf("int length = PPTP_XMIT_WIN - (index+1) = %d\n", len_i); printf("size_t len = sizeof(pptptime_t) * (%d) = 0x%llx (%zu)\n", len_i, (unsigned long long)len, len); printf("kernel timeSent[] = pptptime_t[%d] (%zu bytes), " "index %d is %zd bytes past end\n", PPTP_XMIT_WIN, sizeof(pptptime_t) * PPTP_XMIT_WIN, index, (ssize_t)((index - PPTP_XMIT_WIN + 1) * (ssize_t)sizeof(pptptime_t))); if (argc > 1 && strcmp(argv[1], "--bcopy") == 0) { pptptime_t timeSent[PPTP_XMIT_WIN]; printf("attempting bcopy(dst=timeSent, src=timeSent+%d, len=0x%llx)...\n", index + 1, (unsigned long long)len); fflush(stdout); bcopy(timeSent + index + 1, timeSent, len); /* SIGSEGV */ printf("unreachable\n"); } return 0; } |