File Index
Files and directories
Sign in
FILE

unix_connreset.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/af_unix/unix_connreset.c C
  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
~/ Read-only file preview.