* [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics [not found] <20251112212026.31441-1-adelodunolaoluwa.ref@yahoo.com> @ 2025-11-12 21:20 ` Sunday Adelodun 2025-11-12 22:04 ` Kuniyuki Iwashima 0 siblings, 1 reply; 7+ messages in thread From: Sunday Adelodun @ 2025-11-12 21:20 UTC (permalink / raw) To: kuniyu, davem, edumazet, kuba, pabeni, horms, shuah Cc: skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees, Sunday Adelodun Add selftests to verify and document Linux’s intended behaviour for UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. The tests verify that: 1. SOCK_STREAM returns EOF when the peer closes normally. 2. SOCK_STREAM returns ECONNRESET if the 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. This follows up on review feedback suggesting a selftest to clarify Linux’s semantics. Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> --- tools/testing/selftests/net/.gitignore | 1 + tools/testing/selftests/net/af_unix/Makefile | 1 + .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 439101b518ee..e89a60581a13 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -65,3 +65,4 @@ udpgso udpgso_bench_rx udpgso_bench_tx unix_connect +unix_connreset diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index de805cbbdf69..5826a8372451 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ scm_pidfd \ scm_rights \ unix_connect \ + unix_connreset \ # end of TEST_GEN_PROGS include ../../lib.mk diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c new file mode 100644 index 000000000000..9413f8a0814f --- /dev/null +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c @@ -0,0 +1,178 @@ +// 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 <stdlib.h> +#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) & self->child > 0) + 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; + + if (variant->socket_type == SOCK_DGRAM) { + /* No real connection, just close the server */ + close(self->server); + } else { + /* Establish full connection first */ + self->child = accept(self->server, NULL, NULL); + ASSERT_LT(-1, self->child); + + /* Send data that will remain unread */ + send(self->client, "hello", 5, 0); + + /* 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) + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); + + /* 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 + -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics 2025-11-12 21:20 ` [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics Sunday Adelodun @ 2025-11-12 22:04 ` Kuniyuki Iwashima 2025-11-13 5:24 ` Sunday Adelodun 0 siblings, 1 reply; 7+ messages in thread From: Kuniyuki Iwashima @ 2025-11-12 22:04 UTC (permalink / raw) To: Sunday Adelodun Cc: davem, edumazet, kuba, pabeni, horms, shuah, skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees On Wed, Nov 12, 2025 at 1:20 PM Sunday Adelodun <adelodunolaoluwa@yahoo.com> wrote: > > Add selftests to verify and document Linux’s intended behaviour for > UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. > The tests verify that: > > 1. SOCK_STREAM returns EOF when the peer closes normally. > 2. SOCK_STREAM returns ECONNRESET if the 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. > > This follows up on review feedback suggesting a selftest to clarify > Linux’s semantics. > > Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> > Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> > --- > tools/testing/selftests/net/.gitignore | 1 + > tools/testing/selftests/net/af_unix/Makefile | 1 + > .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ > 3 files changed, 180 insertions(+) > create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c > > diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore > index 439101b518ee..e89a60581a13 100644 > --- a/tools/testing/selftests/net/.gitignore > +++ b/tools/testing/selftests/net/.gitignore > @@ -65,3 +65,4 @@ udpgso > udpgso_bench_rx > udpgso_bench_tx > unix_connect > +unix_connreset > diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile > index de805cbbdf69..5826a8372451 100644 > --- a/tools/testing/selftests/net/af_unix/Makefile > +++ b/tools/testing/selftests/net/af_unix/Makefile > @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ > scm_pidfd \ > scm_rights \ > unix_connect \ > + unix_connreset \ > # end of TEST_GEN_PROGS > > include ../../lib.mk > diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c > new file mode 100644 > index 000000000000..9413f8a0814f > --- /dev/null > +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c > @@ -0,0 +1,178 @@ > +// 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 <stdlib.h> > +#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) & self->child > 0) > + 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; > + > + if (variant->socket_type == SOCK_DGRAM) { > + /* No real connection, just close the server */ > + close(self->server); > + } else { > + /* Establish full connection first */ > + self->child = accept(self->server, NULL, NULL); > + ASSERT_LT(-1, self->child); > + > + /* Send data that will remain unread */ > + send(self->client, "hello", 5, 0); Could you move this send() before "if (...)" because we want to test unread_data behaviour for SOCK_DGRAM too ? Otherwise looks good, so with that fixed: Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Thanks! > + > + /* 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) > + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); > + > + /* 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 > + > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics 2025-11-12 22:04 ` Kuniyuki Iwashima @ 2025-11-13 5:24 ` Sunday Adelodun 2025-11-13 6:10 ` Kuniyuki Iwashima 0 siblings, 1 reply; 7+ messages in thread From: Sunday Adelodun @ 2025-11-13 5:24 UTC (permalink / raw) To: Kuniyuki Iwashima Cc: davem, edumazet, kuba, pabeni, horms, shuah, skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees On 11/12/25 23:04, Kuniyuki Iwashima wrote: > On Wed, Nov 12, 2025 at 1:20 PM Sunday Adelodun > <adelodunolaoluwa@yahoo.com> wrote: >> Add selftests to verify and document Linux’s intended behaviour for >> UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. >> The tests verify that: >> >> 1. SOCK_STREAM returns EOF when the peer closes normally. >> 2. SOCK_STREAM returns ECONNRESET if the 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. >> >> This follows up on review feedback suggesting a selftest to clarify >> Linux’s semantics. >> >> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> >> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> >> --- >> tools/testing/selftests/net/.gitignore | 1 + >> tools/testing/selftests/net/af_unix/Makefile | 1 + >> .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ >> 3 files changed, 180 insertions(+) >> create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c >> >> diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore >> index 439101b518ee..e89a60581a13 100644 >> --- a/tools/testing/selftests/net/.gitignore >> +++ b/tools/testing/selftests/net/.gitignore >> @@ -65,3 +65,4 @@ udpgso >> udpgso_bench_rx >> udpgso_bench_tx >> unix_connect >> +unix_connreset >> diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile >> index de805cbbdf69..5826a8372451 100644 >> --- a/tools/testing/selftests/net/af_unix/Makefile >> +++ b/tools/testing/selftests/net/af_unix/Makefile >> @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ >> scm_pidfd \ >> scm_rights \ >> unix_connect \ >> + unix_connreset \ >> # end of TEST_GEN_PROGS >> >> include ../../lib.mk >> diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c >> new file mode 100644 >> index 000000000000..9413f8a0814f >> --- /dev/null >> +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c >> @@ -0,0 +1,178 @@ >> +// 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 <stdlib.h> >> +#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) & self->child > 0) >> + 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; >> + >> + if (variant->socket_type == SOCK_DGRAM) { >> + /* No real connection, just close the server */ >> + close(self->server); >> + } else { >> + /* Establish full connection first */ >> + self->child = accept(self->server, NULL, NULL); >> + ASSERT_LT(-1, self->child); >> + >> + /* Send data that will remain unread */ >> + send(self->client, "hello", 5, 0); > Could you move this send() before "if (...)" because we want > to test unread_data behaviour for SOCK_DGRAM too ? > > Otherwise looks good, so with that fixed: > > Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> > > Thanks! Thank you for the prompt response. I thought of putting the send before the if the statement , but I was afraid STREAM and SEQPACKET connections won't be accepted before data sending. I will start working on v5. That part will look like this now: /* 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 { /* Establish full connection first */ 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); } } Thank you once again. > > >> + >> + /* 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) >> + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); >> + >> + /* 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 >> + >> -- >> 2.43.0 >> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics 2025-11-13 5:24 ` Sunday Adelodun @ 2025-11-13 6:10 ` Kuniyuki Iwashima 2025-11-13 6:41 ` Sunday Adelodun 0 siblings, 1 reply; 7+ messages in thread From: Kuniyuki Iwashima @ 2025-11-13 6:10 UTC (permalink / raw) To: Sunday Adelodun Cc: davem, edumazet, kuba, pabeni, horms, shuah, skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees On Wed, Nov 12, 2025 at 9:25 PM Sunday Adelodun <adelodunolaoluwa@yahoo.com> wrote: > > On 11/12/25 23:04, Kuniyuki Iwashima wrote: > > On Wed, Nov 12, 2025 at 1:20 PM Sunday Adelodun > > <adelodunolaoluwa@yahoo.com> wrote: > >> Add selftests to verify and document Linux’s intended behaviour for > >> UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. > >> The tests verify that: > >> > >> 1. SOCK_STREAM returns EOF when the peer closes normally. > >> 2. SOCK_STREAM returns ECONNRESET if the 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. > >> > >> This follows up on review feedback suggesting a selftest to clarify > >> Linux’s semantics. > >> > >> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> > >> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> > >> --- > >> tools/testing/selftests/net/.gitignore | 1 + > >> tools/testing/selftests/net/af_unix/Makefile | 1 + > >> .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ > >> 3 files changed, 180 insertions(+) > >> create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c > >> > >> diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore > >> index 439101b518ee..e89a60581a13 100644 > >> --- a/tools/testing/selftests/net/.gitignore > >> +++ b/tools/testing/selftests/net/.gitignore > >> @@ -65,3 +65,4 @@ udpgso > >> udpgso_bench_rx > >> udpgso_bench_tx > >> unix_connect > >> +unix_connreset > >> diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile > >> index de805cbbdf69..5826a8372451 100644 > >> --- a/tools/testing/selftests/net/af_unix/Makefile > >> +++ b/tools/testing/selftests/net/af_unix/Makefile > >> @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ > >> scm_pidfd \ > >> scm_rights \ > >> unix_connect \ > >> + unix_connreset \ > >> # end of TEST_GEN_PROGS > >> > >> include ../../lib.mk > >> diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c > >> new file mode 100644 > >> index 000000000000..9413f8a0814f > >> --- /dev/null > >> +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c > >> @@ -0,0 +1,178 @@ > >> +// 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 <stdlib.h> > >> +#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) & self->child > 0) > >> + 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; > >> + > >> + if (variant->socket_type == SOCK_DGRAM) { > >> + /* No real connection, just close the server */ > >> + close(self->server); > >> + } else { > >> + /* Establish full connection first */ > >> + self->child = accept(self->server, NULL, NULL); > >> + ASSERT_LT(-1, self->child); > >> + > >> + /* Send data that will remain unread */ > >> + send(self->client, "hello", 5, 0); > > Could you move this send() before "if (...)" because we want > > to test unread_data behaviour for SOCK_DGRAM too ? > > > > Otherwise looks good, so with that fixed: > > > > Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> > > > > Thanks! > Thank you for the prompt response. > I thought of putting the send before the if the statement , but I was afraid > STREAM and SEQPACKET connections won't be accepted before data sending. connect() create the paired child socket and accept() allocates a file descriptor to expose the socket to user space. In that sense, the comment above accept() sounds a bit weird ;) > > I will start working on v5. > > That part will look like this now: > /* 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 { > /* Establish full connection first */ > 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); > } > } > > Thank you once again. > > > > > >> + > >> + /* 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) > >> + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); > >> + > >> + /* 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 > >> + > >> -- > >> 2.43.0 > >> > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics 2025-11-13 6:10 ` Kuniyuki Iwashima @ 2025-11-13 6:41 ` Sunday Adelodun 2025-11-13 6:52 ` Kuniyuki Iwashima 0 siblings, 1 reply; 7+ messages in thread From: Sunday Adelodun @ 2025-11-13 6:41 UTC (permalink / raw) To: Kuniyuki Iwashima Cc: davem, edumazet, kuba, pabeni, horms, shuah, skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees On 11/13/25 07:10, Kuniyuki Iwashima wrote: > On Wed, Nov 12, 2025 at 9:25 PM Sunday Adelodun > <adelodunolaoluwa@yahoo.com> wrote: >> On 11/12/25 23:04, Kuniyuki Iwashima wrote: >>> On Wed, Nov 12, 2025 at 1:20 PM Sunday Adelodun >>> <adelodunolaoluwa@yahoo.com> wrote: >>>> Add selftests to verify and document Linux’s intended behaviour for >>>> UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. >>>> The tests verify that: >>>> >>>> 1. SOCK_STREAM returns EOF when the peer closes normally. >>>> 2. SOCK_STREAM returns ECONNRESET if the 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. >>>> >>>> This follows up on review feedback suggesting a selftest to clarify >>>> Linux’s semantics. >>>> >>>> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> >>>> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> >>>> --- >>>> tools/testing/selftests/net/.gitignore | 1 + >>>> tools/testing/selftests/net/af_unix/Makefile | 1 + >>>> .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ >>>> 3 files changed, 180 insertions(+) >>>> create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c >>>> >>>> diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore >>>> index 439101b518ee..e89a60581a13 100644 >>>> --- a/tools/testing/selftests/net/.gitignore >>>> +++ b/tools/testing/selftests/net/.gitignore >>>> @@ -65,3 +65,4 @@ udpgso >>>> udpgso_bench_rx >>>> udpgso_bench_tx >>>> unix_connect >>>> +unix_connreset >>>> diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile >>>> index de805cbbdf69..5826a8372451 100644 >>>> --- a/tools/testing/selftests/net/af_unix/Makefile >>>> +++ b/tools/testing/selftests/net/af_unix/Makefile >>>> @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ >>>> scm_pidfd \ >>>> scm_rights \ >>>> unix_connect \ >>>> + unix_connreset \ >>>> # end of TEST_GEN_PROGS >>>> >>>> include ../../lib.mk >>>> diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c >>>> new file mode 100644 >>>> index 000000000000..9413f8a0814f >>>> --- /dev/null >>>> +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c >>>> @@ -0,0 +1,178 @@ >>>> +// 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 <stdlib.h> >>>> +#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) & self->child > 0) >>>> + 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; >>>> + >>>> + if (variant->socket_type == SOCK_DGRAM) { >>>> + /* No real connection, just close the server */ >>>> + close(self->server); >>>> + } else { >>>> + /* Establish full connection first */ >>>> + self->child = accept(self->server, NULL, NULL); >>>> + ASSERT_LT(-1, self->child); >>>> + >>>> + /* Send data that will remain unread */ >>>> + send(self->client, "hello", 5, 0); >>> Could you move this send() before "if (...)" because we want >>> to test unread_data behaviour for SOCK_DGRAM too ? >>> >>> Otherwise looks good, so with that fixed: >>> >>> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> >>> >>> Thanks! >> Thank you for the prompt response. >> I thought of putting the send before the if the statement , but I was afraid >> STREAM and SEQPACKET connections won't be accepted before data sending. > connect() create the paired child socket and accept() > allocates a file descriptor to expose the socket to user > space. Thank you for this explanation. > > In that sense, the comment above accept() sounds a > bit weird ;) I understand. I am changing it to /* Accept client connection */ If this is accepted, I will send v5 immediately Thank you for your guidance. > > >> I will start working on v5. >> >> That part will look like this now: >> /* 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 { >> /* Establish full connection first */ >> 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); >> } >> } >> >> Thank you once again. >>> >>>> + >>>> + /* 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) >>>> + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); >>>> + >>>> + /* 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 >>>> + >>>> -- >>>> 2.43.0 >>>> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics 2025-11-13 6:41 ` Sunday Adelodun @ 2025-11-13 6:52 ` Kuniyuki Iwashima 2025-11-13 7:29 ` Sunday Adelodun 0 siblings, 1 reply; 7+ messages in thread From: Kuniyuki Iwashima @ 2025-11-13 6:52 UTC (permalink / raw) To: Sunday Adelodun Cc: davem, edumazet, kuba, pabeni, horms, shuah, skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees On Wed, Nov 12, 2025 at 10:41 PM Sunday Adelodun <adelodunolaoluwa@yahoo.com> wrote: > > On 11/13/25 07:10, Kuniyuki Iwashima wrote: > > On Wed, Nov 12, 2025 at 9:25 PM Sunday Adelodun > > <adelodunolaoluwa@yahoo.com> wrote: > >> On 11/12/25 23:04, Kuniyuki Iwashima wrote: > >>> On Wed, Nov 12, 2025 at 1:20 PM Sunday Adelodun > >>> <adelodunolaoluwa@yahoo.com> wrote: > >>>> Add selftests to verify and document Linux’s intended behaviour for > >>>> UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. > >>>> The tests verify that: > >>>> > >>>> 1. SOCK_STREAM returns EOF when the peer closes normally. > >>>> 2. SOCK_STREAM returns ECONNRESET if the 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. > >>>> > >>>> This follows up on review feedback suggesting a selftest to clarify > >>>> Linux’s semantics. > >>>> > >>>> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> > >>>> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> > >>>> --- > >>>> tools/testing/selftests/net/.gitignore | 1 + > >>>> tools/testing/selftests/net/af_unix/Makefile | 1 + > >>>> .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ > >>>> 3 files changed, 180 insertions(+) > >>>> create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c > >>>> > >>>> diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore > >>>> index 439101b518ee..e89a60581a13 100644 > >>>> --- a/tools/testing/selftests/net/.gitignore > >>>> +++ b/tools/testing/selftests/net/.gitignore > >>>> @@ -65,3 +65,4 @@ udpgso > >>>> udpgso_bench_rx > >>>> udpgso_bench_tx > >>>> unix_connect > >>>> +unix_connreset > >>>> diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile > >>>> index de805cbbdf69..5826a8372451 100644 > >>>> --- a/tools/testing/selftests/net/af_unix/Makefile > >>>> +++ b/tools/testing/selftests/net/af_unix/Makefile > >>>> @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ > >>>> scm_pidfd \ > >>>> scm_rights \ > >>>> unix_connect \ > >>>> + unix_connreset \ > >>>> # end of TEST_GEN_PROGS > >>>> > >>>> include ../../lib.mk > >>>> diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c > >>>> new file mode 100644 > >>>> index 000000000000..9413f8a0814f > >>>> --- /dev/null > >>>> +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c > >>>> @@ -0,0 +1,178 @@ > >>>> +// 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 <stdlib.h> > >>>> +#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) & self->child > 0) > >>>> + 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; > >>>> + > >>>> + if (variant->socket_type == SOCK_DGRAM) { > >>>> + /* No real connection, just close the server */ > >>>> + close(self->server); > >>>> + } else { > >>>> + /* Establish full connection first */ > >>>> + self->child = accept(self->server, NULL, NULL); > >>>> + ASSERT_LT(-1, self->child); > >>>> + > >>>> + /* Send data that will remain unread */ > >>>> + send(self->client, "hello", 5, 0); > >>> Could you move this send() before "if (...)" because we want > >>> to test unread_data behaviour for SOCK_DGRAM too ? > >>> > >>> Otherwise looks good, so with that fixed: > >>> > >>> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> > >>> > >>> Thanks! > >> Thank you for the prompt response. > >> I thought of putting the send before the if the statement , but I was afraid > >> STREAM and SEQPACKET connections won't be accepted before data sending. > > connect() create the paired child socket and accept() > > allocates a file descriptor to expose the socket to user > > space. > Thank you for this explanation. > > > > In that sense, the comment above accept() sounds a > > bit weird ;) > I understand. > I am changing it to > /* Accept client connection */ I'd remove it since it just repeats the code, but up to you. Thanks! > > If this is accepted, I will send v5 immediately > > Thank you for your guidance. > > > > > >> I will start working on v5. > >> > >> That part will look like this now: > >> /* 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 { > >> /* Establish full connection first */ > >> 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); > >> } > >> } > >> > >> Thank you once again. > >>> > >>>> + > >>>> + /* 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) > >>>> + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); > >>>> + > >>>> + /* 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 > >>>> + > >>>> -- > >>>> 2.43.0 > >>>> > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics 2025-11-13 6:52 ` Kuniyuki Iwashima @ 2025-11-13 7:29 ` Sunday Adelodun 0 siblings, 0 replies; 7+ messages in thread From: Sunday Adelodun @ 2025-11-13 7:29 UTC (permalink / raw) To: Kuniyuki Iwashima Cc: davem, edumazet, kuba, pabeni, horms, shuah, skhan, david.hunter.linux, linux-kernel, netdev, linux-kselftest, linux-kernel-mentees On 11/13/25 07:52, Kuniyuki Iwashima wrote: > On Wed, Nov 12, 2025 at 10:41 PM Sunday Adelodun > <adelodunolaoluwa@yahoo.com> wrote: >> On 11/13/25 07:10, Kuniyuki Iwashima wrote: >>> On Wed, Nov 12, 2025 at 9:25 PM Sunday Adelodun >>> <adelodunolaoluwa@yahoo.com> wrote: >>>> On 11/12/25 23:04, Kuniyuki Iwashima wrote: >>>>> On Wed, Nov 12, 2025 at 1:20 PM Sunday Adelodun >>>>> <adelodunolaoluwa@yahoo.com> wrote: >>>>>> Add selftests to verify and document Linux’s intended behaviour for >>>>>> UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. >>>>>> The tests verify that: >>>>>> >>>>>> 1. SOCK_STREAM returns EOF when the peer closes normally. >>>>>> 2. SOCK_STREAM returns ECONNRESET if the 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. >>>>>> >>>>>> This follows up on review feedback suggesting a selftest to clarify >>>>>> Linux’s semantics. >>>>>> >>>>>> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> >>>>>> Signed-off-by: Sunday Adelodun <adelodunolaoluwa@yahoo.com> >>>>>> --- >>>>>> tools/testing/selftests/net/.gitignore | 1 + >>>>>> tools/testing/selftests/net/af_unix/Makefile | 1 + >>>>>> .../selftests/net/af_unix/unix_connreset.c | 178 ++++++++++++++++++ >>>>>> 3 files changed, 180 insertions(+) >>>>>> create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c >>>>>> >>>>>> diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore >>>>>> index 439101b518ee..e89a60581a13 100644 >>>>>> --- a/tools/testing/selftests/net/.gitignore >>>>>> +++ b/tools/testing/selftests/net/.gitignore >>>>>> @@ -65,3 +65,4 @@ udpgso >>>>>> udpgso_bench_rx >>>>>> udpgso_bench_tx >>>>>> unix_connect >>>>>> +unix_connreset >>>>>> diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile >>>>>> index de805cbbdf69..5826a8372451 100644 >>>>>> --- a/tools/testing/selftests/net/af_unix/Makefile >>>>>> +++ b/tools/testing/selftests/net/af_unix/Makefile >>>>>> @@ -7,6 +7,7 @@ TEST_GEN_PROGS := \ >>>>>> scm_pidfd \ >>>>>> scm_rights \ >>>>>> unix_connect \ >>>>>> + unix_connreset \ >>>>>> # end of TEST_GEN_PROGS >>>>>> >>>>>> include ../../lib.mk >>>>>> diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c >>>>>> new file mode 100644 >>>>>> index 000000000000..9413f8a0814f >>>>>> --- /dev/null >>>>>> +++ b/tools/testing/selftests/net/af_unix/unix_connreset.c >>>>>> @@ -0,0 +1,178 @@ >>>>>> +// 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 <stdlib.h> >>>>>> +#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) & self->child > 0) >>>>>> + 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; >>>>>> + >>>>>> + if (variant->socket_type == SOCK_DGRAM) { >>>>>> + /* No real connection, just close the server */ >>>>>> + close(self->server); >>>>>> + } else { >>>>>> + /* Establish full connection first */ >>>>>> + self->child = accept(self->server, NULL, NULL); >>>>>> + ASSERT_LT(-1, self->child); >>>>>> + >>>>>> + /* Send data that will remain unread */ >>>>>> + send(self->client, "hello", 5, 0); >>>>> Could you move this send() before "if (...)" because we want >>>>> to test unread_data behaviour for SOCK_DGRAM too ? >>>>> >>>>> Otherwise looks good, so with that fixed: >>>>> >>>>> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> >>>>> >>>>> Thanks! >>>> Thank you for the prompt response. >>>> I thought of putting the send before the if the statement , but I was afraid >>>> STREAM and SEQPACKET connections won't be accepted before data sending. >>> connect() create the paired child socket and accept() >>> allocates a file descriptor to expose the socket to user >>> space. >> Thank you for this explanation. >>> In that sense, the comment above accept() sounds a >>> bit weird ;) >> I understand. >> I am changing it to >> /* Accept client connection */ > I'd remove it since it just repeats the code, but up to you. > > Thanks! Sent. I removed the comment, but unfortunately I didn't know it didn't save. so, the comment was sent along with patch. Thank you for the guidance and patience. I am open to any feedback if any. > >> If this is accepted, I will send v5 immediately >> >> Thank you for your guidance. >>> >>>> I will start working on v5. >>>> >>>> That part will look like this now: >>>> /* 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 { >>>> /* Establish full connection first */ >>>> 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); >>>> } >>>> } >>>> >>>> Thank you once again. >>>>>> + >>>>>> + /* 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) >>>>>> + SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); >>>>>> + >>>>>> + /* 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 >>>>>> + >>>>>> -- >>>>>> 2.43.0 >>>>>> ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-11-13 7:30 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20251112212026.31441-1-adelodunolaoluwa.ref@yahoo.com>
2025-11-12 21:20 ` [PATCH v4] selftests: af_unix: Add tests for ECONNRESET and EOF semantics Sunday Adelodun
2025-11-12 22:04 ` Kuniyuki Iwashima
2025-11-13 5:24 ` Sunday Adelodun
2025-11-13 6:10 ` Kuniyuki Iwashima
2025-11-13 6:41 ` Sunday Adelodun
2025-11-13 6:52 ` Kuniyuki Iwashima
2025-11-13 7:29 ` Sunday Adelodun
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox