]> granicus.if.org Git - strace/blob - dm.c
mem: handle potential error from sysconf(_SC_PAGESIZE)
[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  * 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                         ATTRIBUTE_FALLTHROUGH;
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 */