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