arch/riscv/kernel/usercfi.c

Source file repositories/reference/linux-study-clean/arch/riscv/kernel/usercfi.c

File Facts

System
Linux kernel
Corpus path
arch/riscv/kernel/usercfi.c
Extension
.c
Size
14788 bytes
Lines
536
Domain
Architecture Layer
Bucket
arch/riscv
Inferred role
Architecture Layer: syscall or user/kernel boundary
Status
core implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

SYSCALL_DEFINE3(map_shadow_stack, unsigned long, addr, unsigned long, size, unsigned int, flags)
{
	bool set_tok = flags & SHADOW_STACK_SET_TOKEN;
	unsigned long aligned_size = 0;

	if (!is_user_shstk_enabled())
		return -EOPNOTSUPP;

	/* Anything other than set token should result in invalid param */
	if (flags & ~SHADOW_STACK_SET_TOKEN)
		return -EINVAL;

	/*
	 * Unlike other architectures, on RISC-V, SSP pointer is held in CSR_SSP and is an available
	 * CSR in all modes. CSR accesses are performed using 12bit index programmed in instruction
	 * itself. This provides static property on register programming and writes to CSR can't
	 * be unintentional from programmer's perspective. As long as programmer has guarded areas
	 * which perform writes to CSR_SSP properly, shadow stack pivoting is not possible. Since
	 * CSR_SSP is writable by user mode, it itself can setup a shadow stack token subsequent
	 * to allocation. Although in order to provide portablity with other architectures (because
	 * `map_shadow_stack` is arch agnostic syscall), RISC-V will follow expectation of a token
	 * flag in flags and if provided in flags, will setup a token at the base.
	 */

	/* If there isn't space for a token */
	if (set_tok && size < SHSTK_ENTRY_SIZE)
		return -ENOSPC;

	if (addr && (addr & (PAGE_SIZE - 1)))
		return -EINVAL;

	aligned_size = PAGE_ALIGN(size);
	if (aligned_size < size)
		return -EOVERFLOW;

	return allocate_shadow_stack(addr, aligned_size, size, set_tok);
}

/*
 * This gets called during clone/clone3/fork. And is needed to allocate a shadow stack for
 * cases where CLONE_VM is specified and thus a different stack is specified by user. We
 * thus need a separate shadow stack too. How a separate shadow stack is specified by
 * user is still being debated. Once that's settled, remove this part of the comment.
 * This function simply returns 0 if shadow stacks are not supported or if separate shadow
 * stack allocation is not needed (like in case of !CLONE_VM)
 */
unsigned long shstk_alloc_thread_stack(struct task_struct *tsk,
				       const struct kernel_clone_args *args)
{
	unsigned long addr, size;

	/* If shadow stack is not supported, return 0 */
	if (!is_user_shstk_enabled())
		return 0;

	/*
	 * If shadow stack is not enabled on the new thread, skip any
	 * switch to a new shadow stack.
	 */
	if (!is_shstk_enabled(tsk))
		return 0;

	/*
	 * For CLONE_VFORK the child will share the parents shadow stack.
	 * Set base = 0 and size = 0, this is special means to track this state
	 * so the freeing logic run for child knows to leave it alone.
	 */
	if (args->flags & CLONE_VFORK) {
		set_shstk_base(tsk, 0, 0);
		return 0;
	}

	/*
	 * For !CLONE_VM the child will use a copy of the parents shadow
	 * stack.
	 */
	if (!(args->flags & CLONE_VM))
		return 0;

	/*
	 * reaching here means, CLONE_VM was specified and thus a separate shadow
	 * stack is needed for new cloned thread. Note: below allocation is happening
	 * using current mm.
	 */
	size = calc_shstk_size(args->stack_size);
	addr = allocate_shadow_stack(0, size, 0, false);
	if (IS_ERR_VALUE(addr))
		return addr;

	set_shstk_base(tsk, addr, size);

Annotation

Implementation Notes