xref: /linux/tools/testing/selftests/net/af_unix/unix_connreset.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
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