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