[RFC v2] Decode UFFDIO_* ioctls

Dr. David Alan Gilbert dgilbert at redhat.com
Fri Apr 29 19:32:47 UTC 2016


* Dmitry V. Levin (ldv at altlinux.org) wrote:
> On Sat, Apr 23, 2016 at 05:39:42AM +0300, Dmitry V. Levin wrote:
> > On Fri, Apr 22, 2016 at 07:02:08PM +0100, Dr. David Alan Gilbert (git) wrote:
> [...]
> > >    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 ???
> > 
> > With regards to printing 64-bit flags, see the thread starting at
> > https://sourceforge.net/p/strace/mailman/message/34979546/
> 
> The change of struct xlat is in, you can now generate xlat files filled
> with 64-bit values by using "#val_type uint64_t" directive, and print
> 64-bit values using e.g. printxval64() and printflags64().

Thanks;  it's not 100% happy yet; I was trying to use the 1<< notation
and ended up with this minor fix:

diff --git a/xlat.h b/xlat.h
index 64141b3..2f7643c 100644
--- a/xlat.h
+++ b/xlat.h
@@ -10,7 +10,7 @@ struct xlat {
 # define XLAT(val)                     { (unsigned)(val), #val }
 # define XLAT_PAIR(val, str)           { (unsigned)(val), str  }
 # define XLAT_TYPE(type, val)          {     (type)(val), #val }
-# define XLAT_TYPE_PAIR(val, str)      {     (type)(val), str  }
+# define XLAT_TYPE_PAIR(type, val, str)        {     (type)(val), str  }
 # define XLAT_END                      {               0, 0    }

however, that's not enough, because the '1' is the wrong type,
we really need ((type)1) << thing
and I'm not too sure what the best way of fixing that in gen.sh is;
you can see it getting that wrong in UFFDIO_API:

ioctl(4, UFFDIO_API, {api=0xaa, features.in=0, features.out=0, ioctls=_UFFDIO_REGISTER|_UFFDIO_UNREGISTER|0x8000000000000000}) = 0

> >    Add some tests (How do I make it immune from kernel changes to flags
> >        returned?)

> Can't you just print whatever is returned?

Yes, I don't quite understand the blah.test files yet; I see in the userfault.test
you have a carefully chosen -a  value to make it line up.
With the flags it's a little tricky to print what you receive, at the moment
it's not too bad; but lets say another flag got added _UFFDIO_FOO, then
I'd have to decode what was returned, so it's not just a case of printing
the hex.
(Also, what's the easy way just to run one test?)

Anyway, here's my current working world, I've addressed some of your
other comments, but I don't think all yet (like I can see I have a ', '
missing on the unregister case).

Dave

From d3cc77ac3205b44b340e5427568eea684cbccf3d Mon Sep 17 00:00:00 2001
From: "Dr. David Alan Gilbert" <dgilbert at redhat.com>
Date: Thu, 21 Apr 2016 13:49:40 +0100
Subject: [PATCH] Decode UFFDIO_* ioctls

 !!! TODO!
   Flesh out/fix the tests
     check cases with bad pointers/bad fd's
   Tests the zeropage and wake code
   Fix the 64bit xlat stuff
 !!!

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:
  ioctl(4, UFFDIO_API, {api=0xaa, features.in=0, features.out=0,
ioctls=_UFFDIO_REGISTER|_UFFDIO_UNREGISTER|0x8000000000000000}) = 0
  ioctl(20, UFFDIO_REGISTER{range={start=0x7f1b2b921000,
len=0x1000}, mode=UFFDIO_REGISTER_MODE_MISSING, ioctls=1<<
_UFFDIO_WAKE|1<< _UFFDIO_COPY|1<< _UFFDIO_ZEROPAGE}) = 0
  ioctl(20, UFFDIO_UNREGISTER{start=0x7f1b2b921000, len=0x1000} = 0
  ioctl(20, UFFDIO_COPY, {dst=0x42df71, src=0x7f1b02cf1000,
len=945971036180290, copy=4096}) = 0
---
 configure.ac                      |   1 +
 defs.h                            |   1 +
 ioctl.c                           |   4 ++
 userfaultfd.c                     | 121 ++++++++++++++++++++++++++++++++++++++
 xlat/uffd_api_flags.in            |   4 ++
 xlat/uffd_copy_flags.in           |   2 +
 xlat/uffd_register_ioctl_flags.in |   5 ++
 xlat/uffd_register_mode_flags.in  |   3 +
 xlat/uffd_zeropage_flags.in       |   3 +
 9 files changed, 144 insertions(+)
 create mode 100644 xlat/uffd_api_flags.in
 create mode 100644 xlat/uffd_copy_flags.in
 create mode 100644 xlat/uffd_register_ioctl_flags.in
 create mode 100644 xlat/uffd_register_mode_flags.in
 create mode 100644 xlat/uffd_zeropage_flags.in

diff --git a/configure.ac b/configure.ac
index 8a776d2..522a666 100644
--- a/configure.ac
+++ b/configure.ac
@@ -375,6 +375,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 d1e8e1b..fdc6081 100644
--- a/defs.h
+++ b/defs.h
@@ -647,6 +647,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..bbd26dc 100644
--- a/userfaultfd.c
+++ b/userfaultfd.c
@@ -30,6 +30,127 @@
 
 #include "xlat/uffd_flags.h"
 
+#ifdef HAVE_LINUX_USERFAULTFD_H
+#include <linux/ioctl.h>
+#include <linux/userfaultfd.h>
+
+#include "xlat/uffd_api_flags.h"
+#include "xlat/uffd_copy_flags.h"
+#include "xlat/uffd_register_ioctl_flags.h"
+#include "xlat/uffd_register_mode_flags.h"
+#include "xlat/uffd_zeropage_flags.h"
+
+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;
+		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(tcp, arg, &ua)) {
+				tprintf(", features.out=%#" PRI__x64
+					", ioctls=", ua.features);
+				printflags64(uffd_api_flags, ua.ioctls,
+					"_UFFDIO_???");
+			}
+			tprintf("}");
+			return 1;
+		}
+	}
+
+	case UFFDIO_COPY: {
+		struct uffdio_copy uc;
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &uc))
+				return RVAL_DECODED | 1;
+			tprintf(", {dst=%#" PRI__x64 ", src=%#" PRI__x64
+				", len=%" PRI__u64, ", mode=",
+				uc.dst, uc.src, uc.len);
+			printflags64(uffd_copy_flags, uc.mode,
+				"UFFDIO_COPY_???");
+			return 1;
+		} else {
+			if (!umove(tcp, arg, &uc))
+				tprintf(", copy=%" PRI__d64, uc.copy);
+			tprints("}");
+			return 1;
+		}
+	}
+
+	case UFFDIO_REGISTER: {
+		struct uffdio_register ur;
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &ur))
+				return RVAL_DECODED | 1;
+			tprintf("{range=");
+			tprintf_uffdio_range(&ur.range);
+			tprintf(", mode=");
+			printflags64(uffd_register_mode_flags, ur.mode,
+					"UFFDIO_REGISTER_MODE_???");
+			return 1;
+		} else {
+			if (!umove(tcp, arg, &ur)) {
+				tprintf(", ioctls=");
+				printflags64(uffd_register_ioctl_flags, ur.ioctls,
+					"UFFDIO_???");
+			}
+			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;
+		if (entering(tcp)) {
+			if (umove_or_printaddr(tcp, arg, &uz))
+				return RVAL_DECODED | 1;
+			tprintf(", {range=");
+			tprintf_uffdio_range(&uz.range);
+			tprintf(", mode=");
+			printflags64(uffd_zeropage_flags, uz.mode,
+				"UFFDIO_ZEROPAGE_???");
+			return 1;
+		} else {
+			if (!umove(tcp, arg, &uz))
+				tprintf(", zeropage=%" PRI__d64, uz.zeropage);
+			tprints("}");
+			return 1;
+		}
+	}
+
+	default:
+		return RVAL_DECODED;
+	}
+}
+#endif
+
 SYS_FUNC(userfaultfd)
 {
 	printflags(uffd_flags, tcp->u_arg[0], "UFFD_???");
diff --git a/xlat/uffd_api_flags.in b/xlat/uffd_api_flags.in
new file mode 100644
index 0000000..fd21087
--- /dev/null
+++ b/xlat/uffd_api_flags.in
@@ -0,0 +1,4 @@
+#val_type uint64_t
+1<<_UFFDIO_REGISTER
+1<<_UFFDIO_UNREGISTER
+1<<_UFFDIO_API
diff --git a/xlat/uffd_copy_flags.in b/xlat/uffd_copy_flags.in
new file mode 100644
index 0000000..02d6b19
--- /dev/null
+++ b/xlat/uffd_copy_flags.in
@@ -0,0 +1,2 @@
+#val_type uint64_t
+UFFDIO_COPY_MODE_DONTWAKE
diff --git a/xlat/uffd_register_ioctl_flags.in b/xlat/uffd_register_ioctl_flags.in
new file mode 100644
index 0000000..62615e0
--- /dev/null
+++ b/xlat/uffd_register_ioctl_flags.in
@@ -0,0 +1,5 @@
+#val_type uint64_t
+1<< _UFFDIO_WAKE
+1<< _UFFDIO_COPY
+1<< _UFFDIO_ZEROPAGE
+
diff --git a/xlat/uffd_register_mode_flags.in b/xlat/uffd_register_mode_flags.in
new file mode 100644
index 0000000..996b1f3
--- /dev/null
+++ b/xlat/uffd_register_mode_flags.in
@@ -0,0 +1,3 @@
+#val_type uint64_t
+UFFDIO_REGISTER_MODE_MISSING
+UFFDIO_REGISTER_MODE_WP
diff --git a/xlat/uffd_zeropage_flags.in b/xlat/uffd_zeropage_flags.in
new file mode 100644
index 0000000..111f6df
--- /dev/null
+++ b/xlat/uffd_zeropage_flags.in
@@ -0,0 +1,3 @@
+#val_type uint64_t
+UFFDIO_ZEROPAGE_MODE_DONTWAKE
+
-- 
2.5.5

--
Dr. David Alan Gilbert / dgilbert at redhat.com / Manchester, UK




More information about the Strace-devel mailing list