1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Selftest for AF_UNIX socket close and ECONNRESET behaviour. 4 * 5 * This test verifies: 6 * 1. SOCK_STREAM returns EOF when the peer closes normally. 7 * 2. SOCK_STREAM returns ECONNRESET if peer closes with unread data. 8 * 3. SOCK_SEQPACKET returns EOF when the peer closes normally. 9 * 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data. 10 * 5. SOCK_DGRAM does not return ECONNRESET when the peer closes. 11 * 12 * These tests document the intended Linux behaviour. 13 * 14 */ 15 16 #define _GNU_SOURCE 17 #include <string.h> 18 #include <fcntl.h> 19 #include <unistd.h> 20 #include <errno.h> 21 #include <sys/socket.h> 22 #include <sys/un.h> 23 #include "../../kselftest_harness.h" 24 25 #define SOCK_PATH "/tmp/af_unix_connreset.sock" 26 27 static void remove_socket_file(void) 28 { 29 unlink(SOCK_PATH); 30 } 31 32 FIXTURE(unix_sock) 33 { 34 int server; 35 int client; 36 int child; 37 }; 38 39 FIXTURE_VARIANT(unix_sock) 40 { 41 int socket_type; 42 const char *name; 43 }; 44 45 FIXTURE_VARIANT_ADD(unix_sock, stream) { 46 .socket_type = SOCK_STREAM, 47 .name = "SOCK_STREAM", 48 }; 49 50 FIXTURE_VARIANT_ADD(unix_sock, dgram) { 51 .socket_type = SOCK_DGRAM, 52 .name = "SOCK_DGRAM", 53 }; 54 55 FIXTURE_VARIANT_ADD(unix_sock, seqpacket) { 56 .socket_type = SOCK_SEQPACKET, 57 .name = "SOCK_SEQPACKET", 58 }; 59 60 FIXTURE_SETUP(unix_sock) 61 { 62 struct sockaddr_un addr = {}; 63 int err; 64 65 addr.sun_family = AF_UNIX; 66 strcpy(addr.sun_path, SOCK_PATH); 67 remove_socket_file(); 68 69 self->server = socket(AF_UNIX, variant->socket_type, 0); 70 ASSERT_LT(-1, self->server); 71 72 err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr)); 73 ASSERT_EQ(0, err); 74 75 if (variant->socket_type == SOCK_STREAM || 76 variant->socket_type == SOCK_SEQPACKET) { 77 err = listen(self->server, 1); 78 ASSERT_EQ(0, err); 79 } 80 81 self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0); 82 ASSERT_LT(-1, self->client); 83 84 err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr)); 85 ASSERT_EQ(0, err); 86 } 87 88 FIXTURE_TEARDOWN(unix_sock) 89 { 90 if (variant->socket_type == SOCK_STREAM || 91 variant->socket_type == SOCK_SEQPACKET) 92 close(self->child); 93 94 close(self->client); 95 close(self->server); 96 remove_socket_file(); 97 } 98 99 /* Test 1: peer closes normally */ 100 TEST_F(unix_sock, eof) 101 { 102 char buf[16] = {}; 103 ssize_t n; 104 105 if (variant->socket_type == SOCK_STREAM || 106 variant->socket_type == SOCK_SEQPACKET) { 107 self->child = accept(self->server, NULL, NULL); 108 ASSERT_LT(-1, self->child); 109 110 close(self->child); 111 } else { 112 close(self->server); 113 } 114 115 n = recv(self->client, buf, sizeof(buf), 0); 116 117 if (variant->socket_type == SOCK_STREAM || 118 variant->socket_type == SOCK_SEQPACKET) { 119 ASSERT_EQ(0, n); 120 } else { 121 ASSERT_EQ(-1, n); 122 ASSERT_EQ(EAGAIN, errno); 123 } 124 } 125 126 /* Test 2: peer closes with unread data */ 127 TEST_F(unix_sock, reset_unread_behavior) 128 { 129 char buf[16] = {}; 130 ssize_t n; 131 132 /* Send data that will remain unread */ 133 send(self->client, "hello", 5, 0); 134 135 if (variant->socket_type == SOCK_DGRAM) { 136 /* No real connection, just close the server */ 137 close(self->server); 138 } else { 139 self->child = accept(self->server, NULL, NULL); 140 ASSERT_LT(-1, self->child); 141 142 /* Peer closes before client reads */ 143 close(self->child); 144 } 145 146 n = recv(self->client, buf, sizeof(buf), 0); 147 ASSERT_EQ(-1, n); 148 149 if (variant->socket_type == SOCK_STREAM || 150 variant->socket_type == SOCK_SEQPACKET) { 151 ASSERT_EQ(ECONNRESET, errno); 152 } else { 153 ASSERT_EQ(EAGAIN, errno); 154 } 155 } 156 157 /* Test 3: closing unaccepted (embryo) server socket should reset client. */ 158 TEST_F(unix_sock, reset_closed_embryo) 159 { 160 char buf[16] = {}; 161 ssize_t n; 162 163 if (variant->socket_type == SOCK_DGRAM) { 164 snprintf(_metadata->results->reason, 165 sizeof(_metadata->results->reason), 166 "Test only applies to SOCK_STREAM and SOCK_SEQPACKET"); 167 exit(KSFT_XFAIL); 168 } 169 170 /* Close server without accept()ing */ 171 close(self->server); 172 173 n = recv(self->client, buf, sizeof(buf), 0); 174 175 ASSERT_EQ(-1, n); 176 ASSERT_EQ(ECONNRESET, errno); 177 } 178 179 TEST_HARNESS_MAIN 180 181