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