Netdev List
 help / color / mirror / Atom feed
* [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state
@ 2026-07-18 18:29 John Ericson
  2026-07-18 18:29 ` [PATCH net v3 2/2] selftests/net/af_unix: test listen() rejects wrong socket states John Ericson
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: John Ericson @ 2026-07-18 18:29 UTC (permalink / raw)
  To: Kuniyuki Iwashima, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
	Cong Wang, Sergei Zimmerman, netdev, linux-kernel

From: John Ericson <mail@johnericson.me>

Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err
= -EINVAL and the socket-state check in unix_listen(). Since
prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX
socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is
already connected) now silently returns success without doing anything,
instead of failing with EINVAL as it did before.

Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
Assisted-by: Claude:claude-fable-5
Signed-off-by: John Ericson <mail@johnericson.me>
---
 net/unix/af_unix.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index f7a9d55eee8a..10ed9421e43a 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -823,6 +823,7 @@ static int unix_listen(struct socket *sock, int backlog)
 	if (err)
 		goto out;
 	unix_state_lock(sk);
+	err = -EINVAL;
 	if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
 		goto out_unlock;
 	if (backlog > sk->sk_max_ack_backlog)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net v3 2/2] selftests/net/af_unix: test listen() rejects wrong socket states
  2026-07-18 18:29 [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state John Ericson
@ 2026-07-18 18:29 ` John Ericson
  2026-07-23 16:12 ` [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state Jakub Kicinski
  2026-07-24 22:20 ` patchwork-bot+netdevbpf
  2 siblings, 0 replies; 6+ messages in thread
From: John Ericson @ 2026-07-18 18:29 UTC (permalink / raw)
  To: Kuniyuki Iwashima, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
	Cong Wang, Sergei Zimmerman, netdev, linux-kernel

From: John Ericson <mail@johnericson.me>

Add a regression test for the unix_listen() state check. The key case is
listen() on a bound socket that has already been connected: it is no
longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A
prepare_peercred() call slipped in ahead of that check once left err at 0
and made listen() silently succeed there instead; this guards against a
repeat.

The neighbouring outcomes are covered too so they cannot regress the same
way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a
socket already in TCP_LISTEN is allowed, and an unbound socket fails with
EINVAL.

Each case runs for both listenable socket types (SOCK_STREAM and
SOCK_SEQPACKET) and both pathname and abstract addresses.

Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: John Ericson <mail@johnericson.me>
---
 .../testing/selftests/net/af_unix/.gitignore  |   1 +
 tools/testing/selftests/net/af_unix/Makefile  |   1 +
 .../selftests/net/af_unix/unix_listen.c       | 187 ++++++++++++++++++
 3 files changed, 189 insertions(+)
 create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c

diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
index 240b26740c9e..973176644103 100644
--- a/tools/testing/selftests/net/af_unix/.gitignore
+++ b/tools/testing/selftests/net/af_unix/.gitignore
@@ -6,3 +6,4 @@ scm_rights
 so_peek_off
 unix_connect
 unix_connreset
+unix_listen
diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
index 4c0375e28bbe..57d159803a3a 100644
--- a/tools/testing/selftests/net/af_unix/Makefile
+++ b/tools/testing/selftests/net/af_unix/Makefile
@@ -14,6 +14,7 @@ TEST_GEN_PROGS := \
 	so_peek_off \
 	unix_connect \
 	unix_connreset \
+	unix_listen \
 # end of TEST_GEN_PROGS
 
 include ../../lib.mk
diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c
new file mode 100644
index 000000000000..416fa3e5bfe9
--- /dev/null
+++ b/tools/testing/selftests/net/af_unix/unix_listen.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Tests for the state checks in AF_UNIX listen().
+ *
+ * The central case is a regression test: listen() on a bound socket that
+ * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must
+ * fail with EINVAL.  A prior change accidentally let it return success
+ * without doing anything, because a helper called in between reset the
+ * error code to 0.  The neighbouring checks (unbound, already listening)
+ * are tested too so they cannot silently regress the same way.
+ *
+ * Every case runs for both listenable socket types (SOCK_STREAM and
+ * SOCK_SEQPACKET) and both pathname and abstract addresses.
+ */
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "kselftest_harness.h"
+
+#define SK_NAME		"unix_listen_sk"
+#define SRV_NAME	"unix_listen_srv"
+
+FIXTURE(unix_listen)
+{
+	int sk;			/* socket under test */
+	int server;		/* a listening peer, when a test needs one */
+	struct sockaddr_un addr, srv_addr;
+	socklen_t addrlen, srv_addrlen;
+};
+
+FIXTURE_VARIANT(unix_listen)
+{
+	int type;
+	int abstract;
+};
+
+FIXTURE_VARIANT_ADD(unix_listen, stream_pathname)
+{
+	.type = SOCK_STREAM,
+	.abstract = 0,
+};
+
+FIXTURE_VARIANT_ADD(unix_listen, stream_abstract)
+{
+	.type = SOCK_STREAM,
+	.abstract = 1,
+};
+
+FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname)
+{
+	.type = SOCK_SEQPACKET,
+	.abstract = 0,
+};
+
+FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract)
+{
+	.type = SOCK_SEQPACKET,
+	.abstract = 1,
+};
+
+/* Fill @addr with a pathname or abstract address named @name. */
+static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name,
+			       int abstract)
+{
+	size_t len = strlen(name);
+
+	memset(addr, 0, sizeof(*addr));
+	addr->sun_family = AF_UNIX;
+	/* An abstract address leads with a NUL and has no filesystem entry. */
+	memcpy(addr->sun_path + (abstract ? 1 : 0), name, len);
+
+	return offsetof(struct sockaddr_un, sun_path) + len + 1;
+}
+
+FIXTURE_SETUP(unix_listen)
+{
+	self->sk = -1;
+	self->server = -1;
+	self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract);
+	self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME,
+					  variant->abstract);
+}
+
+FIXTURE_TEARDOWN(unix_listen)
+{
+	if (self->sk >= 0)
+		close(self->sk);
+	if (self->server >= 0)
+		close(self->server);
+
+	/* Pathname sockets leave a filesystem entry behind; abstract ones do not. */
+	if (!variant->abstract) {
+		remove(SK_NAME);
+		remove(SRV_NAME);
+	}
+}
+
+/* A bound socket in TCP_CLOSE is the normal, allowed case. */
+TEST_F(unix_listen, bound_is_ok)
+{
+	int err;
+
+	self->sk = socket(AF_UNIX, variant->type, 0);
+	ASSERT_LE(0, self->sk);
+
+	err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
+	ASSERT_EQ(0, err);
+
+	err = listen(self->sk, 8);
+	EXPECT_EQ(0, err);
+}
+
+/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */
+TEST_F(unix_listen, relisten_is_ok)
+{
+	int err;
+
+	self->sk = socket(AF_UNIX, variant->type, 0);
+	ASSERT_LE(0, self->sk);
+
+	err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
+	ASSERT_EQ(0, err);
+
+	err = listen(self->sk, 8);
+	ASSERT_EQ(0, err);
+
+	err = listen(self->sk, 16);
+	EXPECT_EQ(0, err);
+}
+
+/* listen() on an unbound socket fails: there is nothing to listen on. */
+TEST_F(unix_listen, unbound_is_einval)
+{
+	int err;
+
+	self->sk = socket(AF_UNIX, variant->type, 0);
+	ASSERT_LE(0, self->sk);
+
+	err = listen(self->sk, 8);
+	EXPECT_EQ(-1, err);
+	EXPECT_EQ(EINVAL, errno);
+}
+
+/*
+ * The regression: a bound socket that has already been connected is not in
+ * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather
+ * than quietly succeeding.
+ */
+TEST_F(unix_listen, connected_is_einval)
+{
+	int err;
+
+	self->server = socket(AF_UNIX, variant->type, 0);
+	ASSERT_LE(0, self->server);
+
+	err = bind(self->server, (struct sockaddr *)&self->srv_addr,
+		   self->srv_addrlen);
+	ASSERT_EQ(0, err);
+
+	err = listen(self->server, 8);
+	ASSERT_EQ(0, err);
+
+	self->sk = socket(AF_UNIX, variant->type, 0);
+	ASSERT_LE(0, self->sk);
+
+	/* Bind first so the unbound check does not mask the state check. */
+	err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen);
+	ASSERT_EQ(0, err);
+
+	err = connect(self->sk, (struct sockaddr *)&self->srv_addr,
+		      self->srv_addrlen);
+	ASSERT_EQ(0, err);
+
+	err = listen(self->sk, 8);
+	EXPECT_EQ(-1, err);
+	EXPECT_EQ(EINVAL, errno);
+}
+
+TEST_HARNESS_MAIN
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state
  2026-07-18 18:29 [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state John Ericson
  2026-07-18 18:29 ` [PATCH net v3 2/2] selftests/net/af_unix: test listen() rejects wrong socket states John Ericson
@ 2026-07-23 16:12 ` Jakub Kicinski
  2026-07-23 20:58   ` John Ericson
  2026-07-24 22:20 ` patchwork-bot+netdevbpf
  2 siblings, 1 reply; 6+ messages in thread
From: Jakub Kicinski @ 2026-07-23 16:12 UTC (permalink / raw)
  To: John Ericson
  Cc: Kuniyuki Iwashima, David S . Miller, Eric Dumazet, Paolo Abeni,
	John Ericson, Simon Horman, Christian Brauner, David Rheinsberg,
	Cong Wang, Sergei Zimmerman, netdev, linux-kernel

On Sat, 18 Jul 2026 14:29:01 -0400 John Ericson wrote:
> Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
> reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err
> = -EINVAL and the socket-state check in unix_listen(). Since
> prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX
> socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is
> already connected) now silently returns success without doing anything,
> instead of failing with EINVAL as it did before.

As the bug proves this

	err = x
	if (cond)
		goto bla;

is a fragile pattern. People do this to avoid the extra braces AFAIU
but it's not worth it. Can you move the error setting before the jumps
please?

	if (cond) {
		err = x;
		goto bla;
	}
-- 
pw-bot: cr

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state
  2026-07-23 16:12 ` [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state Jakub Kicinski
@ 2026-07-23 20:58   ` John Ericson
  2026-07-24 22:09     ` Jakub Kicinski
  0 siblings, 1 reply; 6+ messages in thread
From: John Ericson @ 2026-07-23 20:58 UTC (permalink / raw)
  To: Jakub Kicinski, John Ericson
  Cc: Kuniyuki Iwashima, David S . Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, Christian Brauner, David Rheinsberg, Cong Wang,
	Sergei Zimmerman, network dev, LKML

On Thu, Jul 23, 2026, at 12:12 PM, Jakub Kicinski wrote:
> On Sat, 18 Jul 2026 14:29:01 -0400 John Ericson wrote:
> > Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
> > reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err
> > = -EINVAL and the socket-state check in unix_listen(). Since
> > prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX
> > socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is
> > already connected) now silently returns success without doing anything,
> > instead of failing with EINVAL as it did before.
> 
> As the bug proves this
> 
> err = x
> if (cond)
> goto bla;
> 
> is a fragile pattern. People do this to avoid the extra braces AFAIU
> but it's not worth it. Can you move the error setting before the jumps
> please?
> 
> if (cond) {
> err = x;
> goto bla;
> }
> -- 
> pw-bot: cr
> 

Ahahaha. I had the exact same thought!

The original revision of this [1] indeed had the error handling reworked
like that, but Kuniyuki Iwashima asked me to submit the simple 1-line
patch to "net" first, and only submit the cleanup to "net-next" later,
so that is what I was doing.

Is that OK with you, too, I hope?

John

[1]: https://lore.kernel.org/all/20260702202018.2280336-1-John.Ericson@Obsidian.Systems/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state
  2026-07-23 20:58   ` John Ericson
@ 2026-07-24 22:09     ` Jakub Kicinski
  0 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2026-07-24 22:09 UTC (permalink / raw)
  To: John Ericson
  Cc: John Ericson, Kuniyuki Iwashima, David S . Miller, Eric Dumazet,
	Paolo Abeni, Simon Horman, Christian Brauner, David Rheinsberg,
	Cong Wang, Sergei Zimmerman, network dev, LKML

On Thu, 23 Jul 2026 16:58:16 -0400 John Ericson wrote:
> Ahahaha. I had the exact same thought!
> 
> The original revision of this [1] indeed had the error handling reworked
> like that, but Kuniyuki Iwashima asked me to submit the simple 1-line
> patch to "net" first, and only submit the cleanup to "net-next" later,
> so that is what I was doing.
> 
> Is that OK with you, too, I hope?

Oh, you should probably mention the net-next follow up under the ---
separator or in the cover letter next time.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state
  2026-07-18 18:29 [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state John Ericson
  2026-07-18 18:29 ` [PATCH net v3 2/2] selftests/net/af_unix: test listen() rejects wrong socket states John Ericson
  2026-07-23 16:12 ` [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state Jakub Kicinski
@ 2026-07-24 22:20 ` patchwork-bot+netdevbpf
  2 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-07-24 22:20 UTC (permalink / raw)
  To: John Ericson
  Cc: kuniyu, davem, edumazet, kuba, pabeni, mail, horms, brauner,
	david, cwang, sergei, netdev, linux-kernel

Hello:

This series was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Sat, 18 Jul 2026 14:29:01 -0400 you wrote:
> From: John Ericson <mail@johnericson.me>
> 
> Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for
> reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err
> = -EINVAL and the socket-state check in unix_listen(). Since
> prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX
> socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is
> already connected) now silently returns success without doing anything,
> instead of failing with EINVAL as it did before.
> 
> [...]

Here is the summary with links:
  - [net,v3,1/2] af_unix: fix listen() succeeding on sockets in the wrong state
    https://git.kernel.org/netdev/net/c/f0d9c3ffc2b5
  - [net,v3,2/2] selftests/net/af_unix: test listen() rejects wrong socket states
    https://git.kernel.org/netdev/net/c/7f57c650d08b

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-07-24 22:21 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-18 18:29 [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state John Ericson
2026-07-18 18:29 ` [PATCH net v3 2/2] selftests/net/af_unix: test listen() rejects wrong socket states John Ericson
2026-07-23 16:12 ` [PATCH net v3 1/2] af_unix: fix listen() succeeding on sockets in the wrong state Jakub Kicinski
2026-07-23 20:58   ` John Ericson
2026-07-24 22:09     ` Jakub Kicinski
2026-07-24 22:20 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox