uaccess: always export _copy_[from|to]_user with CONFIG_RUST
Rust code needs to be able to access _copy_from_user and _copy_to_user so that it can skip the check_copy_size check in cases where the length is known at compile-time, mirroring the logic for when C code will skip check_copy_size. To do this, we ensure that exported versions of these methods are available when CONFIG_RUST is enabled. Alice has verified that this patch passes the CONFIG_TEST_USER_COPY test on x86 using the Android cuttlefish emulator. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Tested-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Boqun Feng <boqun.feng@gmail.com> Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Acked-by: Andrew Morton <akpm@linux-foundation.org> Link: https://lore.kernel.org/r/20240528-alice-mm-v7-2-78222c31b8f4@google.com Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
1b580e7b9b
commit
1f9a8286bc
@ -5,6 +5,7 @@
|
|||||||
#include <linux/fault-inject-usercopy.h>
|
#include <linux/fault-inject-usercopy.h>
|
||||||
#include <linux/instrumented.h>
|
#include <linux/instrumented.h>
|
||||||
#include <linux/minmax.h>
|
#include <linux/minmax.h>
|
||||||
|
#include <linux/nospec.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/thread_info.h>
|
#include <linux/thread_info.h>
|
||||||
|
|
||||||
@ -138,13 +139,26 @@ __copy_to_user(void __user *to, const void *from, unsigned long n)
|
|||||||
return raw_copy_to_user(to, from, n);
|
return raw_copy_to_user(to, from, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef INLINE_COPY_FROM_USER
|
/*
|
||||||
|
* Architectures that #define INLINE_COPY_TO_USER use this function
|
||||||
|
* directly in the normal copy_to/from_user(), the other ones go
|
||||||
|
* through an extern _copy_to/from_user(), which expands the same code
|
||||||
|
* here.
|
||||||
|
*
|
||||||
|
* Rust code always uses the extern definition.
|
||||||
|
*/
|
||||||
static inline __must_check unsigned long
|
static inline __must_check unsigned long
|
||||||
_copy_from_user(void *to, const void __user *from, unsigned long n)
|
_inline_copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||||
{
|
{
|
||||||
unsigned long res = n;
|
unsigned long res = n;
|
||||||
might_fault();
|
might_fault();
|
||||||
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
|
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
|
||||||
|
/*
|
||||||
|
* Ensure that bad access_ok() speculation will not
|
||||||
|
* lead to nasty side effects *after* the copy is
|
||||||
|
* finished:
|
||||||
|
*/
|
||||||
|
barrier_nospec();
|
||||||
instrument_copy_from_user_before(to, from, n);
|
instrument_copy_from_user_before(to, from, n);
|
||||||
res = raw_copy_from_user(to, from, n);
|
res = raw_copy_from_user(to, from, n);
|
||||||
instrument_copy_from_user_after(to, from, n, res);
|
instrument_copy_from_user_after(to, from, n, res);
|
||||||
@ -153,14 +167,11 @@ _copy_from_user(void *to, const void __user *from, unsigned long n)
|
|||||||
memset(to + (n - res), 0, res);
|
memset(to + (n - res), 0, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
extern __must_check unsigned long
|
extern __must_check unsigned long
|
||||||
_copy_from_user(void *, const void __user *, unsigned long);
|
_copy_from_user(void *, const void __user *, unsigned long);
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef INLINE_COPY_TO_USER
|
|
||||||
static inline __must_check unsigned long
|
static inline __must_check unsigned long
|
||||||
_copy_to_user(void __user *to, const void *from, unsigned long n)
|
_inline_copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||||
{
|
{
|
||||||
might_fault();
|
might_fault();
|
||||||
if (should_fail_usercopy())
|
if (should_fail_usercopy())
|
||||||
@ -171,25 +182,32 @@ _copy_to_user(void __user *to, const void *from, unsigned long n)
|
|||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
extern __must_check unsigned long
|
extern __must_check unsigned long
|
||||||
_copy_to_user(void __user *, const void *, unsigned long);
|
_copy_to_user(void __user *, const void *, unsigned long);
|
||||||
#endif
|
|
||||||
|
|
||||||
static __always_inline unsigned long __must_check
|
static __always_inline unsigned long __must_check
|
||||||
copy_from_user(void *to, const void __user *from, unsigned long n)
|
copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||||
{
|
{
|
||||||
if (check_copy_size(to, n, false))
|
if (!check_copy_size(to, n, false))
|
||||||
n = _copy_from_user(to, from, n);
|
return n;
|
||||||
return n;
|
#ifdef INLINE_COPY_FROM_USER
|
||||||
|
return _inline_copy_from_user(to, from, n);
|
||||||
|
#else
|
||||||
|
return _copy_from_user(to, from, n);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline unsigned long __must_check
|
static __always_inline unsigned long __must_check
|
||||||
copy_to_user(void __user *to, const void *from, unsigned long n)
|
copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||||
{
|
{
|
||||||
if (check_copy_size(from, n, true))
|
if (!check_copy_size(from, n, true))
|
||||||
n = _copy_to_user(to, from, n);
|
return n;
|
||||||
return n;
|
|
||||||
|
#ifdef INLINE_COPY_TO_USER
|
||||||
|
return _inline_copy_to_user(to, from, n);
|
||||||
|
#else
|
||||||
|
return _copy_to_user(to, from, n);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef copy_mc_to_kernel
|
#ifndef copy_mc_to_kernel
|
||||||
|
@ -12,40 +12,18 @@
|
|||||||
|
|
||||||
/* out-of-line parts */
|
/* out-of-line parts */
|
||||||
|
|
||||||
#ifndef INLINE_COPY_FROM_USER
|
#if !defined(INLINE_COPY_FROM_USER) || defined(CONFIG_RUST)
|
||||||
unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
|
unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||||
{
|
{
|
||||||
unsigned long res = n;
|
return _inline_copy_from_user(to, from, n);
|
||||||
might_fault();
|
|
||||||
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
|
|
||||||
/*
|
|
||||||
* Ensure that bad access_ok() speculation will not
|
|
||||||
* lead to nasty side effects *after* the copy is
|
|
||||||
* finished:
|
|
||||||
*/
|
|
||||||
barrier_nospec();
|
|
||||||
instrument_copy_from_user_before(to, from, n);
|
|
||||||
res = raw_copy_from_user(to, from, n);
|
|
||||||
instrument_copy_from_user_after(to, from, n, res);
|
|
||||||
}
|
|
||||||
if (unlikely(res))
|
|
||||||
memset(to + (n - res), 0, res);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(_copy_from_user);
|
EXPORT_SYMBOL(_copy_from_user);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef INLINE_COPY_TO_USER
|
#if !defined(INLINE_COPY_TO_USER) || defined(CONFIG_RUST)
|
||||||
unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
|
unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||||
{
|
{
|
||||||
might_fault();
|
return _inline_copy_to_user(to, from, n);
|
||||||
if (should_fail_usercopy())
|
|
||||||
return n;
|
|
||||||
if (likely(access_ok(to, n))) {
|
|
||||||
instrument_copy_to_user(to, from, n);
|
|
||||||
n = raw_copy_to_user(to, from, n);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(_copy_to_user);
|
EXPORT_SYMBOL(_copy_to_user);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user