1 /*
2 * Check decoding and dumping of read and write syscalls.
3 *
4 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2016-2021 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include "scno.h"
18
19 static void
20 dump_str_ex(const char *str, const unsigned int len, const int idx_w)
21 {
22 static const char chars[256] =
23 "................................"
24 " !\"#$%&'()*+,-./0123456789:;<=>?"
25 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
26 "`abcdefghijklmnopqrstuvwxyz{|}~."
27 "................................"
28 "................................"
29 "................................"
30 "................................";
31
32 for (unsigned int i = 0; i < len; i += 16) {
33 unsigned int n = len - i > 16 ? 16 : len - i;
34 const char *dump = hexdump_memdup(str + i, n);
35
36 tprintf(" | %0*x %-49s %-16.*s |\n",
37 idx_w, i, dump, n, chars + i % 0x100);
38
39 free((void *) dump);
40 }
41 }
42
43 static void
44 dump_str(const char *str, const unsigned int len)
45 {
46 dump_str_ex(str, len, 5);
47 }
48
49 static void
50 print_hex(const char *str, const unsigned int len)
51 {
52 const unsigned char *ustr = (const unsigned char *) str;
53
54 tprintf("\"");
55
56 for (unsigned int i = 0; i < len; ++i) {
57 unsigned int c = ustr[i];
58
59 if (i >= DEFAULT_STRLEN) {
60 tprintf("\"...");
61 return;
62 }
63
64 switch (c) {
65 case '\t':
66 tprintf("\\t"); break;
67 case '\n':
68 tprintf("\\n"); break;
69 case '\v':
70 tprintf("\\v"); break;
71 case '\f':
72 tprintf("\\f"); break;
73 case '\r':
74 tprintf("\\r"); break;
75 default:
76 tprintf("\\%o", ustr[i]);
77 }
78 }
79
80 tprintf("\"");
81 }
82
83 static long
84 k_read(unsigned int fd, void *buf, size_t count)
85 {
86 kernel_ulong_t kfd = (kernel_ulong_t) 0xfacefeed00000000ULL | fd;
87 return syscall(__NR_read, kfd, buf, count);
88 }
89
90 static long
91 k_write(unsigned int fd, const void *buf, size_t count)
92 {
93 kernel_ulong_t kfd = (kernel_ulong_t) 0xfacefeed00000000ULL | fd;
94 return syscall(__NR_write, kfd, buf, count);
95 }
96
97 static void
98 test_dump(const unsigned int len, bool err_desc)
99 {
100 static char *buf;
101 const char *rc_str;
102 int in_fd = err_desc ? 5 : 0;
103 int out_fd = err_desc ? 4 : 1;
104
105 if (buf) {
106 size_t ps1 = get_page_size() - 1;
107 buf = (void *) (((size_t) buf + ps1) & ~ps1) - len;
108 } else {
109 buf = tail_alloc(len);
110 }
111
112 long rc = k_read(in_fd, buf, len);
113 rc_str = sprintrc(rc);
114 if (err_desc ^ (rc != (int) len))
115 perror_msg_and_fail("read: expected %d, returned %ld",
116 err_desc ? -1 : (int) len, rc);
117
118 tprintf("%s(%d, ", "read", in_fd);
119 if (!err_desc)
120 print_hex(buf, len);
121 else
122 tprintf("%p", buf);
123 tprintf(", %d) = %s\n", len, rc_str);
124 if (!err_desc)
125 dump_str(buf, len);
126
127 for (unsigned int i = 0; i < len; ++i)
128 buf[i] = i;
129
130 rc = k_write(out_fd, buf, len);
131 rc_str = sprintrc(rc);
132 if (err_desc ^ (rc != (int) len))
133 perror_msg_and_fail("write: expected %d, returned %ld",
134 err_desc ? -1 : (int) len, rc);
135
136 tprintf("%s(%d, ", "write", out_fd);
137 print_hex(buf, len);
138 tprintf(", %d) = %s\n", len, rc_str);
139 dump_str(buf, len);
140
141 if (!len)
142 buf = 0;
143 }
144
145 int
146 main(void)
147 {
148 tprintf("%s", "");
149
150 skip_if_unavailable("/proc/self/fd/");
151
152 static const char tmp[] = "read-write-tmpfile";
153 bool need_cleanup = true;
154 long rc;
155
156 rc = open(tmp, O_RDONLY, 0600);
157 if (rc < 0) {
158 rc = open(tmp, O_CREAT|O_EXCL|O_RDONLY, 0600);
159 need_cleanup = false;
160 }
161 if (rc != 0)
162 perror_msg_and_fail("creat: %s", tmp);
163 if (open(tmp, O_TRUNC|O_WRONLY) != 1)
164 perror_msg_and_fail("open: %s", tmp);
165
166 static const char w_c[] = "0123456789abcde";
167 const unsigned int w_len = LENGTH_OF(w_c);
168 const char *w_d = hexdump_strdup(w_c);
169 const void *w = tail_memdup(w_c, w_len);
170
171 static const char r0_c[] = "01234567";
172 const char *r0_d = hexdump_strdup(r0_c);
173 const unsigned int r0_len = (w_len + 1) / 2;
174 void *r0 = tail_alloc(r0_len);
175
176 static const char r1_c[] = "89abcde";
177 const char *r1_d = hexdump_strdup(r1_c);
178 const unsigned int r1_len = w_len - r0_len;
179 void *r1 = tail_alloc(w_len);
180
181 void *efault = r1 - get_page_size();
182
183 rc = k_write(1, w, 0);
184 if (rc)
185 perror_msg_and_fail("write: expected 0, returned %ld", rc);
186 tprintf("write(1, \"\", 0) = 0\n");
187
188 rc = k_write(1, efault, 1);
189 if (rc != -1)
190 perror_msg_and_fail("write: expected -1 EFAULT"
191 ", returned %ld", rc);
192 tprintf("write(1, %p, 1) = -1 EFAULT (%m)\n", efault);
193
194 rc = k_write(1, w, w_len);
195 if (rc != (int) w_len)
196 perror_msg_and_fail("write: expected %u, returned %ld",
197 w_len, rc);
198 tprintf("write(1, \"%s\", %u) = %ld\n"
199 " | 00000 %-49s %-16s |\n",
200 w_c, w_len, rc, w_d, w_c);
201
202 rc = k_read(0, r0, 0);
203 if (rc)
204 perror_msg_and_fail("read: expected 0, returned %ld", rc);
205 tprintf("read(0, \"\", 0) = 0\n");
206
207 rc = k_read(0, efault, 1);
208 if (rc != -1)
209 perror_msg_and_fail("read: expected -1, returned %ld", rc);
210 tprintf("read(0, %p, 1) = -1 EFAULT (%m)\n", efault);
211
212 rc = k_read(0, r0, r0_len);
213 if (rc != (int) r0_len)
214 perror_msg_and_fail("read: expected %u, returned %ld",
215 r0_len, rc);
216 tprintf("read(0, \"%s\", %u) = %ld\n"
217 " | 00000 %-49s %-16s |\n",
218 r0_c, r0_len, rc, r0_d, r0_c);
219
220 rc = k_read(0, r1, w_len);
221 if (rc != (int) r1_len)
222 perror_msg_and_fail("read: expected %u, returned %ld",
223 r1_len, rc);
224 tprintf("read(0, \"%s\", %u) = %ld\n"
225 " | 00000 %-49s %-16s |\n",
226 r1_c, w_len, rc, r1_d, r1_c);
227 close(0);
228
229 /*
230 * Check partial dump; relies on dumpstr() implementation details
231 * (maximum size of chunk to be copied at once).
232 */
233 static const size_t six_wide_size = 1 << 20;
234 static const size_t fetch_size = 1 << 16;
235 static const char big_buf_str[] =
236 "\\0\\1\\2\\3\\4\\5\\6\\7"
237 "\\10\\t\\n\\v\\f\\r\\16\\17"
238 "\\20\\21\\22\\23\\24\\25\\26\\27"
239 "\\30\\31\\32\\33\\34\\35\\36\\37";
240 const size_t buf_size = six_wide_size + fetch_size;
241 const size_t sizes[] = {
242 six_wide_size,
243 six_wide_size + 1,
244 buf_size,
245 buf_size + 1,
246 buf_size + 2,
247 };
248 char *big_buf = tail_alloc(buf_size);
249
250 fill_memory_ex(big_buf, buf_size, 0, 0x100);
251
252 for (size_t i = 0; i < ARRAY_SIZE(sizes); i++) {
253 rc = k_write(1, big_buf, sizes[i]);
254 tprintf("write(1, \"%s\"..., %zu) = %s\n",
255 big_buf_str, sizes[i], sprintrc(rc));
256 dump_str_ex(big_buf, MIN(sizes[i], buf_size),
257 sizes[i] > six_wide_size ? 6 : 5);
258
259 if (sizes[i] == buf_size + 1)
260 tprintf(" | <Cannot fetch 1 byte from pid %d @%p>\n",
261 getpid(), big_buf + buf_size);
262
263 if (sizes[i] == buf_size + 2)
264 tprintf(" | <Cannot fetch 2 bytes from pid %d @%p>\n",
265 getpid(), big_buf + buf_size);
266 }
267
268 close(1);
269
270 if (open("/dev/zero", O_RDONLY))
271 perror_msg_and_fail("open");
272
273 if (open("/dev/null", O_WRONLY) != 1)
274 perror_msg_and_fail("open");
275
276 (void) close(4);
277 if (open("/dev/zero", O_RDONLY) != 4)
278 perror_msg_and_fail("open");
279
280 (void) close(5);
281 if (open("/dev/null", O_WRONLY) != 5)
282 perror_msg_and_fail("open");
283
284 for (unsigned int i = 0; i <= DEFAULT_STRLEN; ++i)
285 test_dump(i, false);
286
287 test_dump(256, true);
288
289 if (need_cleanup && unlink(tmp))
290 perror_msg_and_fail("unlink: %s", tmp);
291
292 tprintf("+++ exited with 0 +++\n");
293 return 0;
294 }