RFC userfaultfd ioctl decode

Gabriel Laskar gabriel at lse.epita.fr
Thu Apr 21 16:27:13 UTC 2016


On Thu, 21 Apr 2016 14:04:33 +0100
"Dr. David Alan Gilbert" <dgilbert at redhat.com> wrote:

> Hi,
>    Please find below a decoder for the UFFDIO_COPY ioctl
> on the userfaultfd fd;  but there are a few other ioctls
> in the set to decode and I thought I'd ask before doing those:
> 
>   1) Is it basically on the right lines;  I'm not that confident
>      I understand the return flags on the ioctl decoder function.

The return flags are the same as for the syscalls in general, it is
written in defs.h around line 378. It allows to type the return value
if it is not an int.

For the ioctl decoding function, on entering there is:

* 0 if nothing has been decoded, (and it will be probably done on
  exiting())
* 1 parameter have been parsed, and printed
* RVAL_DECODED if there is nothing else to do.
* 1 | RVAL_DECODED : parameter have been parsed, but there is some kind
  of error/nothing has been printed, so this allow the ioctl decoder to
  write a default value.

and on exiting, there is only 0 or 1.

>   2) Are there any hooks for decoding data read off an fd?  The
> userfaultfd returns structures giving address of the fault etc.

For the moment, there is no way to hook the read/write decoders in
order to decode the buffers.

> 
> Dave
> 
> 
> >From a0f7d0e63a6d33a98d336b588830cd2dada649fc 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_COPY ioctl
> 
> UFFDIO_COPY is one of the ioctls on the fd returned by userfaultfd.
> Note that it reads from and also returns a 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.
> ---
>  configure.ac  |  3 +++
>  defs.h        |  1 +
>  ioctl.c       |  2 ++
>  userfaultfd.c | 41 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 47 insertions(+)
> 
> diff --git a/configure.ac b/configure.ac
> index 2b29c94..cb9b494 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -420,6 +420,9 @@ AC_CHECK_HEADERS([linux/input.h], [
>  	AC_CHECK_MEMBERS([struct input_absinfo.resolution],,,
> [#include <linux/input.h>]) ])
>  
> +AC_CHECK_HEADERS([linux/userfaultfd.h], [
> +])

If there is no custom behavior to have for AC_CHECK_HEADERS, you can
add the header to the global list.

> +
>  AC_CHECK_HEADERS([linux/bpf.h], [
>  	AC_CACHE_CHECK([whether union bpf_attr.log_buf
> initialization works], [st_cv_have_union_bpf_attr_log_buf],
> 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..464329b 100644
> --- a/ioctl.c
> +++ b/ioctl.c
> @@ -263,6 +263,8 @@ ioctl_decode(struct tcb *tcp)
>  	case 'E':
>  		return evdev_ioctl(tcp, code, arg);
>  #endif
> +	case 0xaa:
> +		return uffdio_ioctl(tcp, code, arg);

for most of the ioctl code, we prefer to have the #ifdef HEADER around
the case instead of inside the function, but this is mostly cosmetic
and for consistence.

>  	default:
>  		break;
>  	}
> diff --git a/userfaultfd.c b/userfaultfd.c
> index 15f825a..2a06e1f 100644
> --- a/userfaultfd.c
> +++ b/userfaultfd.c
> @@ -27,9 +27,50 @@
>  
>  #include "defs.h"
>  #include <fcntl.h>
> +#include <linux/ioctl.h>
> +#ifdef HAVE_LINUX_USERFAULTFD_H
> +#include <linux/userfaultfd.h>
> +#endif
>  
>  #include "xlat/uffd_flags.h"
>  
> +
> +int
> +uffdio_ioctl(struct tcb *tcp, const unsigned int code, const long
> arg) +{
> +#ifdef HAVE_LINUX_USERFAULTFD_H
> +	static char auxbuf[64];
> +#endif
> +
> +	switch (code) {
> +#ifdef HAVE_LINUX_USERFAULTFD_H
> +	case UFFDIO_COPY: {

before that, you need to check 

> +		struct uffdio_copy uc;
> +		if (!umove_or_printaddr(tcp, arg, &uc)) {
> +			if (entering(tcp)) {
> +				tprintf("{dst=%" PRIx64 ", src=%"
> PRIx64
> +				", len=%" PRIu64 ", mode=%" PRIu64
> "}",
> +				(uint64_t)uc.dst, (uint64_t)uc.src,
> +				(uint64_t)uc.len, (uint64_t)uc.mode);
> +				return 1;
> +			} else {
> +				/* copy also returns the number of
> bytes
> +				 * copied */
> +				sprintf(auxbuf, "copy=%" PRId64,
> +				(int64_t)uc.copy);
> +				tcp->auxstr = auxbuf;
> +				return RVAL_STR | 1;

There is no need to do anything on exit. The return value as an int is
fine.

> +			}
> +		}
> +		break;
> +	}
> +#endif
> +
> +	default:
> +		return 0;
> +	}
> +}
> +
>  SYS_FUNC(userfaultfd)
>  {
>  	printflags(uffd_flags, tcp->u_arg[0], "UFFD_???");



-- 
Gabriel Laskar




More information about the Strace-devel mailing list