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@altlinux.org>
7 * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
8 * Copyright (c) 2016-2017 The strace developers.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "defs.h"
35
36 #ifdef HAVE_LINUX_DM_IOCTL_H
37
38 # include <linux/dm-ioctl.h>
39 # include <linux/ioctl.h>
40
41 # if DM_VERSION_MAJOR == 4
42
43 /* Definitions for command which have been added later */
44
45 # ifndef DM_LIST_VERSIONS
46 # define DM_LIST_VERSIONS _IOWR(DM_IOCTL, 0xd, struct dm_ioctl)
47 # endif
48 # ifndef DM_TARGET_MSG
49 # define DM_TARGET_MSG _IOWR(DM_IOCTL, 0xe, struct dm_ioctl)
50 # endif
51 # ifndef DM_DEV_SET_GEOMETRY
52 # define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0xf, struct dm_ioctl)
53 # endif
54
55
56 static void
dm_decode_device(const unsigned int code,const struct dm_ioctl * ioc)57 dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc)
58 {
59 switch (code) {
60 case DM_REMOVE_ALL:
61 case DM_LIST_DEVICES:
62 case DM_LIST_VERSIONS:
63 break;
64 default:
65 if (ioc->dev) {
66 tprints(", dev=");
67 print_dev_t(ioc->dev);
68 }
69 if (ioc->name[0]) {
70 tprints(", name=");
71 print_quoted_string(ioc->name, DM_NAME_LEN,
72 QUOTE_0_TERMINATED);
73 }
74 if (ioc->uuid[0]) {
75 tprints(", uuid=");
76 print_quoted_string(ioc->uuid, DM_UUID_LEN,
77 QUOTE_0_TERMINATED);
78 }
79 break;
80 }
81 }
82
83 static void
dm_decode_values(struct tcb * tcp,const unsigned int code,const struct dm_ioctl * ioc)84 dm_decode_values(struct tcb *tcp, const unsigned int code,
85 const struct dm_ioctl *ioc)
86 {
87 if (entering(tcp)) {
88 switch (code) {
89 case DM_TABLE_LOAD:
90 tprintf(", target_count=%" PRIu32,
91 ioc->target_count);
92 break;
93 case DM_DEV_SUSPEND:
94 if (ioc->flags & DM_SUSPEND_FLAG)
95 break;
96 /* Fall through */
97 case DM_DEV_RENAME:
98 case DM_DEV_REMOVE:
99 case DM_DEV_WAIT:
100 tprintf(", event_nr=%" PRIu32,
101 ioc->event_nr);
102 break;
103 }
104 } else if (!syserror(tcp)) {
105 switch (code) {
106 case DM_DEV_CREATE:
107 case DM_DEV_RENAME:
108 case DM_DEV_SUSPEND:
109 case DM_DEV_STATUS:
110 case DM_DEV_WAIT:
111 case DM_TABLE_LOAD:
112 case DM_TABLE_CLEAR:
113 case DM_TABLE_DEPS:
114 case DM_TABLE_STATUS:
115 case DM_TARGET_MSG:
116 tprintf(", target_count=%" PRIu32,
117 ioc->target_count);
118 tprintf(", open_count=%" PRIu32,
119 ioc->open_count);
120 tprintf(", event_nr=%" PRIu32,
121 ioc->event_nr);
122 break;
123 }
124 }
125 }
126
127 #include "xlat/dm_flags.h"
128
129 static void
dm_decode_flags(const struct dm_ioctl * ioc)130 dm_decode_flags(const struct dm_ioctl *ioc)
131 {
132 tprints(", flags=");
133 printflags(dm_flags, ioc->flags, "DM_???");
134 }
135
136 static void
dm_decode_dm_target_spec(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)137 dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
138 const struct dm_ioctl *const ioc)
139 {
140 static const uint32_t target_spec_size =
141 sizeof(struct dm_target_spec);
142 uint32_t i;
143 uint32_t offset = ioc->data_start;
144 uint32_t offset_end = 0;
145
146 if (abbrev(tcp)) {
147 if (ioc->target_count)
148 tprints(", ...");
149
150 return;
151 }
152
153 for (i = 0; i < ioc->target_count; i++) {
154 tprints(", ");
155
156 if (i && offset <= offset_end)
157 goto misplaced;
158
159 offset_end = offset + target_spec_size;
160
161 if (offset_end <= offset || offset_end > ioc->data_size)
162 goto misplaced;
163
164 if (i >= max_strlen) {
165 tprints("...");
166 break;
167 }
168
169 struct dm_target_spec s;
170
171 if (umove_or_printaddr(tcp, addr + offset, &s))
172 break;
173
174 tprintf("{sector_start=%" PRI__u64 ", length=%" PRI__u64,
175 s.sector_start, s.length);
176
177 if (exiting(tcp))
178 tprintf(", status=%" PRId32, s.status);
179
180 tprints(", target_type=");
181 print_quoted_string(s.target_type, DM_MAX_TYPE_NAME,
182 QUOTE_0_TERMINATED);
183
184 tprints(", string=");
185 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
186 QUOTE_0_TERMINATED);
187 tprints("}");
188
189 if (entering(tcp))
190 offset += s.next;
191 else
192 offset = ioc->data_start + s.next;
193 }
194
195 return;
196
197 misplaced:
198 tprints("???");
199 tprints_comment("misplaced struct dm_target_spec");
200 }
201
202 bool
dm_print_dev(struct tcb * tcp,void * dev_ptr,size_t dev_size,void * dummy)203 dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy)
204 {
205 uint64_t *dev = (uint64_t *) dev_ptr;
206
207 print_dev_t(*dev);
208
209 return 1;
210 }
211
212 static void
dm_decode_dm_target_deps(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)213 dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
214 const struct dm_ioctl *const ioc)
215 {
216 if (ioc->data_start == ioc->data_size)
217 return;
218
219 tprints(", ");
220
221 if (abbrev(tcp)) {
222 tprints("...");
223 return;
224 }
225
226 static const uint32_t target_deps_dev_offs =
227 offsetof(struct dm_target_deps, dev);
228 uint64_t dev_buf;
229 struct dm_target_deps s;
230 uint32_t offset = ioc->data_start;
231 uint32_t offset_end = offset + target_deps_dev_offs;
232 uint32_t space;
233
234 if (offset_end <= offset || offset_end > ioc->data_size)
235 goto misplaced;
236
237 if (umove_or_printaddr(tcp, addr + offset, &s))
238 return;
239
240 space = (ioc->data_size - offset_end) / sizeof(dev_buf);
241
242 if (s.count > space)
243 goto misplaced;
244
245 tprintf("{count=%u, deps=", s.count);
246
247 print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
248 umoven_or_printaddr, dm_print_dev, NULL);
249
250 tprints("}");
251
252 return;
253
254 misplaced:
255 tprints("???");
256 tprints_comment("misplaced struct dm_target_deps");
257 }
258
259 static void
dm_decode_dm_name_list(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)260 dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
261 const struct dm_ioctl *const ioc)
262 {
263 static const uint32_t name_list_name_offs =
264 offsetof(struct dm_name_list, name);
265 struct dm_name_list s;
266 uint32_t offset = ioc->data_start;
267 uint32_t offset_end = 0;
268 uint32_t count;
269
270 if (ioc->data_start == ioc->data_size)
271 return;
272
273 if (abbrev(tcp)) {
274 tprints(", ...");
275 return;
276 }
277
278 for (count = 0;; count++) {
279 tprints(", ");
280
281 if (count && offset <= offset_end)
282 goto misplaced;
283
284 offset_end = offset + name_list_name_offs;
285
286 if (offset_end <= offset || offset_end > ioc->data_size)
287 goto misplaced;
288
289 if (count >= max_strlen) {
290 tprints("...");
291 break;
292 }
293
294 if (umove_or_printaddr(tcp, addr + offset, &s))
295 break;
296
297 tprints("{dev=");
298 print_dev_t(s.dev);
299
300 tprints("name=");
301 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
302 QUOTE_0_TERMINATED);
303 tprints("}");
304
305 if (!s.next)
306 break;
307
308 offset += s.next;
309 }
310
311 return;
312
313 misplaced:
314 tprints("???");
315 tprints_comment("misplaced struct dm_name_list");
316 }
317
318 static void
dm_decode_dm_target_versions(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)319 dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
320 const struct dm_ioctl *const ioc)
321 {
322 static const uint32_t target_vers_name_offs =
323 offsetof(struct dm_target_versions, name);
324 struct dm_target_versions s;
325 uint32_t offset = ioc->data_start;
326 uint32_t offset_end = 0;
327 uint32_t count;
328
329 if (ioc->data_start == ioc->data_size)
330 return;
331
332 if (abbrev(tcp)) {
333 tprints(", ...");
334 return;
335 }
336
337 for (count = 0;; count++) {
338 tprints(", ");
339
340 if (count && offset <= offset_end)
341 goto misplaced;
342
343 offset_end = offset + target_vers_name_offs;
344
345 if (offset_end <= offset || offset_end > ioc->data_size)
346 goto misplaced;
347
348 if (count >= max_strlen) {
349 tprints("...");
350 break;
351 }
352
353 if (umove_or_printaddr(tcp, addr + offset, &s))
354 break;
355
356 tprints("{name=");
357 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
358 QUOTE_0_TERMINATED);
359 tprintf(", version=%" PRIu32 ".%" PRIu32 ".%" PRIu32 "}",
360 s.version[0], s.version[1], s.version[2]);
361
362 if (!s.next)
363 break;
364
365 offset += s.next;
366 }
367
368 return;
369
370 misplaced:
371 tprints("???");
372 tprints_comment("misplaced struct dm_target_versions");
373 }
374
375 static void
dm_decode_dm_target_msg(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)376 dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
377 const struct dm_ioctl *const ioc)
378 {
379 if (ioc->data_start == ioc->data_size)
380 return;
381
382 tprints(", ");
383
384 if (abbrev(tcp)) {
385 tprints("...");
386 return;
387 }
388
389 static const uint32_t target_msg_message_offs =
390 offsetof(struct dm_target_msg, message);
391 uint32_t offset = ioc->data_start;
392 uint32_t offset_end = offset + target_msg_message_offs;
393
394 if (offset_end > offset && offset_end <= ioc->data_size) {
395 struct dm_target_msg s;
396
397 if (umove_or_printaddr(tcp, addr + offset, &s))
398 return;
399
400 tprintf("{sector=%" PRI__u64 ", message=", s.sector);
401 printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
402 QUOTE_0_TERMINATED);
403 tprints("}");
404 } else {
405 tprints("???");
406 tprints_comment("misplaced struct dm_target_msg");
407 }
408 }
409
410 static void
dm_decode_string(struct tcb * const tcp,const kernel_ulong_t addr,const struct dm_ioctl * const ioc)411 dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
412 const struct dm_ioctl *const ioc)
413 {
414 tprints(", ");
415
416 if (abbrev(tcp)) {
417 tprints("...");
418 return;
419 }
420
421 uint32_t offset = ioc->data_start;
422
423 if (offset <= ioc->data_size) {
424 tprints("string=");
425 printstr_ex(tcp, addr + offset, ioc->data_size - offset,
426 QUOTE_0_TERMINATED);
427 } else {
428 tprints("???");
429 tprints_comment("misplaced string");
430 }
431 }
432
433 static inline bool
dm_ioctl_has_params(const unsigned int code)434 dm_ioctl_has_params(const unsigned int code)
435 {
436 switch (code) {
437 case DM_VERSION:
438 case DM_REMOVE_ALL:
439 case DM_DEV_CREATE:
440 case DM_DEV_REMOVE:
441 case DM_DEV_SUSPEND:
442 case DM_DEV_STATUS:
443 case DM_TABLE_CLEAR:
444 return false;
445 }
446
447 return true;
448 }
449
450 static int
dm_known_ioctl(struct tcb * const tcp,const unsigned int code,const kernel_ulong_t arg)451 dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
452 const kernel_ulong_t arg)
453 {
454 struct dm_ioctl *ioc = NULL;
455 struct dm_ioctl *entering_ioc = NULL;
456 bool ioc_changed = false;
457
458 if (entering(tcp)) {
459 ioc = malloc(sizeof(*ioc));
460 if (!ioc)
461 return 0;
462 } else {
463 ioc = alloca(sizeof(*ioc));
464 }
465
466 if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
467 (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
468 if (entering(tcp))
469 free(ioc);
470 return 0;
471 }
472 if (entering(tcp))
473 set_tcb_priv_data(tcp, ioc, free);
474 else {
475 entering_ioc = get_tcb_priv_data(tcp);
476
477 /*
478 * retrieve_status, __dev_status called only in case of success,
479 * so it looks like there's no need to check open_count,
480 * event_nr, target_count, dev fields for change (they are
481 * printed only in case of absence of errors).
482 */
483 if (!entering_ioc ||
484 (ioc->version[0] != entering_ioc->version[0]) ||
485 (ioc->version[1] != entering_ioc->version[1]) ||
486 (ioc->version[2] != entering_ioc->version[2]) ||
487 (ioc->data_size != entering_ioc->data_size) ||
488 (ioc->data_start != entering_ioc->data_start) ||
489 (ioc->flags != entering_ioc->flags))
490 ioc_changed = true;
491 }
492
493 if (exiting(tcp) && syserror(tcp) && !ioc_changed)
494 return 1;
495
496 /*
497 * device mapper code uses %d in some places and %u in another, but
498 * fields themselves are declared as __u32.
499 */
500 tprintf("%s{version=%u.%u.%u", entering(tcp) ? ", " : " => ",
501 ioc->version[0], ioc->version[1], ioc->version[2]);
502 /*
503 * if we use a different version of ABI, do not attempt to decode
504 * ioctl fields
505 */
506 if (ioc->version[0] != DM_VERSION_MAJOR) {
507 tprints_comment("unsupported device mapper ABI version");
508 goto skip;
509 }
510
511 tprintf(", data_size=%u", ioc->data_size);
512
513 if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
514 tprints_comment("data_size too small");
515 goto skip;
516 }
517
518 if (dm_ioctl_has_params(code))
519 tprintf(", data_start=%u", ioc->data_start);
520
521 dm_decode_device(code, ioc);
522 dm_decode_values(tcp, code, ioc);
523 dm_decode_flags(ioc);
524
525 switch (code) {
526 case DM_DEV_WAIT:
527 case DM_TABLE_STATUS:
528 if (entering(tcp) || syserror(tcp))
529 break;
530 dm_decode_dm_target_spec(tcp, arg, ioc);
531 break;
532 case DM_TABLE_LOAD:
533 if (exiting(tcp))
534 break;
535 dm_decode_dm_target_spec(tcp, arg, ioc);
536 break;
537 case DM_TABLE_DEPS:
538 if (entering(tcp) || syserror(tcp))
539 break;
540 dm_decode_dm_target_deps(tcp, arg, ioc);
541 break;
542 case DM_LIST_DEVICES:
543 if (entering(tcp) || syserror(tcp))
544 break;
545 dm_decode_dm_name_list(tcp, arg, ioc);
546 break;
547 case DM_LIST_VERSIONS:
548 if (entering(tcp) || syserror(tcp))
549 break;
550 dm_decode_dm_target_versions(tcp, arg, ioc);
551 break;
552 case DM_TARGET_MSG:
553 if (entering(tcp))
554 dm_decode_dm_target_msg(tcp, arg, ioc);
555 else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG)
556 dm_decode_string(tcp, arg, ioc);
557 break;
558 case DM_DEV_RENAME:
559 case DM_DEV_SET_GEOMETRY:
560 if (exiting(tcp))
561 break;
562 dm_decode_string(tcp, arg, ioc);
563 break;
564 }
565
566 skip:
567 tprints("}");
568 return 1;
569 }
570
571 int
dm_ioctl(struct tcb * const tcp,const unsigned int code,const kernel_ulong_t arg)572 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
573 {
574 switch (code) {
575 case DM_VERSION:
576 case DM_REMOVE_ALL:
577 case DM_LIST_DEVICES:
578 case DM_DEV_CREATE:
579 case DM_DEV_REMOVE:
580 case DM_DEV_RENAME:
581 case DM_DEV_SUSPEND:
582 case DM_DEV_STATUS:
583 case DM_DEV_WAIT:
584 case DM_TABLE_LOAD:
585 case DM_TABLE_CLEAR:
586 case DM_TABLE_DEPS:
587 case DM_TABLE_STATUS:
588 case DM_LIST_VERSIONS:
589 case DM_TARGET_MSG:
590 case DM_DEV_SET_GEOMETRY:
591 return dm_known_ioctl(tcp, code, arg);
592 default:
593 return 0;
594 }
595 }
596
597 # else /* !(DM_VERSION_MAJOR == 4) */
598
599 int
dm_ioctl(struct tcb * const tcp,const unsigned int code,const kernel_ulong_t arg)600 dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
601 {
602 return 0;
603 }
604
605 # endif /* DM_VERSION_MAJOR == 4 */
606 #endif /* HAVE_LINUX_DM_IOCTL_H */
607