lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 21 Aug 2020 13:46:46 +0200
From:   Dmitry Vyukov <dvyukov@...gle.com>
To:     albert.linde@...il.com
Cc:     Andrew Morton <akpm@...ux-foundation.org>,
        Borislav Petkov <bp@...en8.de>, Ingo Molnar <mingo@...hat.com>,
        Jonathan Corbet <corbet@....net>,
        Thomas Gleixner <tglx@...utronix.de>,
        Arnd Bergmann <arnd@...db.de>,
        Akinobu Mita <akinobu.mita@...il.com>,
        "H. Peter Anvin" <hpa@...or.com>,
        Al Viro <viro@...iv.linux.org.uk>,
        Alexander Potapenko <glider@...gle.com>,
        Andrey Konovalov <andreyknvl@...gle.com>,
        Marco Elver <elver@...gle.com>,
        "open list:DOCUMENTATION" <linux-doc@...r.kernel.org>,
        LKML <linux-kernel@...r.kernel.org>,
        linux-arch <linux-arch@...r.kernel.org>,
        "the arch/x86 maintainers" <x86@...nel.org>,
        Albert van der Linde <alinde@...gle.com>
Subject: Re: [PATCH 2/3] lib, uaccess: add failure injection to usercopy functions

On Fri, Aug 21, 2020 at 12:50 PM <albert.linde@...il.com> wrote:
>
> From: Albert van der Linde <alinde@...gle.com>
>
> To test fault-tolerance of usercopy accesses, introduce fault injection
> in usercopy functions.
>
> Adds failure injection to usercopy functions. If a failure is expected
> we return either the failure or the total amount of bytes. As many
> usercopy functions can fail partially, permit also partial failures:
> e.g., copy_from_user can fail copying between 0 and n.
>
> Signed-off-by: Albert van der Linde <alinde@...gle.com>
> ---
>  include/linux/uaccess.h | 31 ++++++++++++++++++++++++++-----
>  lib/iov_iter.c          | 20 +++++++++++++++++---
>  lib/strncpy_from_user.c |  3 +++
>  lib/usercopy.c          | 13 +++++++++++--
>  4 files changed, 57 insertions(+), 10 deletions(-)
>
> diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
> index 94b285411659..79e736077ef4 100644
> --- a/include/linux/uaccess.h
> +++ b/include/linux/uaccess.h
> @@ -2,6 +2,7 @@
>  #ifndef __LINUX_UACCESS_H__
>  #define __LINUX_UACCESS_H__
>
> +#include <linux/fault-inject-usercopy.h>
>  #include <linux/instrumented.h>
>  #include <linux/sched.h>
>  #include <linux/thread_info.h>
> @@ -82,10 +83,14 @@ __copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
>  static __always_inline __must_check unsigned long
>  __copy_from_user(void *to, const void __user *from, unsigned long n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;

It feels that this "if (not_copied < 0)" part should be factored into
some common place because it's duplicated lots of times.
Maybe we need a separate helper similar to should_fail_usercopy that
does not return negative values; or make should_fail_usercopy accept a
flag to return -EFAULT or n; or maybe we simply make
should_fail_usercopy return n instead of -EFAULT?

Also, why do we continue when we already know that we should fail it?
In some places we don't, but in most we still run the actual
raw_copy_from_user. What's the rationale? Can we return early in all
cases?

>         might_fault();
>         instrument_copy_from_user(to, from, n);
>         check_object_size(to, n, false);
> -       return raw_copy_from_user(to, from, n);
> +       return not_copied + raw_copy_from_user(to, from, n - not_copied);

Is returning "not_copied + raw_copy_from_user" fine in all cases?
We can return more than the number of bytes we were asked to copy,
which may be surprising for some callers. Or we can both return an
error and do the copy, which may be surprising for userspace.

>  }
>
>  /**
> @@ -104,18 +109,26 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)
>  static __always_inline __must_check unsigned long
>  __copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;
>         instrument_copy_to_user(to, from, n);
>         check_object_size(from, n, true);
> -       return raw_copy_to_user(to, from, n);
> +       return not_copied + raw_copy_to_user(to, from, n - not_copied);
>  }
>
>  static __always_inline __must_check unsigned long
>  __copy_to_user(void __user *to, const void *from, unsigned long n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;
>         might_fault();
>         instrument_copy_to_user(to, from, n);
>         check_object_size(from, n, true);
> -       return raw_copy_to_user(to, from, n);
> +       return not_copied + raw_copy_to_user(to, from, n - not_copied);
>  }
>
>  #ifdef INLINE_COPY_FROM_USER
> @@ -123,10 +136,14 @@ static inline __must_check unsigned long
>  _copy_from_user(void *to, const void __user *from, unsigned long n)
>  {
>         unsigned long res = n;
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;
>         might_fault();
>         if (likely(access_ok(from, n))) {
>                 instrument_copy_from_user(to, from, n);
> -               res = raw_copy_from_user(to, from, n);
> +               res = not_copied + raw_copy_from_user(to, from, n - not_copied);
>         }
>         if (unlikely(res))
>                 memset(to + (n - res), 0, res);
> @@ -141,10 +158,14 @@ _copy_from_user(void *, const void __user *, unsigned long);
>  static inline __must_check unsigned long
>  _copy_to_user(void __user *to, const void *from, unsigned long n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;
>         might_fault();
>         if (access_ok(to, n)) {
>                 instrument_copy_to_user(to, from, n);
> -               n = raw_copy_to_user(to, from, n);
> +               n = not_copied + raw_copy_to_user(to, from, n - not_copied);
>         }
>         return n;
>  }
> diff --git a/lib/iov_iter.c b/lib/iov_iter.c
> index 5e40786c8f12..c55c9bff9b0c 100644
> --- a/lib/iov_iter.c
> +++ b/lib/iov_iter.c
> @@ -2,6 +2,7 @@
>  #include <crypto/hash.h>
>  #include <linux/export.h>
>  #include <linux/bvec.h>
> +#include <linux/fault-inject-usercopy.h>
>  #include <linux/uio.h>
>  #include <linux/pagemap.h>
>  #include <linux/slab.h>
> @@ -139,18 +140,26 @@
>
>  static int copyout(void __user *to, const void *from, size_t n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               return not_copied;
>         if (access_ok(to, n)) {
>                 instrument_copy_to_user(to, from, n);
> -               n = raw_copy_to_user(to, from, n);
> +               n = not_copied + raw_copy_to_user(to, from, n - not_copied);
>         }
>         return n;
>  }
>
>  static int copyin(void *to, const void __user *from, size_t n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               return not_copied;
>         if (access_ok(from, n)) {
>                 instrument_copy_from_user(to, from, n);
> -               n = raw_copy_from_user(to, from, n);
> +               n = not_copied + raw_copy_from_user(to, from, n - not_copied);
>         }
>         return n;
>  }
> @@ -640,9 +649,14 @@ EXPORT_SYMBOL(_copy_to_iter);
>  #ifdef CONFIG_ARCH_HAS_UACCESS_MCSAFE
>  static int copyout_mcsafe(void __user *to, const void *from, size_t n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               return not_copied;
>         if (access_ok(to, n)) {
>                 instrument_copy_to_user(to, from, n);
> -               n = copy_to_user_mcsafe((__force void *) to, from, n);
> +               n = not_copied + copy_to_user_mcsafe((__force void *) to,
> +                              from, n - not_copied);
>         }
>         return n;
>  }
> diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
> index 34696a348864..f65973007931 100644
> --- a/lib/strncpy_from_user.c
> +++ b/lib/strncpy_from_user.c
> @@ -1,6 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <linux/compiler.h>
>  #include <linux/export.h>
> +#include <linux/fault-inject-usercopy.h>
>  #include <linux/kasan-checks.h>
>  #include <linux/thread_info.h>
>  #include <linux/uaccess.h>
> @@ -101,6 +102,8 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
>         might_fault();
>         if (unlikely(count <= 0))
>                 return 0;
> +       if (should_fail_usercopy(count))
> +               return -EFAULT;
>
>         max_addr = user_addr_max();
>         src_addr = (unsigned long)untagged_addr(src);
> diff --git a/lib/usercopy.c b/lib/usercopy.c
> index b26509f112f9..e79716b4634b 100644
> --- a/lib/usercopy.c
> +++ b/lib/usercopy.c
> @@ -1,5 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <linux/bitops.h>
> +#include <linux/fault-inject-usercopy.h>
>  #include <linux/instrumented.h>
>  #include <linux/uaccess.h>
>
> @@ -9,10 +10,14 @@
>  unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
>  {
>         unsigned long res = n;
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;
>         might_fault();
>         if (likely(access_ok(from, n))) {
>                 instrument_copy_from_user(to, from, n);
> -               res = raw_copy_from_user(to, from, n);
> +               res = not_copied + raw_copy_from_user(to, from, n - not_copied);
>         }
>         if (unlikely(res))
>                 memset(to + (n - res), 0, res);
> @@ -24,10 +29,14 @@ EXPORT_SYMBOL(_copy_from_user);
>  #ifndef INLINE_COPY_TO_USER
>  unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
>  {
> +       long not_copied = should_fail_usercopy(n);
> +
> +       if (not_copied < 0)
> +               not_copied = n;
>         might_fault();
>         if (likely(access_ok(to, n))) {
>                 instrument_copy_to_user(to, from, n);
> -               n = raw_copy_to_user(to, from, n);
> +               n = not_copied + raw_copy_to_user(to, from, n - not_copied);
>         }
>         return n;
>  }
> --
> 2.28.0.297.g1956fa8f8d-goog
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ