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