Skip to content

Commit

Permalink
Merge tag 'kernel-hardening-v5.19-rc1' of git://git.kernel.org/pub/sc…
Browse files Browse the repository at this point in the history
…m/linux/kernel/git/kees/linux

Pull kernel hardening updates from Kees Cook:

 - usercopy hardening expanded to check other allocation types (Matthew
   Wilcox, Yuanzheng Song)

 - arm64 stackleak behavioral improvements (Mark Rutland)

 - arm64 CFI code gen improvement (Sami Tolvanen)

 - LoadPin LSM block dev API adjustment (Christoph Hellwig)

 - Clang randstruct support (Bill Wendling, Kees Cook)

* tag 'kernel-hardening-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (34 commits)
  loadpin: stop using bdevname
  mm: usercopy: move the virt_addr_valid() below the is_vmalloc_addr()
  gcc-plugins: randstruct: Remove cast exception handling
  af_unix: Silence randstruct GCC plugin warning
  niu: Silence randstruct warnings
  big_keys: Use struct for internal payload
  gcc-plugins: Change all version strings match kernel
  randomize_kstack: Improve docs on requirements/rationale
  lkdtm/stackleak: fix CONFIG_GCC_PLUGIN_STACKLEAK=n
  arm64: entry: use stackleak_erase_on_task_stack()
  stackleak: add on/off stack variants
  lkdtm/stackleak: check stack boundaries
  lkdtm/stackleak: prevent unexpected stack usage
  lkdtm/stackleak: rework boundary management
  lkdtm/stackleak: avoid spurious failure
  stackleak: rework poison scanning
  stackleak: rework stack high bound handling
  stackleak: clarify variable names
  stackleak: rework stack low bound handling
  stackleak: remove redundant check
  ...
  • Loading branch information
torvalds committed May 24, 2022
2 parents 51518aa + ed5edd5 commit 0bf13a8
Show file tree
Hide file tree
Showing 46 changed files with 539 additions and 449 deletions.
1 change: 1 addition & 0 deletions Documentation/dontdiff
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ r200_reg_safe.h
r300_reg_safe.h
r420_reg_safe.h
r600_reg_safe.h
randstruct.seed
randomize_layout_hash.h
randomize_layout_seed.h
recordmcount
Expand Down
8 changes: 4 additions & 4 deletions Documentation/kbuild/reproducible-builds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ unreproducible parts can be treated as sources:
Structure randomisation
-----------------------

If you enable ``CONFIG_GCC_PLUGIN_RANDSTRUCT``, you will need to
pre-generate the random seed in
``scripts/gcc-plugins/randomize_layout_seed.h`` so the same value
is used in rebuilds.
If you enable ``CONFIG_RANDSTRUCT``, you will need to pre-generate
the random seed in ``scripts/basic/randstruct.seed`` so the same
value is used by each build. See ``scripts/gen-randstruct-seed.sh``
for details.

Debug info conflicts
--------------------
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,7 @@ include-$(CONFIG_KASAN) += scripts/Makefile.kasan
include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan
include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan
include-$(CONFIG_KCOV) += scripts/Makefile.kcov
include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct
include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins

include $(addprefix $(srctree)/, $(include-y))
Expand Down
5 changes: 1 addition & 4 deletions arch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -732,10 +732,7 @@ config ARCH_SUPPORTS_CFI_CLANG
config CFI_CLANG
bool "Use Clang's Control Flow Integrity (CFI)"
depends on LTO_CLANG && ARCH_SUPPORTS_CFI_CLANG
# Clang >= 12:
# - https://bugs.llvm.org/show_bug.cgi?id=46258
# - https://bugs.llvm.org/show_bug.cgi?id=47479
depends on CLANG_VERSION >= 120000
depends on CLANG_VERSION >= 140000
select KALLSYMS
help
This option enables Clang’s forward-edge Control Flow Integrity
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/vdso/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
CFLAGS_REMOVE_vdso.o = -pg

# Force -O2 to avoid libgcc dependencies
CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(GCC_PLUGINS_CFLAGS)
CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS)
ifeq ($(c-gettimeofday-y),)
CFLAGS_vgettimeofday.o = -O2
else
Expand Down
16 changes: 0 additions & 16 deletions arch/arm64/include/asm/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,4 @@
#define __builtin_return_address(val) \
(void *)(ptrauth_clear_pac((unsigned long)__builtin_return_address(val)))

#ifdef CONFIG_CFI_CLANG
/*
* With CONFIG_CFI_CLANG, the compiler replaces function address
* references with the address of the function's CFI jump table
* entry. The function_nocfi macro always returns the address of the
* actual function instead.
*/
#define function_nocfi(x) ({ \
void *addr; \
asm("adrp %0, " __stringify(x) "\n\t" \
"add %0, %0, :lo12:" __stringify(x) \
: "=r" (addr)); \
addr; \
})
#endif

#endif /* __ASM_COMPILER_H */
10 changes: 4 additions & 6 deletions arch/arm64/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,10 @@ long get_tagged_addr_ctrl(struct task_struct *task);
* of header definitions for the use of task_stack_page.
*/

#define current_top_of_stack() \
({ \
struct stack_info _info; \
BUG_ON(!on_accessible_stack(current, current_stack_pointer, 1, &_info)); \
_info.high; \
})
/*
* The top of the current task's task stack
*/
#define current_top_of_stack() ((unsigned long)current->stack + THREAD_SIZE)
#define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1, NULL))

#endif /* __ASSEMBLY__ */
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ SYM_CODE_START_LOCAL(ret_to_user)
ldr x19, [tsk, #TSK_TI_FLAGS] // re-check for single-step
enable_step_tsk x19, x2
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
bl stackleak_erase
bl stackleak_erase_on_task_stack
#endif
kernel_exit 0
SYM_CODE_END(ret_to_user)
Expand Down
3 changes: 2 additions & 1 deletion arch/arm64/kernel/vdso/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
# -Wmissing-prototypes and -Wmissing-declarations are removed from
# the CFLAGS of vgettimeofday.c to make possible to build the
# kernel with CONFIG_WERROR enabled.
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) $(GCC_PLUGINS_CFLAGS) \
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \
$(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \
$(CC_FLAGS_LTO) -Wmissing-prototypes -Wmissing-declarations
KASAN_SANITIZE := n
KCSAN_SANITIZE := n
Expand Down
2 changes: 1 addition & 1 deletion arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ config CC_HAVE_STACKPROTECTOR_TLS

config STACKPROTECTOR_PER_TASK
def_bool y
depends on !GCC_PLUGIN_RANDSTRUCT
depends on !RANDSTRUCT
depends on STACKPROTECTOR && CC_HAVE_STACKPROTECTOR_TLS

config PHYS_RAM_BASE_FIXED
Expand Down
3 changes: 2 additions & 1 deletion arch/sparc/vdso/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 -fasynchronous-unwind-tables -m64

SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 -fcall-used-g5 -fcall-used-g7

$(vobjs): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
$(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)

#
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
Expand Down Expand Up @@ -88,6 +88,7 @@ $(obj)/vdso32.so.dbg: asflags-$(CONFIG_SPARC64) += -m32
KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
KBUILD_CFLAGS_32 := $(filter-out -mcmodel=medlow,$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 += -m32 -msoft-float -fpic
Expand Down
3 changes: 2 additions & 1 deletion arch/x86/entry/vdso/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ ifneq ($(RETPOLINE_VDSO_CFLAGS),)
endif
endif

$(vobjs): KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
$(vobjs): KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO) $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)

#
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
Expand Down Expand Up @@ -148,6 +148,7 @@ KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
KBUILD_CFLAGS_32 := $(filter-out -mcmodel=kernel,$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out -mfentry,$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS_32))
KBUILD_CFLAGS_32 := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS_32))
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/asm/highmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <asm/tlbflush.h>
#include <asm/paravirt.h>
#include <asm/fixmap.h>
#include <asm/pgtable_areas.h>

/* declarations for highmem.c */
extern unsigned long highstart_pfn, highend_pfn;
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/mm/pti.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ static inline bool pti_kernel_image_global_ok(void)
* cases where RANDSTRUCT is in use to help keep the layout a
* secret.
*/
if (IS_ENABLED(CONFIG_GCC_PLUGIN_RANDSTRUCT))
if (IS_ENABLED(CONFIG_RANDSTRUCT))
return false;

return true;
Expand Down
145 changes: 99 additions & 46 deletions drivers/misc/lkdtm/stackleak.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,72 +11,125 @@
#include "lkdtm.h"
#include <linux/stackleak.h>

void lkdtm_STACKLEAK_ERASING(void)
#if defined(CONFIG_GCC_PLUGIN_STACKLEAK)
/*
* Check that stackleak tracks the lowest stack pointer and erases the stack
* below this as expected.
*
* To prevent the lowest stack pointer changing during the test, IRQs are
* masked and instrumentation of this function is disabled. We assume that the
* compiler will create a fixed-size stack frame for this function.
*
* Any non-inlined function may make further use of the stack, altering the
* lowest stack pointer and/or clobbering poison values. To avoid spurious
* failures we must avoid printing until the end of the test or have already
* encountered a failure condition.
*/
static void noinstr check_stackleak_irqoff(void)
{
unsigned long *sp, left, found, i;
const unsigned long check_depth =
STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long);
const unsigned long task_stack_base = (unsigned long)task_stack_page(current);
const unsigned long task_stack_low = stackleak_task_low_bound(current);
const unsigned long task_stack_high = stackleak_task_high_bound(current);
const unsigned long current_sp = current_stack_pointer;
const unsigned long lowest_sp = current->lowest_stack;
unsigned long untracked_high;
unsigned long poison_high, poison_low;
bool test_failed = false;

/*
* For the details about the alignment of the poison values, see
* the comment in stackleak_track_stack().
* Check that the current and lowest recorded stack pointer values fall
* within the expected task stack boundaries. These tests should never
* fail unless the boundaries are incorrect or we're clobbering the
* STACK_END_MAGIC, and in either casee something is seriously wrong.
*/
sp = PTR_ALIGN(&i, sizeof(unsigned long));

left = ((unsigned long)sp & (THREAD_SIZE - 1)) / sizeof(unsigned long);
sp--;
if (current_sp < task_stack_low || current_sp >= task_stack_high) {
pr_err("FAIL: current_stack_pointer (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n",
current_sp, task_stack_low, task_stack_high - 1);
test_failed = true;
goto out;
}
if (lowest_sp < task_stack_low || lowest_sp >= task_stack_high) {
pr_err("FAIL: current->lowest_stack (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n",
lowest_sp, task_stack_low, task_stack_high - 1);
test_failed = true;
goto out;
}

/*
* One 'long int' at the bottom of the thread stack is reserved
* and not poisoned.
* Depending on what has run prior to this test, the lowest recorded
* stack pointer could be above or below the current stack pointer.
* Start from the lowest of the two.
*
* Poison values are naturally-aligned unsigned longs. As the current
* stack pointer might not be sufficiently aligned, we must align
* downwards to find the lowest known stack pointer value. This is the
* high boundary for a portion of the stack which may have been used
* without being tracked, and has to be scanned for poison.
*/
if (left > 1) {
left--;
} else {
pr_err("FAIL: not enough stack space for the test\n");
test_failed = true;
goto end;
}
untracked_high = min(current_sp, lowest_sp);
untracked_high = ALIGN_DOWN(untracked_high, sizeof(unsigned long));

pr_info("checking unused part of the thread stack (%lu bytes)...\n",
left * sizeof(unsigned long));
/*
* Find the top of the poison in the same way as the erasing code.
*/
poison_high = stackleak_find_top_of_poison(task_stack_low, untracked_high);

/*
* Search for 'check_depth' poison values in a row (just like
* stackleak_erase() does).
* Check whether the poisoned portion of the stack (if any) consists
* entirely of poison. This verifies the entries that
* stackleak_find_top_of_poison() should have checked.
*/
for (i = 0, found = 0; i < left && found <= check_depth; i++) {
if (*(sp - i) == STACKLEAK_POISON)
found++;
else
found = 0;
}
poison_low = poison_high;
while (poison_low > task_stack_low) {
poison_low -= sizeof(unsigned long);

if (*(unsigned long *)poison_low == STACKLEAK_POISON)
continue;

if (found <= check_depth) {
pr_err("FAIL: the erased part is not found (checked %lu bytes)\n",
i * sizeof(unsigned long));
pr_err("FAIL: non-poison value %lu bytes below poison boundary: 0x%lx\n",
poison_high - poison_low, *(unsigned long *)poison_low);
test_failed = true;
goto end;
}

pr_info("the erased part begins after %lu not poisoned bytes\n",
(i - found) * sizeof(unsigned long));

/* The rest of thread stack should be erased */
for (; i < left; i++) {
if (*(sp - i) != STACKLEAK_POISON) {
pr_err("FAIL: bad value number %lu in the erased part: 0x%lx\n",
i, *(sp - i));
test_failed = true;
}
}
pr_info("stackleak stack usage:\n"
" high offset: %lu bytes\n"
" current: %lu bytes\n"
" lowest: %lu bytes\n"
" tracked: %lu bytes\n"
" untracked: %lu bytes\n"
" poisoned: %lu bytes\n"
" low offset: %lu bytes\n",
task_stack_base + THREAD_SIZE - task_stack_high,
task_stack_high - current_sp,
task_stack_high - lowest_sp,
task_stack_high - untracked_high,
untracked_high - poison_high,
poison_high - task_stack_low,
task_stack_low - task_stack_base);

end:
out:
if (test_failed) {
pr_err("FAIL: the thread stack is NOT properly erased!\n");
pr_expected_config(CONFIG_GCC_PLUGIN_STACKLEAK);
} else {
pr_info("OK: the rest of the thread stack is properly erased\n");
}
}

void lkdtm_STACKLEAK_ERASING(void)
{
unsigned long flags;

local_irq_save(flags);
check_stackleak_irqoff();
local_irq_restore(flags);
}
#else /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */
void lkdtm_STACKLEAK_ERASING(void)
{
if (IS_ENABLED(CONFIG_HAVE_ARCH_STACKLEAK)) {
pr_err("XFAIL: stackleak is not enabled (CONFIG_GCC_PLUGIN_STACKLEAK=n)\n");
} else {
pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_STACKLEAK=n)\n");
}
}
#endif /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */
Loading

0 comments on commit 0bf13a8

Please sign in to comment.