Skip to content

Commit d344a6b

Browse files
dcpleungnashif
authored andcommitted
xtensa: make arch_user_string_nlen actually work
arch_user_string_nlen() did not exactly work correctly as any invalid pointers being passed are de-referenced naively, which results in DTLB misses (MMU) or access errors (MPU). However, arch_user_string_nlen() should always return to the caller with appropriate error code set, and should never result in thread termination. Since we are usually going through syscalls when arch_user_string_nlen() is called, for MMU, the DTLB miss goes through double exception. Since the pointer is invalid, there is a high chance there is not even a L2 page table associated with that bad address. So the DTLB miss cannot be handled and it just keeps looping in double exception until there is another exception type where we get to the C handler. However, the stack frame is no longer the frame associated with the call to arch_user_string_nlen(), and the call return address would be incorrect. Forcing this incorrect address as the next PC would result in some other exceptions, e.g. illegal instruction, which would go to the C handler again. This time it will go to the end of handler and would result in thread termination. For MPU systems, access errors would simply result in thread terminal in the C handler. Because of these reasons, change the arch_user_string_nlen() to check if the memory region can be accessed under kernel mode first before feeding it to strnlen(). Also remove the exception fixup arrays as there is nothing there anymore. Signed-off-by: Daniel Leung <[email protected]>
1 parent 79939e3 commit d344a6b

File tree

3 files changed

+43
-68
lines changed

3 files changed

+43
-68
lines changed

arch/xtensa/core/syscall_helper.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
#include <string.h>
8+
79
#include <zephyr/arch/xtensa/syscall.h>
810

11+
#include <zephyr/internal/syscall_handler.h>
12+
#include <xtensa_internal.h>
13+
914
#ifdef CONFIG_XTENSA_SYSCALL_USE_HELPER
1015
uintptr_t xtensa_syscall_helper(uintptr_t arg1, uintptr_t arg2,
1116
uintptr_t arg3, uintptr_t arg4,
@@ -47,3 +52,41 @@ bool xtensa_is_user_context(void)
4752
return ret != 0;
4853
}
4954
#endif /* XCHAL_HAVE_THREADPTR */
55+
56+
size_t arch_user_string_nlen(const char *s, size_t maxsize, int *err_arg)
57+
{
58+
/* Check if we can actually read the whole length.
59+
*
60+
* arch_user_string_nlen() is supposed to naively go through
61+
* the string passed from user thread, and relies on page faults
62+
* to catch inaccessible strings, such that user thread can pass
63+
* a string that is shorter than the max length this function
64+
* caller expects. So at least we want to make sure kernel has
65+
* access to the whole length, aka. memory being mapped.
66+
* Note that arch_user_string_nlen() should never result in
67+
* thread termination due to page faults, and must always
68+
* return to the caller with err_arg set or cleared.
69+
* For MMU systems, unmapped memory will result in a DTLB miss
70+
* and that might trigger an infinite DTLB miss storm if
71+
* the corresponding L2 page table never exists in the first
72+
* place (which would result in DTLB misses through L1 page
73+
* table), until some other exceptions occur to break
74+
* the cycle.
75+
* For MPU systems, this would simply results in access errors
76+
* and the exception handler will terminate the thread.
77+
*/
78+
if (!xtensa_mem_kernel_has_access((void *)s, maxsize, 0)) {
79+
/*
80+
* API says we need to set err_arg to -1 if there are
81+
* any errors.
82+
*/
83+
*err_arg = -1;
84+
85+
return 0;
86+
}
87+
88+
/* No error and we can proceed to getting the string length. */
89+
*err_arg = 0;
90+
91+
return strnlen(s, maxsize);
92+
}

arch/xtensa/core/userspace.S

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -352,45 +352,3 @@ xtensa_userspace_enter:
352352
movi a0, 0
353353

354354
rfi 2
355-
356-
/*
357-
* size_t arch_user_string_nlen(const char *s, size_t maxsize, int *err_arg)
358-
*/
359-
.global arch_user_string_nlen
360-
.type arch_user_string_nlen, @function
361-
.align 4
362-
arch_user_string_nlen:
363-
entry a1, 32
364-
365-
/* error value, set to -1. */
366-
movi a5, -1
367-
s32i a5, a4, 0
368-
369-
/* length count */
370-
xor a5, a5, a5
371-
372-
/* This code might page fault */
373-
strlen_loop:
374-
.global xtensa_user_string_nlen_fault_start
375-
xtensa_user_string_nlen_fault_start:
376-
l8ui a6, a2, 0 /* Current char */
377-
378-
.global xtensa_user_string_nlen_fault_end
379-
xtensa_user_string_nlen_fault_end:
380-
beqz a6, strlen_done
381-
addi a5, a5, 1
382-
addi a2, a2, 1
383-
beq a5, a3, strlen_done
384-
j strlen_loop
385-
386-
strlen_done:
387-
/* Set return value */
388-
mov a2, a5
389-
390-
/* Set error value to 0 since we succeeded */
391-
movi a5, 0x0
392-
s32i a5, a4, 0
393-
394-
.global xtensa_user_string_nlen_fixup
395-
xtensa_user_string_nlen_fixup:
396-
retw

arch/xtensa/core/vector_handlers.c

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,6 @@ LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
2929
extern char xtensa_arch_except_epc[];
3030
extern char xtensa_arch_kernel_oops_epc[];
3131

32-
#ifdef CONFIG_USERSPACE
33-
Z_EXC_DECLARE(xtensa_user_string_nlen);
34-
35-
static const struct z_exc_handle exceptions[] = {
36-
Z_EXC_HANDLE(xtensa_user_string_nlen)
37-
};
38-
#endif /* CONFIG_USERSPACE */
39-
4032
void xtensa_dump_stack(const void *stack)
4133
{
4234
_xtensa_irq_stack_frame_raw_t *frame = (void *)stack;
@@ -265,21 +257,6 @@ void *xtensa_excint1_c(void *esf)
265257
ps = bsa->ps;
266258
pc = (void *)bsa->pc;
267259

268-
#ifdef CONFIG_USERSPACE
269-
/* If the faulting address is from one of the known
270-
* exceptions that should not be fatal, return to
271-
* the fixup address.
272-
*/
273-
for (int i = 0; i < ARRAY_SIZE(exceptions); i++) {
274-
if ((pc >= exceptions[i].start) &&
275-
(pc < exceptions[i].end)) {
276-
bsa->pc = (uintptr_t)exceptions[i].fixup;
277-
278-
goto fixup_out;
279-
}
280-
}
281-
#endif /* CONFIG_USERSPACE */
282-
283260
/* Default for exception */
284261
int reason = K_ERR_CPU_EXCEPTION;
285262
is_fatal_error = true;
@@ -371,9 +348,6 @@ void *xtensa_excint1_c(void *esf)
371348
_current_cpu->nested = 1;
372349
}
373350

374-
#ifdef CONFIG_USERSPACE
375-
fixup_out:
376-
#endif
377351
#if defined(CONFIG_XTENSA_MMU)
378352
if (is_dblexc) {
379353
XTENSA_WSR(ZSR_DEPC_SAVE_STR, 0);

0 commit comments

Comments
 (0)