| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Selftest for AF_UNIX socket close and ECONNRESET behaviour.
- *
- * This test verifies:
- * 1. SOCK_STREAM returns EOF when the peer closes normally.
- * 2. SOCK_STREAM returns ECONNRESET if peer closes with unread data.
- * 3. SOCK_SEQPACKET returns EOF when the peer closes normally.
- * 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data.
- * 5. SOCK_DGRAM does not return ECONNRESET when the peer closes.
- *
- * These tests document the intended Linux behaviour.
- *
- */
- #define _GNU_SOURCE
- #include <string.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <errno.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include "../../kselftest_harness.h"
- #define SOCK_PATH "/tmp/af_unix_connreset.sock"
- static void remove_socket_file(void)
- {
- unlink(SOCK_PATH);
- }
- FIXTURE(unix_sock)
- {
- int server;
- int client;
- int child;
- };
- FIXTURE_VARIANT(unix_sock)
- {
- int socket_type;
- const char *name;
- };
- FIXTURE_VARIANT_ADD(unix_sock, stream) {
- .socket_type = SOCK_STREAM,
- .name = "SOCK_STREAM",
- };
- FIXTURE_VARIANT_ADD(unix_sock, dgram) {
- .socket_type = SOCK_DGRAM,
- .name = "SOCK_DGRAM",
- };
- FIXTURE_VARIANT_ADD(unix_sock, seqpacket) {
- .socket_type = SOCK_SEQPACKET,
- .name = "SOCK_SEQPACKET",
- };
- FIXTURE_SETUP(unix_sock)
- {
- struct sockaddr_un addr = {};
- int err;
- addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, SOCK_PATH);
- remove_socket_file();
- self->server = socket(AF_UNIX, variant->socket_type, 0);
- ASSERT_LT(-1, self->server);
- err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr));
- ASSERT_EQ(0, err);
- if (variant->socket_type == SOCK_STREAM ||
- variant->socket_type == SOCK_SEQPACKET) {
- err = listen(self->server, 1);
- ASSERT_EQ(0, err);
- }
- self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0);
- ASSERT_LT(-1, self->client);
- err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr));
- ASSERT_EQ(0, err);
- }
- FIXTURE_TEARDOWN(unix_sock)
- {
- if (variant->socket_type == SOCK_STREAM ||
- variant->socket_type == SOCK_SEQPACKET)
- close(self->child);
- close(self->client);
- close(self->server);
- remove_socket_file();
- }
- /* Test 1: peer closes normally */
- TEST_F(unix_sock, eof)
- {
- char buf[16] = {};
- ssize_t n;
- if (variant->socket_type == SOCK_STREAM ||
- variant->socket_type == SOCK_SEQPACKET) {
- self->child = accept(self->server, NULL, NULL);
- ASSERT_LT(-1, self->child);
- close(self->child);
- } else {
- close(self->server);
- }
- n = recv(self->client, buf, sizeof(buf), 0);
- if (variant->socket_type == SOCK_STREAM ||
- variant->socket_type == SOCK_SEQPACKET) {
- ASSERT_EQ(0, n);
- } else {
- ASSERT_EQ(-1, n);
- ASSERT_EQ(EAGAIN, errno);
- }
- }
- /* Test 2: peer closes with unread data */
- TEST_F(unix_sock, reset_unread_behavior)
- {
- char buf[16] = {};
- ssize_t n;
- /* Send data that will remain unread */
- send(self->client, "hello", 5, 0);
- if (variant->socket_type == SOCK_DGRAM) {
- /* No real connection, just close the server */
- close(self->server);
- } else {
- self->child = accept(self->server, NULL, NULL);
- ASSERT_LT(-1, self->child);
- /* Peer closes before client reads */
- close(self->child);
- }
- n = recv(self->client, buf, sizeof(buf), 0);
- ASSERT_EQ(-1, n);
- if (variant->socket_type == SOCK_STREAM ||
- variant->socket_type == SOCK_SEQPACKET) {
- ASSERT_EQ(ECONNRESET, errno);
- } else {
- ASSERT_EQ(EAGAIN, errno);
- }
- }
- /* Test 3: closing unaccepted (embryo) server socket should reset client. */
- TEST_F(unix_sock, reset_closed_embryo)
- {
- char buf[16] = {};
- ssize_t n;
- if (variant->socket_type == SOCK_DGRAM) {
- snprintf(_metadata->results->reason,
- sizeof(_metadata->results->reason),
- "Test only applies to SOCK_STREAM and SOCK_SEQPACKET");
- exit(KSFT_XFAIL);
- }
- /* Close server without accept()ing */
- close(self->server);
- n = recv(self->client, buf, sizeof(buf), 0);
- ASSERT_EQ(-1, n);
- ASSERT_EQ(ECONNRESET, errno);
- }
- TEST_HARNESS_MAIN
|