Skip to content

Commit 2792d84

Browse files
committed
usercopy: Check valid lifetime via stack depth
One of the things that CONFIG_HARDENED_USERCOPY sanity-checks is whether an object that is about to be copied to/from userspace is overlapping the stack at all. If it is, it performs a number of inexpensive bounds checks. One of the finer-grained checks is whether an object crosses stack frames within the stack region. Doing this on x86 with CONFIG_FRAME_POINTER was cheap/easy. Doing it with ORC was deemed too heavy, and was left out (a while ago), leaving the courser whole-stack check. The LKDTM tests USERCOPY_STACK_FRAME_TO and USERCOPY_STACK_FRAME_FROM try to exercise these cross-frame cases to validate the defense is working. They have been failing ever since ORC was added (which was expected). While Muhammad was investigating various LKDTM failures[1], he asked me for additional details on them, and I realized that when exact stack frame boundary checking is not available (i.e. everything except x86 with FRAME_POINTER), it could check if a stack object is at least "current depth valid", in the sense that any object within the stack region but not between start-of-stack and current_stack_pointer should be considered unavailable (i.e. its lifetime is from a call no longer present on the stack). Introduce ARCH_HAS_CURRENT_STACK_POINTER to track which architectures have actually implemented the common global register alias. Additionally report usercopy bounds checking failures with an offset from current_stack_pointer, which may assist with diagnosing failures. The LKDTM USERCOPY_STACK_FRAME_TO and USERCOPY_STACK_FRAME_FROM tests (once slightly adjusted in a separate patch) pass again with this fixed. [1] kernelci/kernelci-project#84 Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: linux-mm@kvack.org Reported-by: Muhammad Usama Anjum <usama.anjum@collabora.com> Signed-off-by: Kees Cook <keescook@chromium.org> --- v1: https://lore.kernel.org/lkml/20220216201449.2087956-1-keescook@chromium.org v2: https://lore.kernel.org/lkml/20220224060342.1855457-1-keescook@chromium.org v3: https://lore.kernel.org/lkml/20220225173345.3358109-1-keescook@chromium.org v4: - improve commit log (akpm)
1 parent efa90c1 commit 2792d84

8 files changed

Lines changed: 36 additions & 2 deletions

File tree

arch/arm/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ config ARM
55
select ARCH_32BIT_OFF_T
66
select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE if HAVE_KRETPROBES && FRAME_POINTER && !ARM_UNWIND
77
select ARCH_HAS_BINFMT_FLAT
8+
select ARCH_HAS_CURRENT_STACK_POINTER
89
select ARCH_HAS_DEBUG_VIRTUAL if MMU
910
select ARCH_HAS_DMA_WRITE_COMBINE if !ARM_DMA_MEM_BUFFERABLE
1011
select ARCH_HAS_ELF_RANDOMIZE

arch/arm64/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ config ARM64
1818
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
1919
select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
2020
select ARCH_HAS_CACHE_LINE_SIZE
21+
select ARCH_HAS_CURRENT_STACK_POINTER
2122
select ARCH_HAS_DEBUG_VIRTUAL
2223
select ARCH_HAS_DEBUG_VM_PGTABLE
2324
select ARCH_HAS_DMA_PREP_COHERENT

arch/powerpc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ config PPC
108108
select ARCH_ENABLE_MEMORY_HOTPLUG
109109
select ARCH_ENABLE_MEMORY_HOTREMOVE
110110
select ARCH_HAS_COPY_MC if PPC64
111+
select ARCH_HAS_CURRENT_STACK_POINTER
111112
select ARCH_HAS_DEBUG_VIRTUAL
112113
select ARCH_HAS_DEBUG_VM_PGTABLE
113114
select ARCH_HAS_DEBUG_WX if STRICT_KERNEL_RWX

arch/s390/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ config S390
6060
select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM
6161
select ARCH_ENABLE_MEMORY_HOTREMOVE
6262
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
63+
select ARCH_HAS_CURRENT_STACK_POINTER
6364
select ARCH_HAS_DEBUG_VM_PGTABLE
6465
select ARCH_HAS_DEBUG_WX
6566
select ARCH_HAS_DEVMEM_IS_ALLOWED

arch/sh/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ config SUPERH
77
select ARCH_HAVE_CUSTOM_GPIO_H
88
select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A)
99
select ARCH_HAS_BINFMT_FLAT if !MMU
10+
select ARCH_HAS_CURRENT_STACK_POINTER
1011
select ARCH_HAS_GIGANTIC_PAGE
1112
select ARCH_HAS_GCOV_PROFILE_ALL
1213
select ARCH_HAS_PTE_SPECIAL

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ config X86
6969
select ARCH_ENABLE_THP_MIGRATION if X86_64 && TRANSPARENT_HUGEPAGE
7070
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
7171
select ARCH_HAS_CACHE_LINE_SIZE
72+
select ARCH_HAS_CURRENT_STACK_POINTER
7273
select ARCH_HAS_DEBUG_VIRTUAL
7374
select ARCH_HAS_DEBUG_VM_PGTABLE if !X86_PAE
7475
select ARCH_HAS_DEVMEM_IS_ALLOWED

mm/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,15 @@ config IDLE_PAGE_TRACKING
744744
config ARCH_HAS_CACHE_LINE_SIZE
745745
bool
746746

747+
config ARCH_HAS_CURRENT_STACK_POINTER
748+
bool
749+
help
750+
In support of HARDENED_USERCOPY performing stack variable lifetime
751+
checking, an architecture-agnostic way to find the stack pointer
752+
is needed. Once an architecture defines an unsigned long global
753+
register alias named "current_stack_pointer", this config can be
754+
selected.
755+
747756
config ARCH_HAS_PTE_DEVMAP
748757
bool
749758

mm/usercopy.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* Returns:
3030
* NOT_STACK: not at all on the stack
3131
* GOOD_FRAME: fully within a valid stack frame
32-
* GOOD_STACK: fully on the stack (when can't do frame-checking)
32+
* GOOD_STACK: within the current stack (when can't frame-check exactly)
3333
* BAD_STACK: error condition (invalid stack position or bad stack frame)
3434
*/
3535
static noinline int check_stack_object(const void *obj, unsigned long len)
@@ -55,6 +55,17 @@ static noinline int check_stack_object(const void *obj, unsigned long len)
5555
if (ret)
5656
return ret;
5757

58+
/* Finally, check stack depth if possible. */
59+
#ifdef CONFIG_ARCH_HAS_CURRENT_STACK_POINTER
60+
if (IS_ENABLED(CONFIG_STACK_GROWSUP)) {
61+
if ((void *)current_stack_pointer < obj + len)
62+
return BAD_STACK;
63+
} else {
64+
if (obj < (void *)current_stack_pointer)
65+
return BAD_STACK;
66+
}
67+
#endif
68+
5869
return GOOD_STACK;
5970
}
6071

@@ -280,7 +291,15 @@ void __check_object_size(const void *ptr, unsigned long n, bool to_user)
280291
*/
281292
return;
282293
default:
283-
usercopy_abort("process stack", NULL, to_user, 0, n);
294+
usercopy_abort("process stack", NULL, to_user,
295+
#ifdef CONFIG_ARCH_HAS_CURRENT_STACK_POINTER
296+
IS_ENABLED(CONFIG_STACK_GROWSUP) ?
297+
ptr - (void *)current_stack_pointer :
298+
(void *)current_stack_pointer - ptr,
299+
#else
300+
0,
301+
#endif
302+
n);
284303
}
285304

286305
/* Check for bad heap object. */

0 commit comments

Comments
 (0)