Home | History | Annotate | Download | only in strace
      1 /*
      2  * Support for decoding of DM_* ioctl commands.
      3  *
      4  * Copyright (c) 2016 Mikulas Patocka <mpatocka (at) redhat.com>
      5  * Copyright (c) 2016 Masatake Yamato <yamato (at) redhat.com>
      6  * Copyright (c) 2016 Dmitry V. Levin <ldv (at) altlinux.org>
      7  * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr (at) 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
     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
     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
    124 dm_decode_flags(const struct dm_ioctl *ioc)
    125 {
    126 	PRINT_FIELD_FLAGS(", ", *ioc, flags, dm_flags, "DM_???");
    127 }
    128 
    129 static void
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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