Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f60a8ef authored by Kees Cook's avatar Kees Cook
Browse files

mm: Implement stack frame object validation



This creates per-architecture function arch_within_stack_frames() that
should validate if a given object is contained by a kernel stack frame.
Initial implementation is on x86.

This is based on code from PaX.

Signed-off-by: default avatarKees Cook <keescook@chromium.org>
parent 7c15d9bb
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -424,6 +424,15 @@ config CC_STACKPROTECTOR_STRONG

endchoice

config HAVE_ARCH_WITHIN_STACK_FRAMES
	bool
	help
	  An architecture should select this if it can walk the kernel stack
	  frames to determine if an object is part of either the arguments
	  or local variables (i.e. that it excludes saved return addresses,
	  and similar) by implementing an inline arch_within_stack_frames(),
	  which is used by CONFIG_HARDENED_USERCOPY.

config HAVE_CONTEXT_TRACKING
	bool
	help
+1 −0
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ config X86
	select HAVE_ARCH_SOFT_DIRTY		if X86_64
	select HAVE_ARCH_TRACEHOOK
	select HAVE_ARCH_TRANSPARENT_HUGEPAGE
	select HAVE_ARCH_WITHIN_STACK_FRAMES
	select HAVE_EBPF_JIT			if X86_64
	select HAVE_CC_STACKPROTECTOR
	select HAVE_CMPXCHG_DOUBLE
+44 −0
Original line number Diff line number Diff line
@@ -180,6 +180,50 @@ static inline unsigned long current_stack_pointer(void)
	return sp;
}

/*
 * Walks up the stack frames to make sure that the specified object is
 * entirely contained by a single stack frame.
 *
 * Returns:
 *		 1 if within a frame
 *		-1 if placed across a frame boundary (or outside stack)
 *		 0 unable to determine (no frame pointers, etc)
 */
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 *frame = NULL;
	const void *oldframe;

	oldframe = __builtin_frame_address(1);
	if (oldframe)
		frame = __builtin_frame_address(2);
	/*
	 * low ----------------------------------------------> high
	 * [saved bp][saved ip][args][local vars][saved bp][saved ip]
	 *                     ^----------------^
	 *               allow copies only within here
	 */
	while (stack <= frame && frame < stackend) {
		/*
		 * If obj + len extends past the last frame, this
		 * check won't pass and the next frame will be 0,
		 * causing us to bail out and correctly report
		 * the copy as invalid.
		 */
		if (obj + len <= frame)
			return obj >= oldframe + 2 * sizeof(void *) ? 1 : -1;
		oldframe = frame;
		frame = *(const void * const *)frame;
	}
	return -1;
#else
	return 0;
#endif
}

#else /* !__ASSEMBLY__ */

#ifdef CONFIG_X86_64
+9 −0
Original line number Diff line number Diff line
@@ -146,6 +146,15 @@ static inline bool test_and_clear_restore_sigmask(void)
#error "no set_restore_sigmask() provided and default one won't work"
#endif

#ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES
static inline int arch_within_stack_frames(const void * const stack,
					   const void * const stackend,
					   const void *obj, unsigned long len)
{
	return 0;
}
#endif

#endif	/* __KERNEL__ */

#endif /* _LINUX_THREAD_INFO_H */