1 /*
2 * Check decoding of timestamp control messages.
3 *
4 * Copyright (c) 2019 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2019-2023 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12 #include "scno.h"
13
14 #ifdef __NR_recvmsg
15
16 # include <errno.h>
17 # include <stdio.h>
18 # include <string.h>
19 # include <unistd.h>
20 # include <sys/socket.h>
21
22 # include "kernel_time_types.h"
23 # include "kernel_timeval.h"
24 # include "kernel_old_timespec.h"
25
26 # define XLAT_MACROS_ONLY
27 # include "xlat/sock_options.h"
28 # undef XLAT_MACROS_ONLY
29
30 static const char *errstr;
31
32 static long
33 k_recvmsg(const unsigned int fd, const void *const ptr, const unsigned int flags)
34 {
35 const kernel_ulong_t fill = (kernel_ulong_t) 0xdefaced00000000ULL;
36 const kernel_ulong_t bad = (kernel_ulong_t) 0xbadc0dedbadc0dedULL;
37 const kernel_ulong_t arg1 = fill | fd;
38 const kernel_ulong_t arg2 = (uintptr_t) ptr;
39 const kernel_ulong_t arg3 = fill | flags;
40 const long rc = syscall(__NR_recvmsg, arg1, arg2, arg3, bad, bad, bad);
41 if (rc && errno == ENOSYS)
42 perror_msg_and_skip("recvmsg");
43 errstr = sprintrc(rc);
44 return rc;
45 }
46
47 # define SC_setsockopt 14
48 static long
49 k_setsockopt(const unsigned int fd, const unsigned int level,
50 const unsigned int optname, const void *const optval,
51 const unsigned int len)
52 {
53 const kernel_ulong_t fill = (kernel_ulong_t) 0xdefaced00000000ULL;
54 # ifdef __NR_setsockopt
55 const kernel_ulong_t bad = (kernel_ulong_t) 0xbadc0dedbadc0dedULL;
56 # endif
57
58 return syscall(
59 # ifdef __NR_setsockopt
60 __NR_setsockopt,
61 # else /* socketcall */
62 __NR_socketcall, SC_setsockopt,
63 # endif
64 fill | fd , fill | level, fill | optname, optval, fill | len
65 # ifdef __NR_setsockopt
66 , bad
67 # endif
68 );
69 }
70
71 static void
72 print_timestamp_old(const struct cmsghdr *c)
73 {
74 const void *cmsg_header = c;
75 const void *cmsg_data = CMSG_DATA(c);
76 kernel_old_timeval_t tv;
77 const unsigned int expected_len = sizeof(tv);
78 const unsigned int data_len = c->cmsg_len - (cmsg_data - cmsg_header);
79
80 if (expected_len != data_len)
81 perror_msg_and_fail("sizeof(struct timeval) = %u"
82 ", data_len = %u\n",
83 expected_len, data_len);
84
85 memcpy(&tv, cmsg_data, sizeof(tv));
86 printf("{tv_sec=%lld, tv_usec=%lld}",
87 (long long) tv.tv_sec, (long long) tv.tv_usec);
88 }
89
90 static void
91 print_timestampns_old(const struct cmsghdr *c)
92 {
93 const void *cmsg_header = c;
94 const void *cmsg_data = CMSG_DATA(c);
95 kernel_old_timespec_t ts;
96 const unsigned int expected_len = sizeof(ts);
97 const unsigned int data_len = c->cmsg_len - (cmsg_data - cmsg_header);
98
99 if (expected_len != data_len)
100 perror_msg_and_fail("sizeof(struct timespec) = %u"
101 ", data_len = %u\n",
102 expected_len, data_len);
103
104 memcpy(&ts, cmsg_data, sizeof(ts));
105 printf("{tv_sec=%lld, tv_nsec=%lld}",
106 (long long) ts.tv_sec, (long long) ts.tv_nsec);
107 }
108
109 static void
110 print_timestamp_new(const struct cmsghdr *c)
111 {
112 const void *cmsg_header = c;
113 const void *cmsg_data = CMSG_DATA(c);
114 struct __kernel_sock_timeval tv;
115 const unsigned int expected_len = sizeof(tv);
116 const unsigned int data_len = c->cmsg_len - (cmsg_data - cmsg_header);
117
118 if (expected_len != data_len)
119 perror_msg_and_fail("sizeof(struct __kernel_sock_timeval) = %u"
120 ", data_len = %u\n",
121 expected_len, data_len);
122
123 memcpy(&tv, cmsg_data, sizeof(tv));
124 printf("{tv_sec=%lld, tv_usec=%lld}",
125 (long long) tv.tv_sec, (long long) tv.tv_usec);
126 }
127
128 static void
129 print_timestampns_new(const struct cmsghdr *c)
130 {
131 const void *cmsg_header = c;
132 const void *cmsg_data = CMSG_DATA(c);
133 struct __kernel_timespec ts;
134 const unsigned int expected_len = sizeof(ts);
135 const unsigned int data_len = c->cmsg_len - (cmsg_data - cmsg_header);
136
137 if (expected_len != data_len)
138 perror_msg_and_fail("sizeof(struct __kernel_timespec) = %u"
139 ", data_len = %u\n",
140 expected_len, data_len);
141
142 memcpy(&ts, cmsg_data, sizeof(ts));
143 printf("{tv_sec=%lld, tv_nsec=%lld}",
144 (long long) ts.tv_sec, (long long) ts.tv_nsec);
145 }
146
147 static unsigned int
148 test_sockopt(int so_val, const char *str, void (*fun)(const struct cmsghdr *))
149 {
150 static const char data[] = "socketpair";
151 const size_t size = sizeof(data) - 1;
152
153 int sv[2];
154 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv))
155 perror_msg_and_skip(data);
156
157 const int opt_1 = 1;
158 /*
159 * glibc-2.34~294 adds a fallback for SO_TIMESTAMP{,NS}_NEW that calls
160 * SO_TIMESTAMP{,NS}_OLD, so we have to call the setsockopt directly
161 * in order to avoid unexpected recvmsg msg types.
162 */
163 if (k_setsockopt(sv[0], SOL_SOCKET, so_val, &opt_1, sizeof(opt_1))) {
164 perror(str);
165 return 0;
166 }
167
168 if (send(sv[1], data, size, 0) != (int) size)
169 perror_msg_and_fail("send");
170 if (close(sv[1]))
171 perror_msg_and_fail("close send");
172
173 char buf[size];
174 struct iovec iov = {
175 .iov_base = buf,
176 .iov_len = sizeof(buf)
177 };
178 struct cmsghdr control[16];
179 struct msghdr mh = {
180 .msg_iov = &iov,
181 .msg_iovlen = 1,
182 .msg_control = control,
183 .msg_controllen = sizeof(control)
184 };
185
186 if (k_recvmsg(sv[0], &mh, 0) != (int) size)
187 perror_msg_and_fail("recvmsg");
188 if (close(sv[0]))
189 perror_msg_and_fail("close recv");
190
191 printf("recvmsg(%d, {msg_name=NULL, msg_namelen=0"
192 ", msg_iov=[{iov_base=\"%s\", iov_len=%u}], msg_iovlen=1",
193 sv[0], data, (unsigned int) size);
194
195 unsigned int tested = 0;
196 if (mh.msg_controllen) {
197 printf(", msg_control=[");
198 for (struct cmsghdr *c = CMSG_FIRSTHDR(&mh); c;
199 c = CMSG_NXTHDR(&mh, c)) {
200 printf("%s{cmsg_len=%lu, cmsg_level=",
201 (c == control ? "" : ", "),
202 (unsigned long) c->cmsg_len);
203 if (c->cmsg_level == SOL_SOCKET) {
204 printf("SOL_SOCKET");
205 } else {
206 printf("%d /* expected SOL_SOCKET == %d */",
207 c->cmsg_level, (int) SOL_SOCKET);
208 }
209 printf(", cmsg_type=");
210 if (c->cmsg_type == so_val) {
211 printf("%s, cmsg_data=", str);
212 fun(c);
213 tested = 1;
214 } else {
215 printf("%d /* expected %d */",
216 c->cmsg_type, so_val);
217 }
218 printf("}");
219 }
220 printf("]");
221 }
222 printf(", msg_controllen=%lu, msg_flags=0}, 0) = %u\n",
223 (unsigned long) mh.msg_controllen, (unsigned int) size);
224
225 return tested;
226 }
227
228 int
229 main(void)
230 {
231 static const struct {
232 int val;
233 const char *str;
234 void (*fun)(const struct cmsghdr *);
235 } tests[] = {
236 { SO_TIMESTAMP_OLD, "SO_TIMESTAMP_OLD", print_timestamp_old },
237 { SO_TIMESTAMPNS_OLD, "SO_TIMESTAMPNS_OLD", print_timestampns_old },
238 { SO_TIMESTAMP_NEW, "SO_TIMESTAMP_NEW", print_timestamp_new },
239 { SO_TIMESTAMPNS_NEW, "SO_TIMESTAMPNS_NEW", print_timestampns_new },
240 };
241 unsigned int tested = 0;
242 for (unsigned int i = 0; i < ARRAY_SIZE(tests); ++i)
243 tested |= test_sockopt(tests[i].val,
244 tests[i].str,
245 tests[i].fun);
246 if (!tested)
247 return 77;
248
249 puts("+++ exited with 0 +++");
250 return 0;
251 }
252
253 #else
254
255 SKIP_MAIN_UNDEFINED("__NR_recvmsg")
256
257 #endif