Skip to content

Commit fdb92b0

Browse files
keesAlex Shi
authored andcommitted
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: Kees Cook <keescook@chromium.org> (cherry picked from commit 0f60a8efe4005ab5e65ce000724b04d4ca04a199) Signed-off-by: Alex Shi <alex.shi@linaro.org> Conflicts: skip EBPF_JIT in arch/x86/Kconfig
1 parent 472dd69 commit fdb92b0

4 files changed

Lines changed: 63 additions & 1 deletion

File tree

arch/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,15 @@ config CC_STACKPROTECTOR_STRONG
423423

424424
endchoice
425425

426+
config HAVE_ARCH_WITHIN_STACK_FRAMES
427+
bool
428+
help
429+
An architecture should select this if it can walk the kernel stack
430+
frames to determine if an object is part of either the arguments
431+
or local variables (i.e. that it excludes saved return addresses,
432+
and similar) by implementing an inline arch_within_stack_frames(),
433+
which is used by CONFIG_HARDENED_USERCOPY.
434+
426435
config HAVE_CONTEXT_TRACKING
427436
bool
428437
help

arch/x86/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ config X86
8686
select HAVE_ARCH_SOFT_DIRTY if X86_64
8787
select HAVE_ARCH_TRACEHOOK
8888
select HAVE_ARCH_TRANSPARENT_HUGEPAGE
89-
select HAVE_BPF_JIT if X86_64
89+
select HAVE_ARCH_WITHIN_STACK_FRAMES
9090
select HAVE_CC_STACKPROTECTOR
9191
select HAVE_CMPXCHG_DOUBLE
9292
select HAVE_CMPXCHG_LOCAL

arch/x86/include/asm/thread_info.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,50 @@ static inline unsigned long current_stack_pointer(void)
177177
return sp;
178178
}
179179

180+
/*
181+
* Walks up the stack frames to make sure that the specified object is
182+
* entirely contained by a single stack frame.
183+
*
184+
* Returns:
185+
* 1 if within a frame
186+
* -1 if placed across a frame boundary (or outside stack)
187+
* 0 unable to determine (no frame pointers, etc)
188+
*/
189+
static inline int arch_within_stack_frames(const void * const stack,
190+
const void * const stackend,
191+
const void *obj, unsigned long len)
192+
{
193+
#if defined(CONFIG_FRAME_POINTER)
194+
const void *frame = NULL;
195+
const void *oldframe;
196+
197+
oldframe = __builtin_frame_address(1);
198+
if (oldframe)
199+
frame = __builtin_frame_address(2);
200+
/*
201+
* low ----------------------------------------------> high
202+
* [saved bp][saved ip][args][local vars][saved bp][saved ip]
203+
* ^----------------^
204+
* allow copies only within here
205+
*/
206+
while (stack <= frame && frame < stackend) {
207+
/*
208+
* If obj + len extends past the last frame, this
209+
* check won't pass and the next frame will be 0,
210+
* causing us to bail out and correctly report
211+
* the copy as invalid.
212+
*/
213+
if (obj + len <= frame)
214+
return obj >= oldframe + 2 * sizeof(void *) ? 1 : -1;
215+
oldframe = frame;
216+
frame = *(const void * const *)frame;
217+
}
218+
return -1;
219+
#else
220+
return 0;
221+
#endif
222+
}
223+
180224
#else /* !__ASSEMBLY__ */
181225

182226
#ifdef CONFIG_X86_64

include/linux/thread_info.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ static inline bool test_and_clear_restore_sigmask(void)
145145
#error "no set_restore_sigmask() provided and default one won't work"
146146
#endif
147147

148+
#ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES
149+
static inline int arch_within_stack_frames(const void * const stack,
150+
const void * const stackend,
151+
const void *obj, unsigned long len)
152+
{
153+
return 0;
154+
}
155+
#endif
156+
148157
#endif /* __KERNEL__ */
149158

150159
#endif /* _LINUX_THREAD_INFO_H */

0 commit comments

Comments
 (0)