Mirror of strace – the linux syscall tracer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dm.c 13KB

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