145a1cd83SSunday Adelodun // SPDX-License-Identifier: GPL-2.0 245a1cd83SSunday Adelodun /* 345a1cd83SSunday Adelodun * Selftest for AF_UNIX socket close and ECONNRESET behaviour. 445a1cd83SSunday Adelodun * 545a1cd83SSunday Adelodun * This test verifies: 645a1cd83SSunday Adelodun * 1. SOCK_STREAM returns EOF when the peer closes normally. 745a1cd83SSunday Adelodun * 2. SOCK_STREAM returns ECONNRESET if peer closes with unread data. 845a1cd83SSunday Adelodun * 3. SOCK_SEQPACKET returns EOF when the peer closes normally. 945a1cd83SSunday Adelodun * 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data. 1045a1cd83SSunday Adelodun * 5. SOCK_DGRAM does not return ECONNRESET when the peer closes. 1145a1cd83SSunday Adelodun * 1245a1cd83SSunday Adelodun * These tests document the intended Linux behaviour. 1345a1cd83SSunday Adelodun * 1445a1cd83SSunday Adelodun */ 1545a1cd83SSunday Adelodun 1645a1cd83SSunday Adelodun #define _GNU_SOURCE 1745a1cd83SSunday Adelodun #include <string.h> 1845a1cd83SSunday Adelodun #include <fcntl.h> 1945a1cd83SSunday Adelodun #include <unistd.h> 2045a1cd83SSunday Adelodun #include <errno.h> 2145a1cd83SSunday Adelodun #include <sys/socket.h> 2245a1cd83SSunday Adelodun #include <sys/un.h> 2345a1cd83SSunday Adelodun #include "../../kselftest_harness.h" 2445a1cd83SSunday Adelodun 2545a1cd83SSunday Adelodun #define SOCK_PATH "/tmp/af_unix_connreset.sock" 2645a1cd83SSunday Adelodun 2745a1cd83SSunday Adelodun static void remove_socket_file(void) 2845a1cd83SSunday Adelodun { 2945a1cd83SSunday Adelodun unlink(SOCK_PATH); 3045a1cd83SSunday Adelodun } 3145a1cd83SSunday Adelodun 3245a1cd83SSunday Adelodun FIXTURE(unix_sock) 3345a1cd83SSunday Adelodun { 3445a1cd83SSunday Adelodun int server; 3545a1cd83SSunday Adelodun int client; 3645a1cd83SSunday Adelodun int child; 3745a1cd83SSunday Adelodun }; 3845a1cd83SSunday Adelodun 3945a1cd83SSunday Adelodun FIXTURE_VARIANT(unix_sock) 4045a1cd83SSunday Adelodun { 4145a1cd83SSunday Adelodun int socket_type; 4245a1cd83SSunday Adelodun const char *name; 4345a1cd83SSunday Adelodun }; 4445a1cd83SSunday Adelodun 4545a1cd83SSunday Adelodun FIXTURE_VARIANT_ADD(unix_sock, stream) { 4645a1cd83SSunday Adelodun .socket_type = SOCK_STREAM, 4745a1cd83SSunday Adelodun .name = "SOCK_STREAM", 4845a1cd83SSunday Adelodun }; 4945a1cd83SSunday Adelodun 5045a1cd83SSunday Adelodun FIXTURE_VARIANT_ADD(unix_sock, dgram) { 5145a1cd83SSunday Adelodun .socket_type = SOCK_DGRAM, 5245a1cd83SSunday Adelodun .name = "SOCK_DGRAM", 5345a1cd83SSunday Adelodun }; 5445a1cd83SSunday Adelodun 5545a1cd83SSunday Adelodun FIXTURE_VARIANT_ADD(unix_sock, seqpacket) { 5645a1cd83SSunday Adelodun .socket_type = SOCK_SEQPACKET, 5745a1cd83SSunday Adelodun .name = "SOCK_SEQPACKET", 5845a1cd83SSunday Adelodun }; 5945a1cd83SSunday Adelodun 6045a1cd83SSunday Adelodun FIXTURE_SETUP(unix_sock) 6145a1cd83SSunday Adelodun { 6245a1cd83SSunday Adelodun struct sockaddr_un addr = {}; 6345a1cd83SSunday Adelodun int err; 6445a1cd83SSunday Adelodun 6545a1cd83SSunday Adelodun addr.sun_family = AF_UNIX; 6645a1cd83SSunday Adelodun strcpy(addr.sun_path, SOCK_PATH); 6745a1cd83SSunday Adelodun remove_socket_file(); 6845a1cd83SSunday Adelodun 6945a1cd83SSunday Adelodun self->server = socket(AF_UNIX, variant->socket_type, 0); 7045a1cd83SSunday Adelodun ASSERT_LT(-1, self->server); 7145a1cd83SSunday Adelodun 7245a1cd83SSunday Adelodun err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr)); 7345a1cd83SSunday Adelodun ASSERT_EQ(0, err); 7445a1cd83SSunday Adelodun 7545a1cd83SSunday Adelodun if (variant->socket_type == SOCK_STREAM || 7645a1cd83SSunday Adelodun variant->socket_type == SOCK_SEQPACKET) { 7745a1cd83SSunday Adelodun err = listen(self->server, 1); 7845a1cd83SSunday Adelodun ASSERT_EQ(0, err); 7945a1cd83SSunday Adelodun } 8045a1cd83SSunday Adelodun 8145a1cd83SSunday Adelodun self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0); 8245a1cd83SSunday Adelodun ASSERT_LT(-1, self->client); 8345a1cd83SSunday Adelodun 8445a1cd83SSunday Adelodun err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr)); 8545a1cd83SSunday Adelodun ASSERT_EQ(0, err); 8645a1cd83SSunday Adelodun } 8745a1cd83SSunday Adelodun 8845a1cd83SSunday Adelodun FIXTURE_TEARDOWN(unix_sock) 8945a1cd83SSunday Adelodun { 9045a1cd83SSunday Adelodun if (variant->socket_type == SOCK_STREAM || 9145a1cd83SSunday Adelodun variant->socket_type == SOCK_SEQPACKET) 9245a1cd83SSunday Adelodun close(self->child); 9345a1cd83SSunday Adelodun 9445a1cd83SSunday Adelodun close(self->client); 9545a1cd83SSunday Adelodun close(self->server); 9645a1cd83SSunday Adelodun remove_socket_file(); 9745a1cd83SSunday Adelodun } 9845a1cd83SSunday Adelodun 9945a1cd83SSunday Adelodun /* Test 1: peer closes normally */ 10045a1cd83SSunday Adelodun TEST_F(unix_sock, eof) 10145a1cd83SSunday Adelodun { 10245a1cd83SSunday Adelodun char buf[16] = {}; 10345a1cd83SSunday Adelodun ssize_t n; 10445a1cd83SSunday Adelodun 10545a1cd83SSunday Adelodun if (variant->socket_type == SOCK_STREAM || 10645a1cd83SSunday Adelodun variant->socket_type == SOCK_SEQPACKET) { 10745a1cd83SSunday Adelodun self->child = accept(self->server, NULL, NULL); 10845a1cd83SSunday Adelodun ASSERT_LT(-1, self->child); 10945a1cd83SSunday Adelodun 11045a1cd83SSunday Adelodun close(self->child); 11145a1cd83SSunday Adelodun } else { 11245a1cd83SSunday Adelodun close(self->server); 11345a1cd83SSunday Adelodun } 11445a1cd83SSunday Adelodun 11545a1cd83SSunday Adelodun n = recv(self->client, buf, sizeof(buf), 0); 11645a1cd83SSunday Adelodun 11745a1cd83SSunday Adelodun if (variant->socket_type == SOCK_STREAM || 11845a1cd83SSunday Adelodun variant->socket_type == SOCK_SEQPACKET) { 11945a1cd83SSunday Adelodun ASSERT_EQ(0, n); 12045a1cd83SSunday Adelodun } else { 12145a1cd83SSunday Adelodun ASSERT_EQ(-1, n); 12245a1cd83SSunday Adelodun ASSERT_EQ(EAGAIN, errno); 12345a1cd83SSunday Adelodun } 12445a1cd83SSunday Adelodun } 12545a1cd83SSunday Adelodun 12645a1cd83SSunday Adelodun /* Test 2: peer closes with unread data */ 12745a1cd83SSunday Adelodun TEST_F(unix_sock, reset_unread_behavior) 12845a1cd83SSunday Adelodun { 12945a1cd83SSunday Adelodun char buf[16] = {}; 13045a1cd83SSunday Adelodun ssize_t n; 13145a1cd83SSunday Adelodun 13245a1cd83SSunday Adelodun /* Send data that will remain unread */ 13345a1cd83SSunday Adelodun send(self->client, "hello", 5, 0); 13445a1cd83SSunday Adelodun 13545a1cd83SSunday Adelodun if (variant->socket_type == SOCK_DGRAM) { 13645a1cd83SSunday Adelodun /* No real connection, just close the server */ 13745a1cd83SSunday Adelodun close(self->server); 13845a1cd83SSunday Adelodun } else { 13945a1cd83SSunday Adelodun self->child = accept(self->server, NULL, NULL); 14045a1cd83SSunday Adelodun ASSERT_LT(-1, self->child); 14145a1cd83SSunday Adelodun 14245a1cd83SSunday Adelodun /* Peer closes before client reads */ 14345a1cd83SSunday Adelodun close(self->child); 14445a1cd83SSunday Adelodun } 14545a1cd83SSunday Adelodun 14645a1cd83SSunday Adelodun n = recv(self->client, buf, sizeof(buf), 0); 14745a1cd83SSunday Adelodun ASSERT_EQ(-1, n); 14845a1cd83SSunday Adelodun 14945a1cd83SSunday Adelodun if (variant->socket_type == SOCK_STREAM || 15045a1cd83SSunday Adelodun variant->socket_type == SOCK_SEQPACKET) { 15145a1cd83SSunday Adelodun ASSERT_EQ(ECONNRESET, errno); 15245a1cd83SSunday Adelodun } else { 15345a1cd83SSunday Adelodun ASSERT_EQ(EAGAIN, errno); 15445a1cd83SSunday Adelodun } 15545a1cd83SSunday Adelodun } 15645a1cd83SSunday Adelodun 15745a1cd83SSunday Adelodun /* Test 3: closing unaccepted (embryo) server socket should reset client. */ 15845a1cd83SSunday Adelodun TEST_F(unix_sock, reset_closed_embryo) 15945a1cd83SSunday Adelodun { 16045a1cd83SSunday Adelodun char buf[16] = {}; 16145a1cd83SSunday Adelodun ssize_t n; 16245a1cd83SSunday Adelodun 163*e254c212SJakub Kicinski if (variant->socket_type == SOCK_DGRAM) { 164*e254c212SJakub Kicinski snprintf(_metadata->results->reason, 165*e254c212SJakub Kicinski sizeof(_metadata->results->reason), 166*e254c212SJakub Kicinski "Test only applies to SOCK_STREAM and SOCK_SEQPACKET"); 167*e254c212SJakub Kicinski exit(KSFT_XFAIL); 168*e254c212SJakub Kicinski } 16945a1cd83SSunday Adelodun 17045a1cd83SSunday Adelodun /* Close server without accept()ing */ 17145a1cd83SSunday Adelodun close(self->server); 17245a1cd83SSunday Adelodun 17345a1cd83SSunday Adelodun n = recv(self->client, buf, sizeof(buf), 0); 17445a1cd83SSunday Adelodun 17545a1cd83SSunday Adelodun ASSERT_EQ(-1, n); 17645a1cd83SSunday Adelodun ASSERT_EQ(ECONNRESET, errno); 17745a1cd83SSunday Adelodun } 17845a1cd83SSunday Adelodun 17945a1cd83SSunday Adelodun TEST_HARNESS_MAIN 18045a1cd83SSunday Adelodun 181