[PATCH net v2 2/2] selftest: af_unix: Test scm_fds on dgram disconnect.
From: Siddharth Chintamaneni
Date: Wed Nov 11 2026 - 18:00:50 EST
Add dgram_disconnect_fdinfo in scm_rights.c verifying that scm_fds in
/proc/self/fdinfo/<fd> returns to 0 when a connected SOCK_DGRAM socket
with queued SCM_RIGHTS datagrams disconnects via connect(AF_UNSPEC) or
observes a dead peer in send().
Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@xxxxxxxxx>
---
.../selftests/net/af_unix/scm_rights.c | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/tools/testing/selftests/net/af_unix/scm_rights.c b/tools/testing/selftests/net/af_unix/scm_rights.c
index c165f250220a..ced83d3173b7 100644
--- a/tools/testing/selftests/net/af_unix/scm_rights.c
+++ b/tools/testing/selftests/net/af_unix/scm_rights.c
@@ -395,4 +395,83 @@ TEST_F(scm_rights, mixed_lowpoint)
close_sockets(6);
}
+static int count_scm_fds(struct __test_metadata *_metadata, int fd)
+{
+ char path[64], *line = NULL;
+ int scm_fds = -1, ret;
+ size_t unused = 0;
+ FILE *f;
+
+ snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
+
+ f = fopen(path, "r");
+ ASSERT_NE(NULL, f);
+
+ while (getline(&line, &unused, f) != -1) {
+ if (strncmp(line, "scm_fds:", 8))
+ continue;
+
+ ret = sscanf(line + 8, "%d", &scm_fds);
+ ASSERT_EQ(1, ret);
+
+ break;
+ }
+
+ free(line);
+
+ ret = fclose(f);
+ ASSERT_EQ(0, ret);
+
+ return scm_fds;
+}
+
+TEST(dgram_disconnect_fdinfo)
+{
+ const FIXTURE_VARIANT(scm_rights) variant = {
+ .name = "UNIX ",
+ .type = SOCK_DGRAM,
+ };
+ FIXTURE_DATA(scm_rights) self = {};
+ struct sockaddr addr = {
+ .sa_family = AF_UNSPEC,
+ };
+ int ret;
+
+ ret = unshare(CLONE_NEWNET);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, count_sockets(_metadata, &variant));
+
+ create_socketpairs(_metadata, &self, &variant, 2);
+
+ /* 1. Disconnect via connect(AF_UNSPEC) */
+ __send_fd(_metadata, &self, &variant, 0, 0);
+ ASSERT_EQ(2, count_scm_fds(_metadata, self.fd[0]));
+
+ ret = connect(self.fd[0], &addr, sizeof(addr.sa_family));
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, count_scm_fds(_metadata, self.fd[0]));
+
+ /* 2. Dead-peer disconnect via send() */
+ __send_fd(_metadata, &self, &variant, 1, 1);
+ ASSERT_EQ(2, count_scm_fds(_metadata, self.fd[2]));
+
+ ret = close(self.fd[3]);
+ ASSERT_EQ(0, ret);
+
+ ret = send(self.fd[2], "x", 1, 0);
+ ASSERT_EQ(-1, ret);
+ ASSERT_EQ(ECONNREFUSED, errno);
+ ASSERT_EQ(0, count_scm_fds(_metadata, self.fd[2]));
+
+ ret = close(self.fd[0]);
+ ASSERT_EQ(0, ret);
+ ret = close(self.fd[1]);
+ ASSERT_EQ(0, ret);
+ ret = close(self.fd[2]);
+ ASSERT_EQ(0, ret);
+
+ sleep(1);
+ ASSERT_EQ(0, count_sockets(_metadata, &variant));
+}
+
TEST_HARNESS_MAIN
--
2.43.0