+/*
+ * Support for decoding of DM_* ioctl commands.
+ *
+ * Copyright (c) 2016 Mikulas Patocka <mpatocka@redhat.com>
+ * Copyright (c) 2016 Masatake Yamato <yamato@redhat.com>
+ * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
+ * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
+ * Copyright (c) 2016-2018 The strace developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#include "defs.h"
#ifdef HAVE_LINUX_DM_IOCTL_H
+# include "print_fields.h"
# include <linux/dm-ioctl.h>
# include <linux/ioctl.h>
-# include <sys/sysmacros.h>
-
# if DM_VERSION_MAJOR == 4
/* Definitions for command which have been added later */
# ifndef DM_LIST_VERSIONS
-# define DM_LIST_VERSIONS _IOWR(DM_IOCTL, 0xd, struct dm_ioctl)
+# define DM_LIST_VERSIONS _IOWR(DM_IOCTL, 0x0d, struct dm_ioctl)
# endif
# ifndef DM_TARGET_MSG
-# define DM_TARGET_MSG _IOWR(DM_IOCTL, 0xe, struct dm_ioctl)
+# define DM_TARGET_MSG _IOWR(DM_IOCTL, 0x0e, struct dm_ioctl)
# endif
# ifndef DM_DEV_SET_GEOMETRY
-# define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0xf, struct dm_ioctl)
+# define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, 0x0f, struct dm_ioctl)
+# endif
+# ifndef DM_DEV_ARM_POLL
+# define DM_DEV_ARM_POLL _IOWR(DM_IOCTL, 0x10, struct dm_ioctl)
# endif
break;
default:
if (ioc->dev)
- tprintf(", dev=makedev(%u, %u)",
- major(ioc->dev), minor(ioc->dev));
- if (ioc->name[0]) {
- tprints(", name=");
- print_quoted_string(ioc->name, DM_NAME_LEN,
- QUOTE_0_TERMINATED);
- }
- if (ioc->uuid[0]) {
- tprints(", uuid=");
- print_quoted_string(ioc->uuid, DM_UUID_LEN,
- QUOTE_0_TERMINATED);
- }
+ PRINT_FIELD_DEV(", ", *ioc, dev);
+
+ if (ioc->name[0])
+ PRINT_FIELD_CSTRING(", ", *ioc, name);
+
+ if (ioc->uuid[0])
+ PRINT_FIELD_CSTRING(", ", *ioc, uuid);
+
break;
}
}
if (entering(tcp)) {
switch (code) {
case DM_TABLE_LOAD:
- tprintf(", target_count=%" PRIu32,
- ioc->target_count);
+ PRINT_FIELD_U(", ", *ioc, target_count);
break;
case DM_DEV_SUSPEND:
if (ioc->flags & DM_SUSPEND_FLAG)
break;
- /* Fall through */
+ ATTRIBUTE_FALLTHROUGH;
case DM_DEV_RENAME:
case DM_DEV_REMOVE:
case DM_DEV_WAIT:
- tprintf(", event_nr=%" PRIu32,
- ioc->event_nr);
+ PRINT_FIELD_U(", ", *ioc, event_nr);
break;
}
} else if (!syserror(tcp)) {
case DM_TABLE_DEPS:
case DM_TABLE_STATUS:
case DM_TARGET_MSG:
- tprintf(", target_count=%" PRIu32,
- ioc->target_count);
- tprintf(", open_count=%" PRIu32,
- ioc->open_count);
- tprintf(", event_nr=%" PRIu32,
- ioc->event_nr);
+ PRINT_FIELD_U(", ", *ioc, target_count);
+ PRINT_FIELD_U(", ", *ioc, open_count);
+ PRINT_FIELD_U(", ", *ioc, event_nr);
break;
}
}
static void
dm_decode_flags(const struct dm_ioctl *ioc)
{
- tprints(", flags=");
- printflags(dm_flags, ioc->flags, "DM_???");
+ PRINT_FIELD_FLAGS(", ", *ioc, flags, dm_flags, "DM_???");
}
static void
-dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ureg_t addr,
+dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
const struct dm_ioctl *const ioc)
{
static const uint32_t target_spec_size =
sizeof(struct dm_target_spec);
uint32_t i;
uint32_t offset = ioc->data_start;
- uint32_t offset_end;
+ uint32_t offset_end = 0;
if (abbrev(tcp)) {
if (ioc->target_count)
}
for (i = 0; i < ioc->target_count; i++) {
- struct dm_target_spec s;
+ tprints(", ");
+
+ if (i && offset <= offset_end)
+ goto misplaced;
offset_end = offset + target_spec_size;
if (offset_end <= offset || offset_end > ioc->data_size)
goto misplaced;
- tprints(", ");
-
if (i >= max_strlen) {
tprints("...");
break;
}
+ struct dm_target_spec s;
+
if (umove_or_printaddr(tcp, addr + offset, &s))
break;
- tprintf("{sector_start=%" PRI__u64 ", length=%" PRI__u64,
- s.sector_start, s.length);
+ PRINT_FIELD_U("{", s, sector_start);
+ PRINT_FIELD_U(", ", s, length);
if (exiting(tcp))
- tprintf(", status=%" PRId32, s.status);
+ PRINT_FIELD_D(", ", s, status);
- tprints(", target_type=");
- print_quoted_string(s.target_type, DM_MAX_TYPE_NAME,
- QUOTE_0_TERMINATED);
+ PRINT_FIELD_CSTRING(", ", s, target_type);
tprints(", string=");
printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
QUOTE_0_TERMINATED);
- tprintf("}");
+ tprints("}");
if (entering(tcp))
offset += s.next;
else
offset = ioc->data_start + s.next;
-
- if (offset <= offset_end)
- goto misplaced;
}
return;
misplaced:
- tprints(", /* misplaced struct dm_target_spec */ ...");
+ tprints("???");
+ tprints_comment("misplaced struct dm_target_spec");
}
bool
{
uint64_t *dev = (uint64_t *) dev_ptr;
- tprintf("makedev(%u, %u)", major(*dev), minor(*dev));
+ print_dev_t(*dev);
return 1;
}
static void
-dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ureg_t addr,
+dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
const struct dm_ioctl *const ioc)
{
+ if (ioc->data_start == ioc->data_size)
+ return;
+
+ tprints(", ");
+
+ if (abbrev(tcp)) {
+ tprints("...");
+ return;
+ }
+
static const uint32_t target_deps_dev_offs =
offsetof(struct dm_target_deps, dev);
uint64_t dev_buf;
uint32_t offset_end = offset + target_deps_dev_offs;
uint32_t space;
- if (abbrev(tcp)) {
- tprints(", ...");
- return;
- }
-
- tprints(", ");
-
if (offset_end <= offset || offset_end > ioc->data_size)
goto misplaced;
if (s.count > space)
goto misplaced;
- tprintf("{count=%u, deps=", s.count);
+ PRINT_FIELD_U("{", s, count);
+ tprints(", deps=");
print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
- umoven_or_printaddr, dm_print_dev, NULL);
+ tfetch_mem, dm_print_dev, NULL);
tprints("}");
return;
misplaced:
- tprints("/* misplaced struct dm_target_deps */ ...");
+ tprints("???");
+ tprints_comment("misplaced struct dm_target_deps");
}
static void
-dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ureg_t addr,
+dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
const struct dm_ioctl *const ioc)
{
static const uint32_t name_list_name_offs =
offsetof(struct dm_name_list, name);
struct dm_name_list s;
uint32_t offset = ioc->data_start;
- uint32_t offset_end;
+ uint32_t offset_end = 0;
uint32_t count;
+ int rc;
+
+ if (ioc->data_start == ioc->data_size)
+ return;
if (abbrev(tcp)) {
tprints(", ...");
}
for (count = 0;; count++) {
+ tprints(", ");
+
+ if (count && offset <= offset_end)
+ goto misplaced;
+
offset_end = offset + name_list_name_offs;
if (offset_end <= offset || offset_end > ioc->data_size)
goto misplaced;
- tprints(", ");
-
if (count >= max_strlen) {
tprints("...");
break;
if (umove_or_printaddr(tcp, addr + offset, &s))
break;
- if (!count && !s.dev) {
- tprints("/* no devices present */");
- break;
+
+ PRINT_FIELD_DEV("{", s, dev);
+ tprints(", name=");
+ rc = printstr_ex(tcp, addr + offset_end,
+ ioc->data_size - offset_end,
+ QUOTE_0_TERMINATED);
+
+ /*
+ * In Linux v4.13-rc1~137^2~13 it has been decided to cram in
+ * one more undocumented field after the device name, as if the
+ * format decoding was not twisted enough already. So, we have
+ * to check "next" now, and if it _looks like_ that there is
+ * a space for one additional integer, let's print it. As if the
+ * perversity with "name string going further than pointer to
+ * the next one" wasn't enough. Moreover, the calculation was
+ * broken for m32 on 64-bit kernels until v4.14-rc4~20^2~3, and
+ * we have no ability to detect kernel bit-ness (on x86, at
+ * least), so refrain from printing it for the DM versions below
+ * 4.37 (the original version was also aligned differently than
+ * now even on 64 bit).
+ */
+
+ if ((rc > 0) && ioc->version[1] >= 37) {
+ kernel_ulong_t event_addr =
+ (addr + offset_end + rc + 7) & ~7;
+ uint32_t event_nr;
+
+ if ((event_addr + sizeof(event_nr)) <=
+ (addr + offset + s.next) &&
+ !umove(tcp, event_addr, &event_nr))
+ tprintf(", event_nr=%" PRIu32, event_nr);
}
- tprintf("{dev=makedev(%u, %u), name=", major(s.dev),
- minor(s.dev));
- printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
- QUOTE_0_TERMINATED);
tprints("}");
if (!s.next)
break;
offset += s.next;
- if (offset <= offset_end)
- goto misplaced;
}
return;
misplaced:
- tprints(", /* misplaced struct dm_name_list */ ...");
+ tprints("???");
+ tprints_comment("misplaced struct dm_name_list");
}
static void
-dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ureg_t addr,
+dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
const struct dm_ioctl *const ioc)
{
static const uint32_t target_vers_name_offs =
offsetof(struct dm_target_versions, name);
struct dm_target_versions s;
uint32_t offset = ioc->data_start;
- uint32_t offset_end;
+ uint32_t offset_end = 0;
uint32_t count;
+ if (ioc->data_start == ioc->data_size)
+ return;
+
if (abbrev(tcp)) {
tprints(", ...");
return;
}
for (count = 0;; count++) {
+ tprints(", ");
+
+ if (count && offset <= offset_end)
+ goto misplaced;
+
offset_end = offset + target_vers_name_offs;
if (offset_end <= offset || offset_end > ioc->data_size)
goto misplaced;
- tprints(", ");
-
if (count >= max_strlen) {
tprints("...");
break;
break;
offset += s.next;
- if (offset <= offset_end)
- goto misplaced;
}
return;
misplaced:
- tprints(", /* misplaced struct dm_target_versions */ ...");
+ tprints("???");
+ tprints_comment("misplaced struct dm_target_versions");
}
static void
-dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ureg_t addr,
- const struct dm_ioctl *const ioc)
+dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
+ const struct dm_ioctl *const ioc)
{
- static const uint32_t target_msg_message_offs =
- offsetof(struct dm_target_msg, message);
- uint32_t offset = ioc->data_start;
- uint32_t offset_end = offset + target_msg_message_offs;
+ if (ioc->data_start == ioc->data_size)
+ return;
+
+ tprints(", ");
if (abbrev(tcp)) {
- tprints(", ...");
+ tprints("...");
return;
}
+ static const uint32_t target_msg_message_offs =
+ offsetof(struct dm_target_msg, message);
+ uint32_t offset = ioc->data_start;
+ uint32_t offset_end = offset + target_msg_message_offs;
+
if (offset_end > offset && offset_end <= ioc->data_size) {
struct dm_target_msg s;
- tprints(", ");
-
if (umove_or_printaddr(tcp, addr + offset, &s))
return;
- tprintf("{sector=%" PRI__u64 ", message=", s.sector);
+ PRINT_FIELD_U("{", s, sector);
+ tprints(", message=");
printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
QUOTE_0_TERMINATED);
tprints("}");
} else {
- tprints(", /* misplaced struct dm_target_msg */");
+ tprints("???");
+ tprints_comment("misplaced struct dm_target_msg");
}
}
static void
-dm_decode_string(struct tcb *const tcp, const kernel_ureg_t addr,
+dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
const struct dm_ioctl *const ioc)
{
- uint32_t offset = ioc->data_start;
+ tprints(", ");
if (abbrev(tcp)) {
- tprints(", ...");
+ tprints("...");
return;
}
- if (offset < ioc->data_size) {
- tprints(", string=");
+ uint32_t offset = ioc->data_start;
+
+ if (offset <= ioc->data_size) {
+ tprints("string=");
printstr_ex(tcp, addr + offset, ioc->data_size - offset,
QUOTE_0_TERMINATED);
} else {
- tprints(", /* misplaced string */");
+ tprints("???");
+ tprints_comment("misplaced string");
}
}
case DM_DEV_SUSPEND:
case DM_DEV_STATUS:
case DM_TABLE_CLEAR:
+ case DM_DEV_ARM_POLL:
return false;
}
static int
dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
- const kernel_ureg_t arg)
+ const kernel_ulong_t arg)
{
struct dm_ioctl *ioc = NULL;
struct dm_ioctl *entering_ioc = NULL;
}
if (exiting(tcp) && syserror(tcp) && !ioc_changed)
- return 1;
+ return RVAL_IOCTL_DECODED;
/*
* device mapper code uses %d in some places and %u in another, but
* ioctl fields
*/
if (ioc->version[0] != DM_VERSION_MAJOR) {
- tprints(", /* Unsupported device mapper ABI version */ ...");
+ tprints_comment("unsupported device mapper ABI version");
goto skip;
}
- tprintf(", data_size=%u", ioc->data_size);
-
- if (dm_ioctl_has_params(code))
- tprintf(", data_start=%u", ioc->data_start);
+ PRINT_FIELD_U(", ", *ioc, data_size);
if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
- tprints(", /* Incorrect data_size */ ...");
+ tprints_comment("data_size too small");
goto skip;
}
+ if (dm_ioctl_has_params(code))
+ PRINT_FIELD_U(", ", *ioc, data_start);
+
dm_decode_device(code, ioc);
dm_decode_values(tcp, code, ioc);
dm_decode_flags(ioc);
skip:
tprints("}");
- return 1;
+ return entering(tcp) ? 0 : RVAL_IOCTL_DECODED;
}
int
-dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ureg_t arg)
+dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
{
switch (code) {
case DM_VERSION:
case DM_LIST_VERSIONS:
case DM_TARGET_MSG:
case DM_DEV_SET_GEOMETRY:
+ case DM_DEV_ARM_POLL:
return dm_known_ioctl(tcp, code, arg);
default:
- return 0;
+ return RVAL_DECODED;
}
}
# else /* !(DM_VERSION_MAJOR == 4) */
int
-dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ureg_t arg)
+dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
{
- return 0;
+ return RVAL_DECODED;
}
# endif /* DM_VERSION_MAJOR == 4 */