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