[RFC v2] Decode UFFDIO_* ioctls

Dr. David Alan Gilbert (git) dgilbert at redhat.com
Fri Apr 22 18:02:08 UTC 2016


From: "Dr. David Alan Gilbert" <dgilbert at redhat.com>

 !!! TODO!
   Add some tests (How do I make it immune from kernel changes to flags
       returned?)
   Tests the zeropage and wake code
   don't use my decode_flags64 - I guess I should be using printflags
       but printflags uses struct xlat and they're both unsigned int
       where I'm dealing in uint64_t's; perhaps if I just rework
       printflags/xlat it'll all work ???
 !!!

Decode the ioctl's associated with the userfaultfd fd.
Note that they tend to read from and also returns result in it's data
structure.

* configure.ac: Add test for userfaultfd.h.
* userfaultfd.c: Add ioctl decoder.
* defs.h: declare that decoder.
* ioctl.c: Wire in the new decoder.

Example output:
14279 ioctl(20, UFFDIO_API{api=0xaa,
features.in=0,features.out=0,ioctls=0x8000000000000003(Register|Unregister|API)})
= 0
14279 ioctl(20, UFFDIO_REGISTER{range={start=0x7f72ec200000,
len=0x8fc00000}, mode=0x1(Missing),ioctls=0x1c(Wake|Copy|Zeropage)}) = 0
14308 ioctl(20, UFFDIO_UNREGISTER{start=0x7f72ec200000, len=0x8fc00000})
= 0
14308 ioctl(20, UFFDIO_COPY{dst=0x7f730e076000, src=0x5561e2ad66c0,
len=4096, mode=0, copy=4096}) = 0
---
 configure.ac  |   1 +
 defs.h        |   1 +
 ioctl.c       |   4 ++
 userfaultfd.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 180 insertions(+)

diff --git a/configure.ac b/configure.ac
index 2b29c94..8ad844e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -377,6 +377,7 @@ AC_CHECK_HEADERS(m4_normalize([
 	linux/securebits.h
 	linux/sem.h
 	linux/shm.h
+	linux/userfaultfd.h
 	linux/utsname.h
 	mqueue.h
 	netinet/sctp.h
diff --git a/defs.h b/defs.h
index ac59349..4894fdb 100644
--- a/defs.h
+++ b/defs.h
@@ -649,6 +649,7 @@ extern int scsi_ioctl(struct tcb *, const unsigned int, long);
 extern int sock_ioctl(struct tcb *, const unsigned int, long);
 extern int term_ioctl(struct tcb *, const unsigned int, long);
 extern int ubi_ioctl(struct tcb *, const unsigned int, long);
+extern int uffdio_ioctl(struct tcb *, const unsigned int, long);
 extern int v4l2_ioctl(struct tcb *, const unsigned int, long);
 
 extern int tv_nz(const struct timeval *);
diff --git a/ioctl.c b/ioctl.c
index f70dc44..1b69e97 100644
--- a/ioctl.c
+++ b/ioctl.c
@@ -263,6 +263,10 @@ ioctl_decode(struct tcb *tcp)
 	case 'E':
 		return evdev_ioctl(tcp, code, arg);
 #endif
+#ifdef HAVE_LINUX_USERFAULTFD_H
+	case 0xaa:
+		return uffdio_ioctl(tcp, code, arg);
+#endif
 	default:
 		break;
 	}
diff --git a/userfaultfd.c b/userfaultfd.c
index 15f825a..accd103 100644
--- a/userfaultfd.c
+++ b/userfaultfd.c
@@ -30,6 +30,180 @@
 
 #include "xlat/uffd_flags.h"
 
+#ifdef HAVE_LINUX_USERFAULTFD_H
+#include <linux/ioctl.h>
+#include <linux/userfaultfd.h>
+
+/*
+ * Print value (in hex) followed by a decode using the strings
+ * and masks in masks and names; we stop at a 0 entry in masks.
+ * !!! Need to turn this into printflags - but it's int
+ */
+static void
+decode_flags64(const char *leader, uint64_t value, const uint64_t masks[],
+		const char *names[])
+{
+	unsigned i;
+	bool first = true;
+
+	if (!value) {
+		tprintf("%s%#" PRIx64, leader, value);
+		return;
+	}
+
+	tprintf("%s%#" PRIx64 "(", leader, value);
+
+	for (i = 0; masks[i]; i++) {
+		if (value & masks[i]) {
+			tprintf(first ? "%s" : "|%s", names[i]);
+			first = false;
+			value -= masks[i];
+		}
+	}
+	if (value)
+		tprintf(first ? "%#" PRIx64 : "|%#" PRIx64, value);
+	tprintf(")");
+}
+
+static void
+tprintf_uffdio_range(const struct uffdio_range *range)
+{
+	tprintf("{start=%#" PRI__x64 ", len=%#" PRI__x64 "}",
+		range->start, range->len);
+}
+
+int
+uffdio_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
+{
+	switch (code) {
+	case UFFDIO_API: {
+		struct uffdio_api ua;
+		const uint64_t api_ioctl_masks[] = {
+			(uint64_t)1 << _UFFDIO_REGISTER,
+			(uint64_t)1 << _UFFDIO_UNREGISTER,
+			(uint64_t)1 << _UFFDIO_API,
+			0
+			};
+		const char *api_ioctl_names[] = { "Register", "Unregister",
+						  "API" };
+
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &ua))
+				return RVAL_DECODED | 1;
+			/* Features is intended to contain some flags, but
+			 * there aren't any defined yet.
+			 */
+			tprintf("{api=%#" PRI__x64 ", features.in=%#" PRI__x64,
+				ua.api, ua.features);
+			return 1;
+		} else {
+			if (!umove_or_printaddr(tcp, arg, &ua)) {
+				tprintf(",features.out=%#" PRI__x64,
+					ua.features);
+				decode_flags64(",ioctls=", ua.ioctls,
+						api_ioctl_masks, api_ioctl_names);
+			}
+			tprintf("}");
+			return 1;
+		}
+	}
+
+	case UFFDIO_COPY: {
+		struct uffdio_copy uc;
+		const uint64_t copy_mode_masks[] = {
+			UFFDIO_COPY_MODE_DONTWAKE,
+			0
+			};
+		const char *copy_mode_names[] = { "Dontwake" };
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &uc))
+				return RVAL_DECODED | 1;
+			tprintf("{dst=%#" PRI__x64 ", src=%#" PRI__x64
+				", len=%" PRI__u64,
+				uc.dst, uc.src, uc.len);
+			decode_flags64(", mode=", uc.mode,
+					copy_mode_masks, copy_mode_names);
+			return 1;
+		} else {
+			if (!umove_or_printaddr(tcp, arg, &uc))
+				tprintf(", copy=%" PRI__s64, uc.copy);
+			tprints("}");
+			return 1;
+		}
+	}
+
+	case UFFDIO_REGISTER: {
+		struct uffdio_register ur;
+		const uint64_t mode_masks[] = {
+			UFFDIO_REGISTER_MODE_MISSING,
+			UFFDIO_REGISTER_MODE_WP,
+			0
+			};
+		const char *mode_names[] = { "Missing", "WP" };
+		const uint64_t reg_ioctl_masks[] = {
+			(__u64)1 << _UFFDIO_WAKE,
+			(__u64)1 << _UFFDIO_COPY,
+			(__u64)1 << _UFFDIO_ZEROPAGE,
+			0
+			};
+		const char *reg_ioctl_names[] = { "Wake", "Copy", "Zeropage" };
+
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &ur))
+				return RVAL_DECODED | 1;
+			tprintf("{range=");
+			tprintf_uffdio_range(&ur.range);
+			decode_flags64(", mode=", ur.mode,
+					mode_masks, mode_names);
+			return 1;
+		} else {
+			if (!umove_or_printaddr(tcp, arg, &ur))
+				decode_flags64(",ioctls=", ur.ioctls,
+				reg_ioctl_masks, reg_ioctl_names);
+			tprints("}");
+			return 1;
+		}
+	}
+
+	case UFFDIO_UNREGISTER:
+	case UFFDIO_WAKE: {
+		struct uffdio_range ura;
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &ura))
+				return RVAL_DECODED | 1;
+			tprintf_uffdio_range(&ura);
+			return RVAL_DECODED | 1;
+		}
+	}
+	case UFFDIO_ZEROPAGE: {
+		struct uffdio_zeropage uz;
+		const uint64_t zero_mode_masks[] = {
+			UFFDIO_ZEROPAGE_MODE_DONTWAKE,
+			0
+			};
+		const char *zero_mode_names[] = { "Dontwake" };
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &uz))
+				return RVAL_DECODED | 1;
+			tprintf("{range=");
+			tprintf_uffdio_range(&uz.range);
+			decode_flags64(", mode=", uz.mode,
+					zero_mode_masks, zero_mode_names);
+			return 1;
+		} else {
+			if (!umove_or_printaddr(tcp, arg, &uz))
+				tprintf(", zeropage=%" PRI__s64, uz.zeropage);
+			tprints("}");
+			return 1;
+		}
+	}
+
+	default:
+		return RVAL_DECODED;
+	}
+}
+#endif
+
 SYS_FUNC(userfaultfd)
 {
 	printflags(uffd_flags, tcp->u_arg[0], "UFFD_???");
-- 
2.5.5





More information about the Strace-devel mailing list