1 /*
2 * Support for decoding of DM_* ioctl commands.
3 *
4 * Copyright (c) 2016 Mikulas Patocka <mpatocka@redhat.com>
5 * Copyright (c) 2016 Masatake Yamato <yamato@redhat.com>
6 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
7 * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
8 * Copyright (c) 2016-2022 The strace developers.
9 * All rights reserved.
10 *
11 * SPDX-License-Identifier: LGPL-2.1-or-later
12 */
13
14 #include "defs.h"
15 #include <linux/dm-ioctl.h>
16 #include <linux/ioctl.h>
17
18 /* Defined in lvm2/libdm/ioctl/libdm-iface.c file. */
19 #define DM_EXISTS_FLAG 0x00000004
20
21 #include "xlat/dm_flags.h"
22
23 static void
24 dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc)
25 {
26 switch (code) {
27 case DM_REMOVE_ALL:
28 case DM_LIST_DEVICES:
29 case DM_LIST_VERSIONS:
30 break;
31 default:
32 if (ioc->dev) {
33 tprint_struct_next();
34 PRINT_FIELD_DEV(*ioc, dev);
35 }
36
37 if (ioc->name[0]) {
38 tprint_struct_next();
39 PRINT_FIELD_CSTRING(*ioc, name);
40 }
41
42 if (ioc->uuid[0]) {
43 tprint_struct_next();
44 PRINT_FIELD_CSTRING(*ioc, uuid);
45 }
46
47 break;
48 }
49 }
50
51 static void
52 dm_decode_values(struct tcb *tcp, const unsigned int code,
53 const struct dm_ioctl *ioc)
54 {
55 if (entering(tcp)) {
56 switch (code) {
57 case DM_TABLE_LOAD:
58 tprint_struct_next();
59 PRINT_FIELD_U(*ioc, target_count);
60 break;
61 case DM_DEV_SUSPEND:
62 if (ioc->flags & DM_SUSPEND_FLAG)
63 break;
64 ATTRIBUTE_FALLTHROUGH;
65 case DM_DEV_RENAME:
66 case DM_DEV_REMOVE:
67 case DM_DEV_WAIT:
68 tprint_struct_next();
69 PRINT_FIELD_U(*ioc, event_nr);
70 break;
71 }
72 } else if (!syserror(tcp)) {
73 switch (code) {
74 case DM_DEV_CREATE:
75 case DM_DEV_RENAME:
76 case DM_DEV_SUSPEND:
77 case DM_DEV_STATUS:
78 case DM_DEV_WAIT:
79 case DM_TABLE_LOAD:
80 case DM_TABLE_CLEAR:
81 case DM_TABLE_DEPS:
82 case DM_TABLE_STATUS:
83 case DM_TARGET_MSG:
84 tprint_struct_next();
85 PRINT_FIELD_U(*ioc, target_count);
86 tprint_struct_next();
87 PRINT_FIELD_U(*ioc, open_count);
88 tprint_struct_next();
89 PRINT_FIELD_U(*ioc, event_nr);
90 break;
91 }
92 }
93 }
94
95 static void
96 dm_decode_flags(const struct dm_ioctl *ioc)
97 {
98 tprint_struct_next();
99 PRINT_FIELD_FLAGS(*ioc, flags, dm_flags, "DM_???");
100 }
101
102 static bool
103 dm_ioctl_has_params(const unsigned int code)
104 {
105 switch (code) {
106 case DM_VERSION:
107 case DM_REMOVE_ALL:
108 case DM_DEV_CREATE:
109 case DM_DEV_REMOVE:
110 case DM_DEV_SUSPEND:
111 case DM_DEV_STATUS:
112 case DM_TABLE_CLEAR:
113 case DM_DEV_ARM_POLL:
114 return false;
115 }
116
117 return true;
118 }
119
120 static bool
121 dm_decode_header(struct tcb *const tcp, const unsigned int code,
122 const kernel_ulong_t arg, const struct dm_ioctl *const ioc)
123 {
124 bool rc = false;
125
126 tprint_struct_begin();
127 /*
128 * device mapper code uses %d in some places and %u in another, but
129 * fields themselves are declared as __u32.
130 */
131 PRINT_FIELD_U_ARRAY(*ioc, version);
132 /*
133 * if we use a different version of ABI, do not attempt to decode
134 * ioctl fields
135 */
136 if (ioc->version[0] != DM_VERSION_MAJOR) {
137 tprints_comment("unsupported device mapper ABI version");
138 goto skip;
139 }
140
141 tprint_struct_next();
142 PRINT_FIELD_U(*ioc, data_size);
143
144 if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
145 tprints_comment("data_size too small");
146 goto skip;
147 }
148
149 if (dm_ioctl_has_params(code)) {
150 tprint_struct_next();
151 PRINT_FIELD_U(*ioc, data_start);
152 }
153
154 dm_decode_device(code, ioc);
155 dm_decode_values(tcp, code, ioc);
156 dm_decode_flags(ioc);
157 rc = true;
158
159 skip:
160 tprint_struct_end();
161 return rc;
162 }
163
164 static void
165 dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
166 const struct dm_ioctl *const ioc)
167 {
168 static const uint32_t target_spec_size =
169 sizeof(struct dm_target_spec);
170 uint32_t offset = ioc->data_start;
171 uint32_t offset_end = 0;
172
173 if (abbrev(tcp)) {
174 if (ioc->target_count) {
175 tprint_array_next();
176 tprint_more_data_follows();
177 }
178
179 return;
180 }
181
182 for (uint32_t i = 0; i < ioc->target_count; ++i) {
183 tprint_array_next();
184
185 if (i && offset <= offset_end)
186 goto misplaced;
187
188 offset_end = offset + target_spec_size;
189
190 if (offset_end <= offset || offset_end > ioc->data_size)
191 goto misplaced;
192
193 if (i >= max_strlen) {
194 tprint_more_data_follows();
195 break;
196 }
197
198 struct dm_target_spec s;
199
200 if (umove_or_printaddr(tcp, addr + offset, &s))
201 break;
202
203 tprint_struct_begin();
204 PRINT_FIELD_U(s, sector_start);
205 tprint_struct_next();
206 PRINT_FIELD_U(s, length);
207
208 if (exiting(tcp)) {
209 tprint_struct_next();
210 PRINT_FIELD_D(s, status);
211 }
212
213 tprint_struct_next();
214 PRINT_FIELD_CSTRING(s, target_type);
215
216 tprint_struct_next();
217 tprints_field_name("string");
218 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
219 QUOTE_0_TERMINATED);
220 tprint_struct_end();
221
222 if (entering(tcp))
223 offset += s.next;
224 else
225 offset = ioc->data_start + s.next;
226 }
227
228 return;
229
230 misplaced:
231 tprint_unavailable();
232 tprints_comment("misplaced struct dm_target_spec");
233 }
234
235 static bool
236 dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy)
237 {
238 uint64_t *dev = (uint64_t *) dev_ptr;
239
240 print_dev_t(*dev);
241
242 return 1;
243 }
244
245 static void
246 dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
247 const struct dm_ioctl *const ioc)
248 {
249 if (ioc->data_start == ioc->data_size)
250 return;
251
252 tprint_array_next();
253
254 if (abbrev(tcp)) {
255 tprint_more_data_follows();
256 return;
257 }
258
259 static const uint32_t target_deps_dev_offs =
260 offsetof(struct dm_target_deps, dev);
261 uint64_t dev_buf;
262 struct dm_target_deps s;
263 uint32_t offset = ioc->data_start;
264 uint32_t offset_end = offset + target_deps_dev_offs;
265 uint32_t space;
266
267 if (offset_end <= offset || offset_end > ioc->data_size)
268 goto misplaced;
269
270 if (umove_or_printaddr(tcp, addr + offset, &s))
271 return;
272
273 space = (ioc->data_size - offset_end) / sizeof(dev_buf);
274
275 if (s.count > space)
276 goto misplaced;
277
278 tprint_struct_begin();
279 PRINT_FIELD_U(s, count);
280
281 tprint_struct_next();
282 tprints_field_name("deps");
283 print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
284 tfetch_mem, dm_print_dev, NULL);
285
286 tprint_struct_end();
287
288 return;
289
290 misplaced:
291 tprint_unavailable();
292 tprints_comment("misplaced struct dm_target_deps");
293 }
294
295 static void
296 dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
297 const struct dm_ioctl *const ioc)
298 {
299 static const uint32_t name_list_name_offs =
300 offsetof(struct dm_name_list, name);
301 struct dm_name_list s;
302 uint32_t offset = ioc->data_start;
303 uint32_t offset_end = 0;
304 int rc;
305
306 if (ioc->data_start == ioc->data_size)
307 return;
308
309 if (abbrev(tcp)) {
310 tprint_array_next();
311 tprint_more_data_follows();
312 return;
313 }
314
315 for (uint32_t count = 0;; ++count) {
316 tprint_array_next();
317
318 if (count && offset <= offset_end)
319 goto misplaced;
320
321 offset_end = offset + name_list_name_offs;
322
323 if (offset_end <= offset || offset_end > ioc->data_size)
324 goto misplaced;
325
326 if (count >= max_strlen) {
327 tprint_more_data_follows();
328 break;
329 }
330
331 if (umove_or_printaddr(tcp, addr + offset, &s))
332 break;
333
334 tprint_struct_begin();
335 PRINT_FIELD_DEV(s, dev);
336 tprint_struct_next();
337 tprints_field_name("name");
338 rc = printstr_ex(tcp, addr + offset_end,
339 ioc->data_size - offset_end,
340 QUOTE_0_TERMINATED);
341
342 /*
343 * In Linux v4.13-rc1~137^2~13 it has been decided to cram in
344 * one more undocumented field after the device name, as if the
345 * format decoding was not twisted enough already. So, we have
346 * to check "next" now, and if it _looks like_ that there is
347 * a space for one additional integer, let's print it. As if the
348 * perversity with "name string going further than pointer to
349 * the next one" wasn't enough. Moreover, the calculation was
350 * broken for m32 on 64-bit kernels until v4.14-rc4~20^2~3, and
351 * we have no ability to detect kernel bit-ness (on x86, at
352 * least), so refrain from printing it for the DM versions below
353 * 4.37 (the original version was also aligned differently than
354 * now even on 64 bit).
355 */
356
357 if ((rc > 0) && ioc->version[1] >= 37) {
358 kernel_ulong_t event_addr =
359 (addr + offset_end + rc + 7) & ~7;
360 uint32_t event_nr;
361
362 if ((event_addr + sizeof(event_nr)) <=
363 (addr + offset + s.next) &&
364 !umove(tcp, event_addr, &event_nr)) {
365 tprint_struct_next();
366 tprints_field_name("event_nr");
367 PRINT_VAL_U(event_nr);
368 }
369 }
370
371 tprint_struct_end();
372
373 if (!s.next)
374 break;
375
376 offset += s.next;
377 }
378
379 return;
380
381 misplaced:
382 tprint_unavailable();
383 tprints_comment("misplaced struct dm_name_list");
384 }
385
386 static void
387 dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
388 const struct dm_ioctl *const ioc)
389 {
390 static const uint32_t target_vers_name_offs =
391 offsetof(struct dm_target_versions, name);
392 struct dm_target_versions s;
393 uint32_t offset = ioc->data_start;
394 uint32_t offset_end = 0;
395
396 if (ioc->data_start == ioc->data_size)
397 return;
398
399 if (abbrev(tcp)) {
400 tprint_array_next();
401 tprint_more_data_follows();
402 return;
403 }
404
405 for (uint32_t count = 0;; ++count) {
406 tprint_array_next();
407
408 if (count && offset <= offset_end)
409 goto misplaced;
410
411 offset_end = offset + target_vers_name_offs;
412
413 if (offset_end <= offset || offset_end > ioc->data_size)
414 goto misplaced;
415
416 if (count >= max_strlen) {
417 tprint_more_data_follows();
418 break;
419 }
420
421 if (umove_or_printaddr(tcp, addr + offset, &s))
422 break;
423
424 tprint_struct_begin();
425 tprints_field_name("name");
426 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
427 QUOTE_0_TERMINATED);
428 tprint_struct_next();
429 PRINT_FIELD_U_ARRAY(s, version);
430 tprint_struct_end();
431
432 if (!s.next)
433 break;
434
435 offset += s.next;
436 }
437
438 return;
439
440 misplaced:
441 tprint_unavailable();
442 tprints_comment("misplaced struct dm_target_versions");
443 }
444
445 static void
446 dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
447 const struct dm_ioctl *const ioc)
448 {
449 if (ioc->data_start == ioc->data_size)
450 return;
451
452 tprint_array_next();
453
454 if (abbrev(tcp)) {
455 tprint_more_data_follows();
456 return;
457 }
458
459 static const uint32_t target_msg_message_offs =
460 offsetof(struct dm_target_msg, message);
461 uint32_t offset = ioc->data_start;
462 uint32_t offset_end = offset + target_msg_message_offs;
463
464 if (offset_end > offset && offset_end <= ioc->data_size) {
465 struct dm_target_msg s;
466
467 if (umove_or_printaddr(tcp, addr + offset, &s))
468 return;
469
470 tprint_struct_begin();
471 PRINT_FIELD_U(s, sector);
472 tprint_struct_next();
473 tprints_field_name("message");
474 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
475 QUOTE_0_TERMINATED);
476 tprint_struct_end();
477 } else {
478 tprint_unavailable();
479 tprints_comment("misplaced struct dm_target_msg");
480 }
481 }
482
483 static void
484 dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
485 const struct dm_ioctl *const ioc)
486 {
487 tprint_array_next();
488
489 if (abbrev(tcp)) {
490 tprint_more_data_follows();
491 return;
492 }
493
494 uint32_t offset = ioc->data_start;
495
496 if (offset <= ioc->data_size) {
497 tprint_struct_begin();
498 tprints_field_name("string");
499 printstr_ex(tcp, addr + offset, ioc->data_size - offset,
500 QUOTE_0_TERMINATED);
501 tprint_struct_end();
502 } else {
503 tprint_unavailable();
504 tprints_comment("misplaced string");
505 }
506 }
507
508 static void
509 dm_decode_payload(struct tcb *const tcp, const unsigned int code,
510 const kernel_ulong_t arg, const struct dm_ioctl *const ioc)
511 {
512 switch (code) {
513 case DM_DEV_WAIT:
514 case DM_TABLE_STATUS:
515 if (exiting(tcp) && !syserror(tcp))
516 dm_decode_dm_target_spec(tcp, arg, ioc);
517 break;
518 case DM_TABLE_LOAD:
519 if (entering(tcp))
520 dm_decode_dm_target_spec(tcp, arg, ioc);
521 break;
522 case DM_TABLE_DEPS:
523 if (exiting(tcp) && !syserror(tcp))
524 dm_decode_dm_target_deps(tcp, arg, ioc);
525 break;
526 case DM_LIST_DEVICES:
527 if (exiting(tcp) && !syserror(tcp))
528 dm_decode_dm_name_list(tcp, arg, ioc);
529 break;
530 case DM_LIST_VERSIONS:
531 if (exiting(tcp) && !syserror(tcp))
532 dm_decode_dm_target_versions(tcp, arg, ioc);
533 break;
534 case DM_TARGET_MSG:
535 if (entering(tcp))
536 dm_decode_dm_target_msg(tcp, arg, ioc);
537 else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG)
538 dm_decode_string(tcp, arg, ioc);
539 break;
540 case DM_DEV_RENAME:
541 case DM_DEV_SET_GEOMETRY:
542 if (entering(tcp))
543 dm_decode_string(tcp, arg, ioc);
544 break;
545 }
546 }
547
548 static int
549 dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
550 const kernel_ulong_t arg)
551 {
552 struct dm_ioctl *ioc = NULL;
553 struct dm_ioctl *entering_ioc = NULL;
554 bool ioc_changed = false;
555
556 if (entering(tcp)) {
557 ioc = malloc(sizeof(*ioc));
558 if (!ioc)
559 return 0;
560 } else {
561 ioc = alloca(sizeof(*ioc));
562 }
563
564 if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
565 (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
566 if (entering(tcp))
567 free(ioc);
568 return 0;
569 }
570 if (entering(tcp))
571 set_tcb_priv_data(tcp, ioc, free);
572 else {
573 entering_ioc = get_tcb_priv_data(tcp);
574
575 /*
576 * retrieve_status, __dev_status called only in case of success,
577 * so it looks like there's no need to check open_count,
578 * event_nr, target_count, dev fields for change (they are
579 * printed only in case of absence of errors).
580 */
581 if (!entering_ioc ||
582 (ioc->version[0] != entering_ioc->version[0]) ||
583 (ioc->version[1] != entering_ioc->version[1]) ||
584 (ioc->version[2] != entering_ioc->version[2]) ||
585 (ioc->data_size != entering_ioc->data_size) ||
586 (ioc->data_start != entering_ioc->data_start) ||
587 (ioc->flags != entering_ioc->flags))
588 ioc_changed = true;
589 }
590
591 if (exiting(tcp) && syserror(tcp) && !ioc_changed)
592 return RVAL_IOCTL_DECODED;
593
594 if (entering(tcp))
595 tprint_arg_next();
596 else
597 tprint_value_changed();
598
599 tprint_array_begin();
600 if (dm_decode_header(tcp, code, arg, ioc))
601 dm_decode_payload(tcp, code, arg, ioc);
602 tprint_array_end();
603
604 return entering(tcp) ? 0 : RVAL_IOCTL_DECODED;
605 }
606
607 int
608 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
609 {
610 switch (code) {
611 case DM_VERSION:
612 case DM_REMOVE_ALL:
613 case DM_LIST_DEVICES:
614 case DM_DEV_CREATE:
615 case DM_DEV_REMOVE:
616 case DM_DEV_RENAME:
617 case DM_DEV_SUSPEND:
618 case DM_DEV_STATUS:
619 case DM_DEV_WAIT:
620 case DM_TABLE_LOAD:
621 case DM_TABLE_CLEAR:
622 case DM_TABLE_DEPS:
623 case DM_TABLE_STATUS:
624 case DM_LIST_VERSIONS:
625 case DM_TARGET_MSG:
626 case DM_DEV_SET_GEOMETRY:
627 case DM_DEV_ARM_POLL:
628 return dm_known_ioctl(tcp, code, arg);
629 default:
630 return RVAL_DECODED;
631 }
632 }