DF-3279 / span_leak.c
/* * DF-3279 -- struct bridge_ifinfo leak on span-port delete. * * bridge_ioctl_addspan() (if_bridge.c:1823) allocates bif_info for the * span bif; bridge_delete_span() (if_bridge.c:1155-1156) frees only the * bif, never bif_info. Each spanport add/del cycle leaks * sizeof(struct bridge_ifinfo) (~136 bytes) of M_DEVBUF kernel memory. * * Run: ./span_leak <iterations>; watch `vmstat -m :devbuf` (in-use bytes) * grow by ~136 * iterations. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <net/ethernet.h> #include <net/bridge/if_bridgevar.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> static int s; static void drvspec(const char *ifname, unsigned long cmd, int subcmd) { struct ifbreq r; struct ifdrv ifd; memset(&r, 0, sizeof(r)); strlcpy(r.ifbr_ifsname, "tap0", IFNAMSIZ); memset(&ifd, 0, sizeof(ifd)); strlcpy(ifd.ifd_name, ifname, IFNAMSIZ); ifd.ifd_cmd = subcmd; ifd.ifd_len = sizeof(r); ifd.ifd_data = &r; if (ioctl(s, cmd, &ifd) < 0) { fprintf(stderr, "drvspec %d: %s\n", subcmd, strerror(errno)); exit(1); } } int main(int argc, char **argv) { long n, i; if (argc < 2) n = 5000; else n = atol(argv[1]); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } printf("[+] running %ld spanport add/del cycles on bridge0/tap0\n", n); for (i = 0; i < n; i++) { drvspec("bridge0", SIOCSDRVSPEC, BRDGADDS); drvspec("bridge0", SIOCSDRVSPEC, BRDGDELS); if ((i % 500) == 0) { printf(" %ld\r", i); fflush(stdout); } } printf("[+] done: %ld cycles\n", n); return (0); } |