|
|
Message-Id: <1499724283-30719-3-git-send-email-labbott@redhat.com>
Date: Mon, 10 Jul 2017 15:04:43 -0700
From: Laura Abbott <labbott@...hat.com>
To: Kees Cook <keescook@...omium.org>,
Alex Popov <alex.popov@...ux.com>
Cc: Laura Abbott <labbott@...hat.com>,
kernel-hardening@...ts.openwall.com,
Mark Rutland <mark.rutland@....com>,
Ard Biesheuvel <ard.biesheuvel@...aro.org>
Subject: [RFC][PATCH 2/2] arm64: Clear the stack
Implementation of stackleak based heavily on the x86 version
Signed-off-by: Laura Abbott <labbott@...hat.com>
---
The biggest points that need review here:
- Is the extra offsetting (subtracting/adding from the stack) correct?
- Where else do we need to clear the stack?
- The assembly can almost certainly be optimized. I tried to keep the
behavior of the x86 'repe scasq' and the like where possible. I'm also a
terrible register allocator.
---
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/processor.h | 3 ++
arch/arm64/kernel/asm-offsets.c | 3 ++
arch/arm64/kernel/entry.S | 92 +++++++++++++++++++++++++++++++++++
arch/arm64/kernel/process.c | 18 +++++++
drivers/firmware/efi/libstub/Makefile | 3 +-
scripts/Makefile.gcc-plugins | 5 +-
7 files changed, 123 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 8addb85..0b65bfc 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -17,6 +17,7 @@ config ARM64
select ARCH_HAS_KCOV
select ARCH_HAS_SET_MEMORY
select ARCH_HAS_SG_CHAIN
+ select ARCH_HAS_STACKLEAK
select ARCH_HAS_STRICT_KERNEL_RWX
select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 64c9e78..76f2738 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -88,6 +88,9 @@ struct thread_struct {
unsigned long fault_address; /* fault info */
unsigned long fault_code; /* ESR_EL1 value */
struct debug_info debug; /* debugging */
+#ifdef CONFIG_STACKLEAK
+ unsigned long lowest_stack;
+#endif
};
#ifdef CONFIG_COMPAT
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index b3bb7ef..e0a5ae2 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -43,6 +43,9 @@ int main(void)
DEFINE(TSK_TI_TTBR0, offsetof(struct task_struct, thread_info.ttbr0));
#endif
DEFINE(TSK_STACK, offsetof(struct task_struct, stack));
+#ifdef CONFIG_STACKLEAK
+ DEFINE(TSK_TI_LOWEST_STACK, offsetof(struct task_struct, thread.lowest_stack));
+#endif
BLANK();
DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context));
BLANK();
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index b738880..e573633 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -744,6 +744,7 @@ ENDPROC(cpu_switch_to)
*/
ret_fast_syscall:
disable_irq // disable interrupts
+ bl erase_kstack
str x0, [sp, #S_X0] // returned x0
ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for syscall tracing
and x2, x1, #_TIF_SYSCALL_WORK
@@ -772,6 +773,7 @@ work_pending:
*/
ret_to_user:
disable_irq // disable interrupts
+ bl erase_kstack
ldr x1, [tsk, #TSK_TI_FLAGS]
and x2, x1, #_TIF_WORK_MASK
cbnz x2, work_pending
@@ -865,3 +867,93 @@ ENTRY(sys_rt_sigreturn_wrapper)
mov x0, sp
b sys_rt_sigreturn
ENDPROC(sys_rt_sigreturn_wrapper)
+
+#ifdef CONFIG_STACKLEAK
+ENTRY(erase_kstack)
+ /* save x0 for the fast path */
+ mov x10, x0
+
+ get_thread_info x0
+ ldr x1, [x0, #TSK_TI_LOWEST_STACK]
+
+ /* get the number of bytes to check for lowest stack */
+ mov x3, x1
+ and x3, x3, #THREAD_SIZE - 1
+ lsr x3, x3, #3
+
+ /* now generate the start of the stack */
+ mov x4, sp
+ movn x2, #THREAD_SIZE - 1
+ and x1, x4, x2
+
+ mov x2, #-0xBEEF /* stack poison */
+
+ cmp x3, #0
+ b.eq 4f /* Found nothing, go poison */
+
+1:
+ ldr x4, [x1, x3, lsl #3]
+ cmp x4, x2 /* did we find the poison? */
+ b.eq 2f /* yes we did, go check it */
+
+ sub x3, x3, #1
+ cmp x3, #0
+ b.eq 4f /* Found nothing, go poison */
+ b 1b /* loop again */
+
+2:
+ cmp x3, #16
+ b.ls 4f /* Go poison if there are less than 16 things left? */
+
+ mov x3, #16
+3:
+ ldr x4, [x1, x3, lsl #3]
+ cmp x4, x2 /* did we find the poison? */
+ b.ne 1b /* nope we have to check deeper */
+
+ sub x3, x3, #1
+ cmp x3, #0
+ b.eq 4f /* Found nothing, go poison */
+ b 3b /* loop again */
+
+ /* The poison function */
+4:
+ /* Generate the address we found */
+ add x5, x1, x3, lsl #3
+ orr x5, x5, #16
+
+ mov x4, sp
+ /* subtrace the current pointer */
+ sub x8, x4, x5
+
+ /* subtract one more so we don't touch the current sp */
+ sub x8, x8, #1
+
+ /* sanity check */
+ cmp x8, #THREAD_SIZE
+ b.lo 5f
+999:
+ b 999b
+
+5:
+ lsr x8, x8, #3
+ mov x3, x8
+6:
+ cmp x3, #0
+ b.eq 7f
+
+ str x2, [x1, x3, lsl #3]
+ sub x3, x3, #1
+ b 6b
+
+ /* Reset the lowest stack to the top of the stack */
+7:
+ ldr x1, [x0, TSK_STACK]
+ add x1, x1, #THREAD_SIZE
+ sub x1, x1, #256
+ str x1, [x0, #TSK_TI_LOWEST_STACK]
+
+ mov x0, x10
+ ret
+ENDPROC(erase_kstack)
+#endif
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 659ae80..1b6cca2 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -293,6 +293,12 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
p->thread.cpu_context.sp = (unsigned long)childregs;
+#ifdef CONFIG_STACKLEAK
+ p->thread.lowest_stack = (unsigned long)task_stack_page(p) +
+ 2 * sizeof(unsigned long);
+#endif
+
+
ptrace_hw_copy_thread(p);
return 0;
@@ -417,3 +423,15 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
else
return randomize_page(mm->brk, SZ_1G);
}
+
+#ifdef CONFIG_STACKLEAK
+void __used check_alloca(unsigned long size)
+{
+ unsigned long sp = (unsigned long)&sp, stack_left;
+
+ /* all kernel stacks are of the same size */
+ stack_left = sp & (THREAD_SIZE - 1);
+ BUG_ON(stack_left < 256 || size >= stack_left - 256);
+}
+EXPORT_SYMBOL(check_alloca);
+#endif
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index f742596..cb378fa 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -18,7 +18,8 @@ cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
$(call cc-option,-ffreestanding) \
- $(call cc-option,-fno-stack-protector)
+ $(call cc-option,-fno-stack-protector) \
+ $(DISABLE_STACKLEAK_PLUGIN)
GCOV_PROFILE := n
KASAN_SANITIZE := n
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 5c86f64..0eb8dbc 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -35,11 +35,14 @@ ifdef CONFIG_GCC_PLUGINS
gcc-plugin-$(CONFIG_STACKLEAK) += stackleak_plugin.so
gcc-plugin-cflags-$(CONFIG_STACKLEAK) += -DSTACKLEAK_PLUGIN -fplugin-arg-stackleak_plugin-track-lowest-sp=100
+ ifdef CONFIG_STACKLEAK
+ DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable
+ endif
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
- export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
+ export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN DISABLE_STACKLEAK_PLUGIN
ifneq ($(PLUGINCC),)
# SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
--
2.7.5
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.