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