• 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 <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