Skip to content

Commit 94902d8

Browse files
mrutland-armwilldeacon
authored andcommitted
arm64: uaccess: avoid blocking within critical sections
As Vincent reports in: https://lore.kernel.org/r/[email protected] The put_user() in schedule_tail() can get stuck in a livelock, similar to a problem recently fixed on riscv in commit: 285a76b ("riscv: evaluate put_user() arg before enabling user access") In __raw_put_user() we have a critical section between uaccess_ttbr0_enable() and uaccess_ttbr0_disable() where we cannot safely call into the scheduler without having taken an exception, as schedule() and other scheduling functions will not save/restore the TTBR0 state. If either of the `x` or `ptr` arguments to __raw_put_user() contain a blocking call, we may call into the scheduler within the critical section. This can result in two problems: 1) The access within the critical section will occur without the required TTBR0 tables installed. This will fault, and where the required tables permit access, the access will be retried without the required tables, resulting in a livelock. 2) When TTBR0 SW PAN is in use, check_and_switch_context() does not modify TTBR0, leaving a stale value installed. The mappings of the blocked task will erroneously be accessible to regular accesses in the context of the new task. Additionally, if the tables are subsequently freed, local TLB maintenance required to reuse the ASID may be lost, potentially resulting in TLB corruption (e.g. in the presence of CnP). The same issue exists for __raw_get_user() in the critical section between uaccess_ttbr0_enable() and uaccess_ttbr0_disable(). A similar issue exists for __get_kernel_nofault() and __put_kernel_nofault() for the critical section between __uaccess_enable_tco_async() and __uaccess_disable_tco_async(), as the TCO state is not context-switched by direct calls into the scheduler. Here the TCO state may be lost from the context of the current task, resulting in unexpected asynchronous tag check faults. It may also be leaked to another task, suppressing expected tag check faults. To fix all of these cases, we must ensure that we do not directly call into the scheduler in their respective critical sections. This patch reworks __raw_put_user(), __raw_get_user(), __get_kernel_nofault(), and __put_kernel_nofault(), ensuring that parameters are evaluated outside of the critical sections. To make this requirement clear, comments are added describing the problem, and line spaces added to separate the critical sections from other portions of the macros. For __raw_get_user() and __raw_put_user() the `err` parameter is conditionally assigned to, and we must currently evaluate this in the critical section. This behaviour is relied upon by the signal code, which uses chains of put_user_error() and get_user_error(), checking the return value at the end. In all cases, the `err` parameter is a plain int rather than a more complex expression with a blocking call, so this is safe. In future we should try to clean up the `err` usage to remove the potential for this to be a problem. Aside from the changes to time of evaluation, there should be no functional change as a result of this patch. Reported-by: Vincent Whitchurch <[email protected]> Link: https://lore.kernel.org/r/[email protected] Fixes: f253d82 ("arm64: uaccess: refactor __{get,put}_user") Signed-off-by: Mark Rutland <[email protected]> Cc: Will Deacon <[email protected]> Cc: Catalin Marinas <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent d3eb70e commit 94902d8

File tree

1 file changed

+41
-7
lines changed

1 file changed

+41
-7
lines changed

arch/arm64/include/asm/uaccess.h

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,22 @@ do { \
281281
(x) = (__force __typeof__(*(ptr)))__gu_val; \
282282
} while (0)
283283

284+
/*
285+
* We must not call into the scheduler between uaccess_ttbr0_enable() and
286+
* uaccess_ttbr0_disable(). As `x` and `ptr` could contain blocking functions,
287+
* we must evaluate these outside of the critical section.
288+
*/
284289
#define __raw_get_user(x, ptr, err) \
285290
do { \
291+
__typeof__(*(ptr)) __user *__rgu_ptr = (ptr); \
292+
__typeof__(x) __rgu_val; \
286293
__chk_user_ptr(ptr); \
294+
\
287295
uaccess_ttbr0_enable(); \
288-
__raw_get_mem("ldtr", x, ptr, err); \
296+
__raw_get_mem("ldtr", __rgu_val, __rgu_ptr, err); \
289297
uaccess_ttbr0_disable(); \
298+
\
299+
(x) = __rgu_val; \
290300
} while (0)
291301

292302
#define __get_user_error(x, ptr, err) \
@@ -310,14 +320,22 @@ do { \
310320

311321
#define get_user __get_user
312322

323+
/*
324+
* We must not call into the scheduler between __uaccess_enable_tco_async() and
325+
* __uaccess_disable_tco_async(). As `dst` and `src` may contain blocking
326+
* functions, we must evaluate these outside of the critical section.
327+
*/
313328
#define __get_kernel_nofault(dst, src, type, err_label) \
314329
do { \
330+
__typeof__(dst) __gkn_dst = (dst); \
331+
__typeof__(src) __gkn_src = (src); \
315332
int __gkn_err = 0; \
316333
\
317334
__uaccess_enable_tco_async(); \
318-
__raw_get_mem("ldr", *((type *)(dst)), \
319-
(__force type *)(src), __gkn_err); \
335+
__raw_get_mem("ldr", *((type *)(__gkn_dst)), \
336+
(__force type *)(__gkn_src), __gkn_err); \
320337
__uaccess_disable_tco_async(); \
338+
\
321339
if (unlikely(__gkn_err)) \
322340
goto err_label; \
323341
} while (0)
@@ -351,11 +369,19 @@ do { \
351369
} \
352370
} while (0)
353371

372+
/*
373+
* We must not call into the scheduler between uaccess_ttbr0_enable() and
374+
* uaccess_ttbr0_disable(). As `x` and `ptr` could contain blocking functions,
375+
* we must evaluate these outside of the critical section.
376+
*/
354377
#define __raw_put_user(x, ptr, err) \
355378
do { \
356-
__chk_user_ptr(ptr); \
379+
__typeof__(*(ptr)) __user *__rpu_ptr = (ptr); \
380+
__typeof__(*(ptr)) __rpu_val = (x); \
381+
__chk_user_ptr(__rpu_ptr); \
382+
\
357383
uaccess_ttbr0_enable(); \
358-
__raw_put_mem("sttr", x, ptr, err); \
384+
__raw_put_mem("sttr", __rpu_val, __rpu_ptr, err); \
359385
uaccess_ttbr0_disable(); \
360386
} while (0)
361387

@@ -380,14 +406,22 @@ do { \
380406

381407
#define put_user __put_user
382408

409+
/*
410+
* We must not call into the scheduler between __uaccess_enable_tco_async() and
411+
* __uaccess_disable_tco_async(). As `dst` and `src` may contain blocking
412+
* functions, we must evaluate these outside of the critical section.
413+
*/
383414
#define __put_kernel_nofault(dst, src, type, err_label) \
384415
do { \
416+
__typeof__(dst) __pkn_dst = (dst); \
417+
__typeof__(src) __pkn_src = (src); \
385418
int __pkn_err = 0; \
386419
\
387420
__uaccess_enable_tco_async(); \
388-
__raw_put_mem("str", *((type *)(src)), \
389-
(__force type *)(dst), __pkn_err); \
421+
__raw_put_mem("str", *((type *)(__pkn_src)), \
422+
(__force type *)(__pkn_dst), __pkn_err); \
390423
__uaccess_disable_tco_async(); \
424+
\
391425
if (unlikely(__pkn_err)) \
392426
goto err_label; \
393427
} while(0)

0 commit comments

Comments
 (0)