Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ config RISCV
select HAVE_ARCH_USERFAULTFD_MINOR if 64BIT && USERFAULTFD
select HAVE_ARCH_USERFAULTFD_WP if 64BIT && MMU && USERFAULTFD && RISCV_ISA_SVRSW60T59B
select HAVE_ARCH_VMAP_STACK if MMU && 64BIT
select HAVE_ARCH_WITHIN_STACK_FRAMES
select HAVE_ASM_MODVERSIONS
select HAVE_CONTEXT_TRACKING_USER
select HAVE_DEBUG_KMEMLEAK
Expand Down
68 changes: 68 additions & 0 deletions arch/riscv/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,74 @@ struct thread_info {
void arch_release_task_struct(struct task_struct *tsk);
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);

/*
* RISC-V stack frame layout (with frame pointer enabled).
*
* Reference: RISC-V ELF psABI, Frame Pointer Convention
* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/
* riscv-cc.adoc#frame-pointer-convention
*
* high addr
* +------------------+ <--- fp (s0) points here
* | saved ra | fp - 1*sizeof(void*) (return address)
* | saved fp | fp - 2*sizeof(void*) (previous frame pointer)
* +------------------+
* | local vars |
* | arguments |
* +------------------+ <--- sp
* low addr
*
* The struct stackframe { fp, ra } lives at (fp - sizeof(stackframe)),
* i.e. fp[-2]=saved_fp and fp[-1]=saved_ra.
*
* For usercopy safety, we allow copies within [prev_fp, fp - 2*sizeof(void*))
* for each frame in the chain, where prev_fp is the fp of the previous
* (lower) frame. This covers local variables and arguments but excludes
* the saved ra/fp slots at the top of the frame.
*
* We walk the frame chain starting from __builtin_frame_address(0) (the
* current frame), with prev_fp initialized to current_stack_pointer.
* Using current_stack_pointer -- rather than the 'stack' argument (which is
* the thread's entire stack base) -- ensures that objects in already-returned
* frames (address below current sp) are correctly detected as BAD_STACK,
* because no live frame in the chain will claim that region.
*/
__no_kmsan_checks
static inline int arch_within_stack_frames(const void * const stack,
const void * const stackend,
const void *obj, unsigned long len)
{
#if defined(CONFIG_FRAME_POINTER)
const void *fp = (const void *)__builtin_frame_address(0);
const void *prev_fp = (const void *)current_stack_pointer;

/*
* Walk the frame chain. Each iteration checks whether [obj, obj+len)
* falls within the local-variable area of the current frame:
*
* [prev_fp, fp - 2*sizeof(void*))
*
* i.e. from the base of this frame (sp of this frame, which equals
* the fp of the frame below) up to (but not including) the saved
* fp/ra area at the top of this frame.
*/
while (stack + 2 * sizeof(void *) <= fp && fp < stackend) {
const void *frame_vars_end = (const char *)fp - 2 * sizeof(void *);

if (obj + len <= frame_vars_end) {
if (obj >= prev_fp)
return GOOD_FRAME;
return BAD_STACK;
}
prev_fp = fp;
fp = *(const void * const *)frame_vars_end;
}
return BAD_STACK;
#else
return NOT_STACK;
#endif
}

#endif /* !__ASSEMBLER__ */

/*
Expand Down