xref: /linux/tools/testing/selftests/net/rds/getsockopt.c (revision b74360369e13a68337a411efba1fd2bdbb6f1b80)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Exercise the RDS getsockopt() paths that were converted to the
4  * getsockopt_iter() / sockopt_t callback.
5  *
6  * Three distinct paths are covered:
7  *
8  *   - RDS_RECVERR and SO_RDS_TRANSPORT, which now return their int value
9  *     through copy_to_iter() and report the written length in opt->optlen.
10  *
11  *   - RDS_INFO_*, which pins the userspace buffer with
12  *     iov_iter_extract_pages() (including a non-zero starting page offset)
13  *     and lets the info producers memcpy the snapshot in under a spinlock.
14  *
15  * The kvec (in-kernel buffer) -> -EOPNOTSUPP path of rds_info_getsockopt()
16  * is not reachable from a userspace getsockopt() and so is not tested here.
17  */
18 #include <errno.h>
19 #include <stdint.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/mman.h>
23 #include <sys/socket.h>
24 #include <linux/rds.h>
25 
26 #include "../../kselftest_harness.h"
27 
28 #ifndef AF_RDS
29 #define AF_RDS 21
30 #endif
31 
32 FIXTURE(rds) {
33 	int fd;
34 };
35 
36 FIXTURE_SETUP(rds)
37 {
38 	self->fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
39 	if (self->fd < 0)
40 		SKIP(return, "AF_RDS unavailable (errno %d) - load the rds module",
41 		     errno);
42 }
43 
44 FIXTURE_TEARDOWN(rds)
45 {
46 	if (self->fd >= 0)
47 		close(self->fd);
48 }
49 
50 /* RDS_RECVERR defaults to 0 and is reported back as a 4-byte int. */
51 TEST_F(rds, recverr_default)
52 {
53 	socklen_t len = sizeof(int);
54 	int val = 0xdeadbeef;
55 
56 	ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, &len));
57 	EXPECT_EQ(sizeof(int), len);
58 	EXPECT_EQ(0, val);
59 }
60 
61 /* A value set via setsockopt() must be readable back unchanged. */
62 TEST_F(rds, recverr_set_get)
63 {
64 	socklen_t len = sizeof(int);
65 	int val = 1;
66 
67 	ASSERT_EQ(0, setsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, len));
68 
69 	val = 0;
70 	ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, &len));
71 	EXPECT_EQ(sizeof(int), len);
72 	EXPECT_EQ(1, val);
73 }
74 
75 /* A buffer smaller than an int is rejected with EINVAL, not silently. */
76 TEST_F(rds, recverr_short_buffer)
77 {
78 	socklen_t len = sizeof(int) - 1;
79 	char buf[sizeof(int)];
80 
81 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, buf, &len));
82 	EXPECT_EQ(EINVAL, errno);
83 }
84 
85 /* An unbound socket reports RDS_TRANS_NONE for SO_RDS_TRANSPORT. */
86 TEST_F(rds, transport_unbound)
87 {
88 	socklen_t len = sizeof(int);
89 	int val = 0;
90 
91 	ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, SO_RDS_TRANSPORT, &val,
92 				&len));
93 	EXPECT_EQ(sizeof(int), len);
94 	EXPECT_EQ(RDS_TRANS_NONE, (unsigned int)val);
95 }
96 
97 TEST_F(rds, transport_short_buffer)
98 {
99 	socklen_t len = sizeof(int) - 1;
100 	char buf[sizeof(int)];
101 
102 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, SO_RDS_TRANSPORT, buf,
103 				 &len));
104 	EXPECT_EQ(EINVAL, errno);
105 }
106 
107 /*
108  * RDS_INFO_COUNTERS with a zero-length buffer is the "probe" call: it must
109  * fail with ENOSPC and report the required snapshot size in optlen.
110  */
111 TEST_F(rds, info_counters_probe)
112 {
113 	socklen_t len = 0;
114 
115 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL,
116 				 &len));
117 	EXPECT_EQ(ENOSPC, errno);
118 	EXPECT_GT(len, 0);
119 	/* The snapshot is an array of fixed-size counter records. */
120 	EXPECT_EQ(0, len % (socklen_t)sizeof(struct rds_info_counter));
121 }
122 
123 /*
124  * A real snapshot into an unaligned userspace buffer exercises the
125  * iov_iter_extract_pages() path, including the non-zero offset0 handling
126  * that the patch reworked. Place the buffer at a non-page-aligned address
127  * spanning into the next page to make sure multi-page pinning works too.
128  */
129 TEST_F(rds, info_counters_snapshot)
130 {
131 	struct rds_info_counter *ctr;
132 	socklen_t need = 0, len;
133 	long pagesz = sysconf(_SC_PAGESIZE);
134 	size_t offset, map_len;
135 	unsigned int i, n;
136 	char *region, *buf;
137 	int ret;
138 
139 	/* Probe for the required size. */
140 	getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, &need);
141 	ASSERT_GT(need, 0);
142 
143 	/*
144 	 * Place the buffer at a non-page-aligned offset that runs past the
145 	 * first page boundary, and size the mapping from the probed length so
146 	 * the test keeps working if the counter set grows.
147 	 */
148 	offset = pagesz - 64;
149 	map_len = ((offset + need + pagesz - 1) / pagesz) * pagesz;
150 
151 	region = mmap(NULL, map_len, PROT_READ | PROT_WRITE,
152 		      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
153 	ASSERT_NE(MAP_FAILED, region);
154 
155 	buf = region + offset;
156 
157 	/*
158 	 * On success the RDS_INFO path returns the positive per-element size
159 	 * (lens.each) rather than 0, and writes the full snapshot length back
160 	 * into optlen.
161 	 */
162 	len = need;
163 	ret = getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, buf, &len);
164 	ASSERT_GE(ret, 0) {
165 		TH_LOG("getsockopt snapshot failed: errno %d", errno);
166 	}
167 	EXPECT_EQ(sizeof(struct rds_info_counter), ret);
168 	EXPECT_EQ(need, len);
169 
170 	/* The counter names must be NUL-terminated, non-empty strings. */
171 	ctr = (struct rds_info_counter *)buf;
172 	n = len / sizeof(*ctr);
173 	ASSERT_GT(n, 0);
174 	for (i = 0; i < n; i++) {
175 		size_t namelen = strnlen((char *)ctr[i].name,
176 					 sizeof(ctr[i].name));
177 
178 		EXPECT_GT(namelen, 0);
179 		EXPECT_LT(namelen, sizeof(ctr[i].name));
180 	}
181 
182 	munmap(region, map_len);
183 }
184 
185 /*
186  * A non-zero but too-small buffer must report ENOSPC and the full required
187  * length, without corrupting memory past the buffer.
188  */
189 TEST_F(rds, info_counters_short_buffer)
190 {
191 	socklen_t need = 0, len;
192 	char small[sizeof(struct rds_info_counter)];
193 
194 	getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, &need);
195 	ASSERT_GT(need, 0);
196 
197 	/* Ask with a buffer guaranteed smaller than the full snapshot. */
198 	if (need <= (socklen_t)sizeof(small))
199 		SKIP(return, "snapshot fits in one record; nothing to test");
200 
201 	len = 1; /* < sizeof(struct rds_info_counter) */
202 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, small,
203 				 &len));
204 	EXPECT_EQ(ENOSPC, errno);
205 	EXPECT_EQ(need, len);
206 }
207 
208 TEST_HARNESS_MAIN
209