DF-2836 / stateinherit.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 122 123 | /* * DF-2836 PoC: sonewconn_faddr() inherits the listener's ENTIRE so_state * (uipc_socket2.c:383: `so->so_state = head->so_state | SS_NOFDREF | * SS_ASSERTINPROG;`). A listener that has been shutdown(SHUT_WR) carries * SS_CANTSENDMORE; every child born afterwards (TCP handshake via the * syncache, or an AF_UNIX connect) is born half-shut: the accepted socket * returns EPIPE on its first send() even though the connection is fully * live and data flows in the receive direction. * * Unprivileged. Expected on a correct stack (FreeBSD inherits only the * NBIO bit): accepted send() == 4. On DragonFly: send() == -1 EPIPE. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <signal.h> static void demo_tcp(void) { struct sockaddr_in sa; socklen_t sl = sizeof(sa); char buf[16]; int l, c, a, n; l = socket(AF_INET, SOCK_STREAM, 0); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_len = sizeof(sa); sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = 0; if (bind(l, (struct sockaddr *)&sa, sizeof(sa)) < 0 || getsockname(l, (struct sockaddr *)&sa, &sl) < 0 || listen(l, 5) < 0) { printf("tcp: setup failed: %s\n", strerror(errno)); return; } /* graceful "stop taking new requests" a la daemon restart */ printf("tcp: shutdown(listener, SHUT_WR) = %d\n", shutdown(l, SHUT_WR)); c = socket(AF_INET, SOCK_STREAM, 0); if (connect(c, (struct sockaddr *)&sa, sizeof(sa)) < 0) { printf("tcp: connect failed: %s\n", strerror(errno)); return; } a = accept(l, NULL, 0); printf("tcp: connect ok, accept = %d\n", a); if (a < 0) return; n = send(c, "PING", 4, 0); printf("tcp: client send = %d (%s)\n", n, strerror(errno)); usleep(200000); n = recv(a, buf, sizeof(buf), 0); printf("tcp: accepted recv = %d (%s) [recv direction is LIVE]\n", n, strerror(errno)); n = send(a, "PONG", 4, 0); printf("tcp: accepted send = %d errno=%d [%s] " "<-- want 4; EPIPE(32) = BUG\n", n, errno, strerror(errno)); close(a); close(c); close(l); } static void demo_unix(void) { struct sockaddr_un sun; char buf[16]; int l, c, a, n; l = socket(AF_UNIX, SOCK_STREAM, 0); memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; sun.sun_len = sizeof(sun); strcpy(sun.sun_path, "/tmp/df2836.sock"); unlink(sun.sun_path); if (bind(l, (struct sockaddr *)&sun, sizeof(sun)) < 0 || listen(l, 5) < 0) { printf("unix: setup failed: %s\n", strerror(errno)); return; } printf("unix: shutdown(listener, SHUT_WR) = %d\n", shutdown(l, SHUT_WR)); c = socket(AF_UNIX, SOCK_STREAM, 0); if (connect(c, (struct sockaddr *)&sun, sizeof(sun)) < 0) { printf("unix: connect failed: %s\n", strerror(errno)); return; } a = accept(l, NULL, 0); printf("unix: connect ok, accept = %d\n", a); if (a < 0) return; n = send(c, "PING", 4, 0); printf("unix: client send = %d (%s)\n", n, strerror(errno)); n = recv(a, buf, sizeof(buf), 0); printf("unix: accepted recv = %d (%s)\n", n, strerror(errno)); n = send(a, "PONG", 4, 0); printf("unix: accepted send = %d errno=%d [%s] " "<-- want 4; EPIPE(32) = BUG\n", n, errno, strerror(errno)); close(a); close(c); close(l); unlink(sun.sun_path); } int main(void) { signal(SIGPIPE, SIG_IGN); setvbuf(stdout, NULL, _IONBF, 0); printf("== DF-2836: listener state inheritance ==\n"); demo_tcp(); demo_unix(); return (0); } |