Skip to content

Commit 1f9a828

Browse files
arndbojeda
authored andcommitted
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 <[email protected]> Tested-by: Alice Ryhl <[email protected]> Reviewed-by: Boqun Feng <[email protected]> Reviewed-by: Kees Cook <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Acked-by: Andrew Morton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 1b580e7 commit 1f9a828

File tree

2 files changed

+36
-40
lines changed

2 files changed

+36
-40
lines changed

include/linux/uaccess.h

+32-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <linux/fault-inject-usercopy.h>
66
#include <linux/instrumented.h>
77
#include <linux/minmax.h>
8+
#include <linux/nospec.h>
89
#include <linux/sched.h>
910
#include <linux/thread_info.h>
1011

@@ -138,13 +139,26 @@ __copy_to_user(void __user *to, const void *from, unsigned long n)
138139
return raw_copy_to_user(to, from, n);
139140
}
140141

141-
#ifdef INLINE_COPY_FROM_USER
142+
/*
143+
* Architectures that #define INLINE_COPY_TO_USER use this function
144+
* directly in the normal copy_to/from_user(), the other ones go
145+
* through an extern _copy_to/from_user(), which expands the same code
146+
* here.
147+
*
148+
* Rust code always uses the extern definition.
149+
*/
142150
static inline __must_check unsigned long
143-
_copy_from_user(void *to, const void __user *from, unsigned long n)
151+
_inline_copy_from_user(void *to, const void __user *from, unsigned long n)
144152
{
145153
unsigned long res = n;
146154
might_fault();
147155
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
156+
/*
157+
* Ensure that bad access_ok() speculation will not
158+
* lead to nasty side effects *after* the copy is
159+
* finished:
160+
*/
161+
barrier_nospec();
148162
instrument_copy_from_user_before(to, from, n);
149163
res = raw_copy_from_user(to, from, n);
150164
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)
153167
memset(to + (n - res), 0, res);
154168
return res;
155169
}
156-
#else
157170
extern __must_check unsigned long
158171
_copy_from_user(void *, const void __user *, unsigned long);
159-
#endif
160172

161-
#ifdef INLINE_COPY_TO_USER
162173
static inline __must_check unsigned long
163-
_copy_to_user(void __user *to, const void *from, unsigned long n)
174+
_inline_copy_to_user(void __user *to, const void *from, unsigned long n)
164175
{
165176
might_fault();
166177
if (should_fail_usercopy())
@@ -171,25 +182,32 @@ _copy_to_user(void __user *to, const void *from, unsigned long n)
171182
}
172183
return n;
173184
}
174-
#else
175185
extern __must_check unsigned long
176186
_copy_to_user(void __user *, const void *, unsigned long);
177-
#endif
178187

179188
static __always_inline unsigned long __must_check
180189
copy_from_user(void *to, const void __user *from, unsigned long n)
181190
{
182-
if (check_copy_size(to, n, false))
183-
n = _copy_from_user(to, from, n);
184-
return n;
191+
if (!check_copy_size(to, n, false))
192+
return n;
193+
#ifdef INLINE_COPY_FROM_USER
194+
return _inline_copy_from_user(to, from, n);
195+
#else
196+
return _copy_from_user(to, from, n);
197+
#endif
185198
}
186199

187200
static __always_inline unsigned long __must_check
188201
copy_to_user(void __user *to, const void *from, unsigned long n)
189202
{
190-
if (check_copy_size(from, n, true))
191-
n = _copy_to_user(to, from, n);
192-
return n;
203+
if (!check_copy_size(from, n, true))
204+
return n;
205+
206+
#ifdef INLINE_COPY_TO_USER
207+
return _inline_copy_to_user(to, from, n);
208+
#else
209+
return _copy_to_user(to, from, n);
210+
#endif
193211
}
194212

195213
#ifndef copy_mc_to_kernel

lib/usercopy.c

+4-26
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,18 @@
1212

1313
/* out-of-line parts */
1414

15-
#ifndef INLINE_COPY_FROM_USER
15+
#if !defined(INLINE_COPY_FROM_USER) || defined(CONFIG_RUST)
1616
unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
1717
{
18-
unsigned long res = n;
19-
might_fault();
20-
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
21-
/*
22-
* Ensure that bad access_ok() speculation will not
23-
* lead to nasty side effects *after* the copy is
24-
* finished:
25-
*/
26-
barrier_nospec();
27-
instrument_copy_from_user_before(to, from, n);
28-
res = raw_copy_from_user(to, from, n);
29-
instrument_copy_from_user_after(to, from, n, res);
30-
}
31-
if (unlikely(res))
32-
memset(to + (n - res), 0, res);
33-
return res;
18+
return _inline_copy_from_user(to, from, n);
3419
}
3520
EXPORT_SYMBOL(_copy_from_user);
3621
#endif
3722

38-
#ifndef INLINE_COPY_TO_USER
23+
#if !defined(INLINE_COPY_TO_USER) || defined(CONFIG_RUST)
3924
unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
4025
{
41-
might_fault();
42-
if (should_fail_usercopy())
43-
return n;
44-
if (likely(access_ok(to, n))) {
45-
instrument_copy_to_user(to, from, n);
46-
n = raw_copy_to_user(to, from, n);
47-
}
48-
return n;
26+
return _inline_copy_to_user(to, from, n);
4927
}
5028
EXPORT_SYMBOL(_copy_to_user);
5129
#endif

0 commit comments

Comments
 (0)