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