[RFC net-next] selftests: af_unix: test zero-length MSG_PEEK with OOB data

From: Sahaj Chaudhari

Date: Thu Oct 08 2026 - 01:52:06 EST


A zero-length MSG_PEEK on an AF_UNIX stream socket can busy-loop when
an OOB skb is queued. Add a regression test that sends MSG_OOB and
checks that recv(..., 0, MSG_PEEK) returns promptly in a child process.
Skip the test when AF_UNIX OOB support is unavailable.

Register the test as a normal generated program so it is included in
the AF_UNIX selftest run.

The regression was fixed by commit 6e5ee08eb585 ("af_unix: Return
immediately when manage_oob() returns NULL for 0-length buffer.").

Tested: fails on 7.0.0-34-generic (expected regression); passes in QEMU
with a kernel built from 0c2669a9f4a1.

Signed-off-by: Sahaj Chaudhari <sahaj123.sc@xxxxxxxxx>
---
.../testing/selftests/net/af_unix/.gitignore | 1 +
tools/testing/selftests/net/af_unix/Makefile | 1 +
.../net/af_unix/unix_oob_zero_peek.c | 89 +++++++++++++++++++
3 files changed, 91 insertions(+)
create mode 100644 tools/testing/selftests/net/af_unix/unix_oob_zero_peek.c

diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
index 954f0958dd03..b95a86fb77f5 100644
--- a/tools/testing/selftests/net/af_unix/.gitignore
+++ b/tools/testing/selftests/net/af_unix/.gitignore
@@ -9,3 +9,4 @@ so_peek_off
unix_connect
unix_connreset
unix_listen
+unix_oob_zero_peek
diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
index a66f10fb0c23..5fbd00741e50 100644
--- a/tools/testing/selftests/net/af_unix/Makefile
+++ b/tools/testing/selftests/net/af_unix/Makefile
@@ -16,6 +16,7 @@ TEST_GEN_PROGS := \
unix_connect \
unix_connreset \
unix_listen \
+ unix_oob_zero_peek \
# end of TEST_GEN_PROGS

TEST_GEN_FILES := scm_rights_denial_lsm.bpf.o
diff --git a/tools/testing/selftests/net/af_unix/unix_oob_zero_peek.c b/tools/testing/selftests/net/af_unix/unix_oob_zero_peek.c
new file mode 100644
index 000000000000..52a14d0901b4
--- /dev/null
+++ b/tools/testing/selftests/net/af_unix/unix_oob_zero_peek.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "kselftest.h"
+
+#define RECV_TIMEOUT_MS 150
+
+struct recv_result {
+ ssize_t result;
+ int error;
+};
+
+int main(void)
+{
+ struct recv_result result;
+ struct pollfd pfd;
+ char byte = 'x';
+ int sockets[2], pipefd[2], status;
+ ssize_t n;
+ pid_t child;
+
+ ksft_print_header();
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets))
+ ksft_exit_fail_perror("socketpair");
+ if (send(sockets[0], &byte, sizeof(byte), MSG_OOB) != sizeof(byte)) {
+ if (errno == EOPNOTSUPP || errno == EINVAL)
+ ksft_exit_skip("AF_UNIX OOB support is unavailable\n");
+ ksft_exit_fail_perror("send MSG_OOB");
+ }
+ if (pipe(pipefd))
+ ksft_exit_fail_perror("pipe");
+
+ ksft_set_plan(1);
+ child = fork();
+ if (child < 0)
+ ksft_exit_fail_perror("fork");
+ if (!child) {
+ struct recv_result child_result;
+
+ close(sockets[0]);
+ close(pipefd[0]);
+ errno = 0;
+ child_result.result = recv(sockets[1], &byte, 0, MSG_PEEK);
+ child_result.error = errno;
+ if (write(pipefd[1], &child_result, sizeof(child_result)) !=
+ sizeof(child_result))
+ _exit(1);
+ _exit(0);
+ }
+
+ close(sockets[1]);
+ close(pipefd[1]);
+ pfd.fd = pipefd[0];
+ pfd.events = POLLIN;
+ n = poll(&pfd, 1, RECV_TIMEOUT_MS);
+ if (n <= 0) {
+ kill(child, SIGKILL);
+ waitpid(child, &status, 0);
+ ksft_test_result_fail("zero-length MSG_PEEK did not return promptly\n");
+ ksft_finished();
+ }
+
+ n = read(pipefd[0], &result, sizeof(result));
+ if (n != sizeof(result)) {
+ waitpid(child, &status, 0);
+ ksft_test_result_fail("worker did not report the recv result\n");
+ ksft_finished();
+ }
+ if (waitpid(child, &status, 0) != child || !WIFEXITED(status) ||
+ WEXITSTATUS(status)) {
+ ksft_test_result_fail("worker did not exit successfully\n");
+ ksft_finished();
+ }
+ if (result.result != 0) {
+ errno = result.error;
+ ksft_test_result_fail("zero-length MSG_PEEK returned an error\n");
+ ksft_finished();
+ }
+
+ ksft_test_result_pass("zero-length MSG_PEEK returned immediately\n");
+ ksft_finished();
+}
--
2.43.0