]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests: af_unix: validate SO_PEEK_OFF advancement and reset
authorSoichiro Ueda <the.latticeheart@gmail.com>
Tue, 10 Mar 2026 07:28:31 +0000 (16:28 +0900)
committerJakub Kicinski <kuba@kernel.org>
Thu, 12 Mar 2026 01:20:17 +0000 (18:20 -0700)
Extend the so_peek_off selftest to ensure the socket peek offset is handled
correctly after both MSG_PEEK and actual data consumption.

Verify that the peek offset advances by the same amount as the number of
bytes read when performing a read with MSG_PEEK.

After exercising SO_PEEK_OFF via MSG_PEEK, drain the receive queue with a
non-peek recv() and verify that it can receive all the content in the
buffer and SO_PEEK_OFF returns back to 0.

The verification after actual data consumption was suggested by Miao Wang
when the original so_peek_off selftest was introduced.

Link: https://lore.kernel.org/all/7B657CC7-B5CA-46D2-8A4B-8AB5FB83C6DA@gmail.com/
Suggested-by: Miao Wang <shankerwangmiao@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Soichiro Ueda <the.latticeheart@gmail.com>
Link: https://patch.msgid.link/20260310072832.127848-1-the.latticeheart@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/testing/selftests/net/af_unix/so_peek_off.c

index 86e7b0fb522d50b3fdc74487689ab70c04d600c4..f6466a717f49393cb23e31a22f14a559eba88936 100644 (file)
@@ -76,6 +76,19 @@ FIXTURE_TEARDOWN(so_peek_off)
                ASSERT_STREQ(str, buf);                         \
        } while (0)
 
+#define peekoffeq(fd, expected)                                        \
+       do {                                                    \
+               socklen_t optlen = sizeof(int);                 \
+               int off = -1;                                   \
+               int ret;                                        \
+                                                               \
+               ret = getsockopt(fd, SOL_SOCKET, SO_PEEK_OFF,   \
+                                &off, &optlen);                \
+               ASSERT_EQ(0, ret);                              \
+               ASSERT_EQ((socklen_t)sizeof(off), optlen);      \
+               ASSERT_EQ(expected, off);                       \
+       } while (0)
+
 #define async                                                  \
        for (pid_t pid = (pid = fork(),                         \
                          pid < 0 ?                             \
@@ -91,7 +104,12 @@ TEST_F(so_peek_off, single_chunk)
        sendeq(self->fd[0], "aaaabbbb", 0);
 
        recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
+       peekoffeq(self->fd[1], 4);
        recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
+       peekoffeq(self->fd[1], 8);
+
+       recveq(self->fd[1], "aaaabbbb", 8, 0);
+       peekoffeq(self->fd[1], 0);
 }
 
 TEST_F(so_peek_off, two_chunks)
@@ -100,7 +118,13 @@ TEST_F(so_peek_off, two_chunks)
        sendeq(self->fd[0], "bbbb", 0);
 
        recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
+       peekoffeq(self->fd[1], 4);
        recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
+       peekoffeq(self->fd[1], 8);
+
+       recveq(self->fd[1], "aaaa", 4, 0);
+       recveq(self->fd[1], "bbbb", 4, 0);
+       peekoffeq(self->fd[1], 0);
 }
 
 TEST_F(so_peek_off, two_chunks_blocking)
@@ -111,6 +135,7 @@ TEST_F(so_peek_off, two_chunks_blocking)
        }
 
        recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
+       peekoffeq(self->fd[1], 4);
 
        async {
                usleep(1000);
@@ -119,24 +144,38 @@ TEST_F(so_peek_off, two_chunks_blocking)
 
        /* goto again; -> goto redo; in unix_stream_read_generic(). */
        recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
+       peekoffeq(self->fd[1], 8);
+
+       recveq(self->fd[1], "aaaa", 4, 0);
+       recveq(self->fd[1], "bbbb", 4, 0);
+       peekoffeq(self->fd[1], 0);
 }
 
 TEST_F(so_peek_off, two_chunks_overlap)
 {
        sendeq(self->fd[0], "aaaa", 0);
        recveq(self->fd[1], "aa", 2, MSG_PEEK);
+       peekoffeq(self->fd[1], 2);
 
        sendeq(self->fd[0], "bbbb", 0);
 
        if (variant->type == SOCK_STREAM) {
                /* SOCK_STREAM tries to fill the buffer. */
                recveq(self->fd[1], "aabb", 4, MSG_PEEK);
+               peekoffeq(self->fd[1], 6);
                recveq(self->fd[1], "bb", 100, MSG_PEEK);
+               peekoffeq(self->fd[1], 8);
        } else {
                /* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */
                recveq(self->fd[1], "aa", 100, MSG_PEEK);
+               peekoffeq(self->fd[1], 4);
                recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
+               peekoffeq(self->fd[1], 8);
        }
+
+       recveq(self->fd[1], "aaaa", 4, 0);
+       recveq(self->fd[1], "bbbb", 4, 0);
+       peekoffeq(self->fd[1], 0);
 }
 
 TEST_F(so_peek_off, two_chunks_overlap_blocking)
@@ -147,6 +186,7 @@ TEST_F(so_peek_off, two_chunks_overlap_blocking)
        }
 
        recveq(self->fd[1], "aa", 2, MSG_PEEK);
+       peekoffeq(self->fd[1], 2);
 
        async {
                usleep(1000);
@@ -155,8 +195,14 @@ TEST_F(so_peek_off, two_chunks_overlap_blocking)
 
        /* Even SOCK_STREAM does not wait if at least one byte is read. */
        recveq(self->fd[1], "aa", 100, MSG_PEEK);
+       peekoffeq(self->fd[1], 4);
 
        recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
+       peekoffeq(self->fd[1], 8);
+
+       recveq(self->fd[1], "aaaa", 4, 0);
+       recveq(self->fd[1], "bbbb", 4, 0);
+       peekoffeq(self->fd[1], 0);
 }
 
 TEST_HARNESS_MAIN