Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
27static void remove_socket_file(void)
28{
29 unlink(SOCK_PATH);
30}
31
32FIXTURE(unix_sock)
33{
34 int server;
35 int client;
36 int child;
37};
38
39FIXTURE_VARIANT(unix_sock)
40{
41 int socket_type;
42 const char *name;
43};
44
45FIXTURE_VARIANT_ADD(unix_sock, stream) {
46 .socket_type = SOCK_STREAM,
47 .name = "SOCK_STREAM",
48};
49
50FIXTURE_VARIANT_ADD(unix_sock, dgram) {
51 .socket_type = SOCK_DGRAM,
52 .name = "SOCK_DGRAM",
53};
54
55FIXTURE_VARIANT_ADD(unix_sock, seqpacket) {
56 .socket_type = SOCK_SEQPACKET,
57 .name = "SOCK_SEQPACKET",
58};
59
60FIXTURE_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
88FIXTURE_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 */
100TEST_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 */
127TEST_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. */
158TEST_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
179TEST_HARNESS_MAIN
180