DF-3425 / gif_race.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 | /* * DF-3425 - gif(4) teardown races netisr encap walk (no common lock). * * The global encaptab list (sys/netinet/ip_encap.c) is walked lock-free by * encap4_input() on NETISR(0) for every inbound IPIP packet, calling * ep->func(ep->arg) [= gif_encapcheck(softc)] for each gif entry. * Concurrently, from a normal thread (no serialization shared with netisr): * - gif_clone_destroy() (SIOCIFDESTROY, ifnet_lock held, NOT ifnet serializer) * -> encap_detach(): LIST_REMOVE + kfree(ep) [ip_encap.c:411-426] * -> gif_delete_tunnel(), kfree(sc) [if_gif.c:176-203] * - gif_set_tunnel() (SIOCSIFPHYADDR reconfig, ifnet serializer only) * -> in_gif_detach()/in_gif_attach() churn on encaptab * * This harness: 30 static gif tunnels (long walk), one bouncing gif * (create/set/destroy loop), one reconfig gif (set-tunnel loop), * 4 flood threads sending IPIP to 127.0.0.1 through raw sockets. * * Run as root. Expected on hit: panic (wild deref / "Memory modified after * free" / KASSERT) or silent corruption; run ends after <secs> without * incident if the race is not won. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/sockio.h> #include <net/if.h> #include <net/if_dl.h> #include <netinet/in.h> #include <netinet/in_var.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #define S_SOCKET 0 #define NSTATIC 30 /* gif0..gif29 (skip bounce) */ static int cmdsock; static volatile unsigned long n_flood, n_cyc, n_recfg, n_err; static volatile int stop; static void mk_sin(struct sockaddr_in *sin, uint32_t a) { memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr.s_addr = htonl(a); } static int gif_create(const char *name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); return ioctl(cmdsock, SIOCIFCREATE, &ifr); } static int gif_destroy(const char *name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); return ioctl(cmdsock, SIOCIFDESTROY, &ifr); } static int gif_set_tunnel(const char *name, uint32_t src, uint32_t dst) { struct in_aliasreq ifra; memset(&ifra, 0, sizeof(ifra)); strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name)); mk_sin(&ifra.ifra_addr, src); mk_sin(&ifra.ifra_dstaddr, dst); return ioctl(cmdsock, SIOCSIFPHYADDR, &ifra); } static void * flood(void *arg) { struct sockaddr_in to; unsigned char pkt[36]; /* inner dummy ipv4-ish payload */ int s = socket(AF_INET, SOCK_RAW, IPPROTO_IPV4); if (s < 0) { perror("raw socket"); return NULL; } memset(&to, 0, sizeof(to)); to.sin_family = AF_INET; to.sin_len = sizeof(to); to.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 -> lo0 */ memset(pkt, 0x45, sizeof(pkt)); while (!stop) { if (sendto(s, pkt, sizeof(pkt), 0, (struct sockaddr *)&to, sizeof(to)) > 0) n_flood++; else n_err++; } close(s); return NULL; } static void * destroyer(void *arg) { while (!stop) { if (gif_create("gif50") == 0) { gif_set_tunnel("gif50", 0x0a646461, 0x0a646462); gif_destroy("gif50"); n_cyc++; } else n_err++; } return NULL; } static void * reconfig(void *arg) { int flip = 0; while (!stop) { gif_set_tunnel("gif51", flip ? 0x0a656561 : 0x0a656563, flip ? 0x0a656562 : 0x0a656564); flip = !flip; n_recfg++; } return NULL; } int main(int argc, char **argv) { pthread_t th[6]; char name[32]; int i, secs = argc > 1 ? atoi(argv[1]) : 240; uint32_t base; cmdsock = socket(AF_INET, SOCK_DGRAM, 0); if (cmdsock < 0) { perror("dgram socket"); return 1; } /* static walk-lengtheners: gif0..gif29 with distinct endpoints */ for (i = 0, base = 0x0a000000; i < NSTATIC; i++, base += 0x202) { snprintf(name, sizeof(name), "gif%d", i); if (gif_create(name) == 0) gif_set_tunnel(name, base + 1, base + 2); } /* bounce unit gif50, reconfig unit gif51 */ gif_create("gif51"); gif_set_tunnel("gif51", 0x0a656563, 0x0a656564); for (i = 0; i < 4; i++) pthread_create(&th[i], NULL, flood, NULL); pthread_create(&th[4], NULL, destroyer, NULL); pthread_create(&th[5], NULL, reconfig, NULL); printf("racing: %d static gifs, gif50 bounce, gif51 reconfig, 4 floods\n", NSTATIC); for (i = 0; i < secs; i += 10) { sleep(10); printf("t+%ds: flood=%lu cyc=%lu recfg=%lu err=%lu\n", i + 10, n_flood, n_cyc, n_recfg, n_err); fflush(stdout); } stop = 1; for (i = 0; i < 6; i++) pthread_join(th[i], NULL); printf("done (no panic): flood=%lu cyc=%lu recfg=%lu err=%lu\n", n_flood, n_cyc, n_recfg, n_err); return 0; } |