xref: /linux/tools/testing/selftests/net/af_unix/so_peek_off.c (revision 127fa2ae9e2b1f9b9d876dfaa39fe3640cec5764)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2025 Google LLC */
3 
4 #include <stdlib.h>
5 #include <unistd.h>
6 
7 #include <sys/socket.h>
8 
9 #include "../../kselftest_harness.h"
10 
11 FIXTURE(so_peek_off)
12 {
13 	int fd[2];	/* 0: sender, 1: receiver */
14 };
15 
16 FIXTURE_VARIANT(so_peek_off)
17 {
18 	int type;
19 };
20 
21 FIXTURE_VARIANT_ADD(so_peek_off, stream)
22 {
23 	.type = SOCK_STREAM,
24 };
25 
26 FIXTURE_VARIANT_ADD(so_peek_off, dgram)
27 {
28 	.type = SOCK_DGRAM,
29 };
30 
31 FIXTURE_VARIANT_ADD(so_peek_off, seqpacket)
32 {
33 	.type = SOCK_SEQPACKET,
34 };
35 
36 FIXTURE_SETUP(so_peek_off)
37 {
38 	struct timeval timeout = {
39 		.tv_sec = 0,
40 		.tv_usec = 3000,
41 	};
42 	int ret;
43 
44 	ret = socketpair(AF_UNIX, variant->type, 0, self->fd);
45 	ASSERT_EQ(0, ret);
46 
47 	ret = setsockopt(self->fd[1], SOL_SOCKET, SO_RCVTIMEO_NEW,
48 			 &timeout, sizeof(timeout));
49 	ASSERT_EQ(0, ret);
50 
51 	ret = setsockopt(self->fd[1], SOL_SOCKET, SO_PEEK_OFF,
52 			 &(int){0}, sizeof(int));
53 	ASSERT_EQ(0, ret);
54 }
55 
56 FIXTURE_TEARDOWN(so_peek_off)
57 {
58 	close_range(self->fd[0], self->fd[1], 0);
59 }
60 
61 #define sendeq(fd, str, flags)					\
62 	do {							\
63 		int bytes, len = strlen(str);			\
64 								\
65 		bytes = send(fd, str, len, flags);		\
66 		ASSERT_EQ(len, bytes);				\
67 	} while (0)
68 
69 #define recveq(fd, str, buflen, flags)				\
70 	do {							\
71 		char buf[(buflen) + 1] = {};			\
72 		int bytes;					\
73 								\
74 		bytes = recv(fd, buf, buflen, flags);		\
75 		ASSERT_NE(-1, bytes);				\
76 		ASSERT_STREQ(str, buf);				\
77 	} while (0)
78 
79 #define async							\
80 	for (pid_t pid = (pid = fork(),				\
81 			  pid < 0 ?				\
82 			  __TH_LOG("Failed to start async {}"),	\
83 			  _metadata->exit_code = KSFT_FAIL,	\
84 			  __bail(1, _metadata),			\
85 			  0xdead :				\
86 			  pid);					\
87 	     !pid; exit(0))
88 
89 TEST_F(so_peek_off, single_chunk)
90 {
91 	sendeq(self->fd[0], "aaaabbbb", 0);
92 
93 	recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
94 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
95 }
96 
97 TEST_F(so_peek_off, two_chunks)
98 {
99 	sendeq(self->fd[0], "aaaa", 0);
100 	sendeq(self->fd[0], "bbbb", 0);
101 
102 	recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
103 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
104 }
105 
106 TEST_F(so_peek_off, two_chunks_blocking)
107 {
108 	async {
109 		usleep(1000);
110 		sendeq(self->fd[0], "aaaa", 0);
111 	}
112 
113 	recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
114 
115 	async {
116 		usleep(1000);
117 		sendeq(self->fd[0], "bbbb", 0);
118 	}
119 
120 	/* goto again; -> goto redo; in unix_stream_read_generic(). */
121 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
122 }
123 
124 TEST_F(so_peek_off, two_chunks_overlap)
125 {
126 	sendeq(self->fd[0], "aaaa", 0);
127 	recveq(self->fd[1], "aa", 2, MSG_PEEK);
128 
129 	sendeq(self->fd[0], "bbbb", 0);
130 
131 	if (variant->type == SOCK_STREAM) {
132 		/* SOCK_STREAM tries to fill the buffer. */
133 		recveq(self->fd[1], "aabb", 4, MSG_PEEK);
134 		recveq(self->fd[1], "bb", 100, MSG_PEEK);
135 	} else {
136 		/* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */
137 		recveq(self->fd[1], "aa", 100, MSG_PEEK);
138 		recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
139 	}
140 }
141 
142 TEST_F(so_peek_off, two_chunks_overlap_blocking)
143 {
144 	async {
145 		usleep(1000);
146 		sendeq(self->fd[0], "aaaa", 0);
147 	}
148 
149 	recveq(self->fd[1], "aa", 2, MSG_PEEK);
150 
151 	async {
152 		usleep(1000);
153 		sendeq(self->fd[0], "bbbb", 0);
154 	}
155 
156 	/* Even SOCK_STREAM does not wait if at least one byte is read. */
157 	recveq(self->fd[1], "aa", 100, MSG_PEEK);
158 
159 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
160 }
161 
162 TEST_HARNESS_MAIN
163