New gcc-plugin:

- Enable per-task stack protector for ARM (Ard Biesheuvel)
 -----BEGIN PGP SIGNATURE-----
 Comment: Kees Cook <kees@outflux.net>
 
 iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAlwYN04WHGtlZXNjb29r
 QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJhUkD/4i0FW2fW1tY63lnbKhp6LFSGvF
 N7OdwPXwS5dlmVY8KFFrJp0GlT8qvJ3Kqwit5QTu9U9Mo32UbAVwB6gEW/kT3oB3
 Pk9pR/DMhTTxD6mUjIHq2c/IJgzw42slTbf1ezb5RtmFYaxqbX1A/wKes7ZTtp23
 JtNw2EkqDGMeMNDCbIZAaPHC7fwk6t40qgyrLOAHz+jnrL9tEd9/UIdqaRTn6HKE
 nslegmaDAUmPmmG8UK3RITQBD+FZsizcz/n2xKkCB79fld0qGwlUriacSmwVScBS
 LzGvVSrjUbfo/oo850RgrE48GxxlQntOF0z1foRQ7EFygk4gPPsqYRE9OvjE2Iid
 9BNtImt6mvOw7faD7sS6R9U5n/66Q3qlHi5FcU8ID6wVjwPM0RX0G4SDO/aTL//w
 FedMx4PPQtzQIH+X0WxI3mXreuEmN36rnQBR6XovBg2IBlmT6zMRXKhC+50U8+89
 WCUeWCuUz4p4daPgV4xKJu8H7EJxQN3YQ2IMStJCAgmrMAUX710pkJ5bt7meGg6Y
 LUPxRVBeQIC9UuJBDWzMbCYpHx/09o8FsFSEd/M6hW9NhGKo6PIGUu+1oy12LuqM
 eqb+m4wnylUXeaZnDTIYaVzwfCDCcMPTnt2X3Czc1huq8A5pGmfbUGBLMKHVoguA
 8J3bD55suTMTZut3vA==
 =AkLm
 -----END PGP SIGNATURE-----

Merge tag 'gcc-plugins-v4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull gcc-plugins update from Kees Cook:
 "Both arm and arm64 are gaining per-task stack canaries (to match x86),
  but arm is being done with a gcc plugin, hence it going through the
  gcc-plugins tree.

  New gcc-plugin:

   - Enable per-task stack protector for ARM (Ard Biesheuvel)"

* tag 'gcc-plugins-v4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  ARM: smp: add support for per-task stack canaries
This commit is contained in:
Linus Torvalds 2018-12-27 11:19:07 -08:00
commit c6f1b355d4
10 changed files with 163 additions and 3 deletions

View File

@ -1810,6 +1810,21 @@ config XEN
help help
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM. Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
config STACKPROTECTOR_PER_TASK
bool "Use a unique stack canary value for each task"
depends on GCC_PLUGINS && STACKPROTECTOR && SMP && !XIP_DEFLATED_DATA
select GCC_PLUGIN_ARM_SSP_PER_TASK
default y
help
Due to the fact that GCC uses an ordinary symbol reference from
which to load the value of the stack canary, this value can only
change at reboot time on SMP systems, and all tasks running in the
kernel's address space are forced to use the same canary value for
the entire duration that the system is up.
Enable this option to switch to a different method that uses a
different canary value for each task.
endmenu endmenu
menu "Boot options" menu "Boot options"

View File

@ -303,6 +303,18 @@ else
KBUILD_IMAGE := $(boot)/zImage KBUILD_IMAGE := $(boot)/zImage
endif endif
ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y)
prepare: stack_protector_prepare
stack_protector_prepare: prepare0
$(eval KBUILD_CFLAGS += \
-fplugin-arg-arm_ssp_per_task_plugin-tso=$(shell \
awk '{if ($$2 == "THREAD_SZ_ORDER") print $$3;}'\
include/generated/asm-offsets.h) \
-fplugin-arg-arm_ssp_per_task_plugin-offset=$(shell \
awk '{if ($$2 == "TI_STACK_CANARY") print $$3;}'\
include/generated/asm-offsets.h))
endif
all: $(notdir $(KBUILD_IMAGE)) all: $(notdir $(KBUILD_IMAGE))

View File

@ -101,6 +101,7 @@ clean-files += piggy_data lib1funcs.S ashldi3.S bswapsdi2.S \
$(libfdt) $(libfdt_hdrs) hyp-stub.S $(libfdt) $(libfdt_hdrs) hyp-stub.S
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
KBUILD_CFLAGS += $(DISABLE_ARM_SSP_PER_TASK_PLUGIN)
ifeq ($(CONFIG_FUNCTION_TRACER),y) ifeq ($(CONFIG_FUNCTION_TRACER),y)
ORIG_CFLAGS := $(KBUILD_CFLAGS) ORIG_CFLAGS := $(KBUILD_CFLAGS)

View File

@ -6,8 +6,10 @@
* the stack frame and verifying that it hasn't been overwritten when * the stack frame and verifying that it hasn't been overwritten when
* returning from the function. The pattern is called stack canary * returning from the function. The pattern is called stack canary
* and gcc expects it to be defined by a global variable called * and gcc expects it to be defined by a global variable called
* "__stack_chk_guard" on ARM. This unfortunately means that on SMP * "__stack_chk_guard" on ARM. This prevents SMP systems from using a
* we cannot have a different canary value per task. * different value for each task unless we enable a GCC plugin that
* replaces these symbol references with references to each task's own
* value.
*/ */
#ifndef _ASM_STACKPROTECTOR_H #ifndef _ASM_STACKPROTECTOR_H
@ -16,6 +18,8 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/version.h> #include <linux/version.h>
#include <asm/thread_info.h>
extern unsigned long __stack_chk_guard; extern unsigned long __stack_chk_guard;
/* /*
@ -33,7 +37,11 @@ static __always_inline void boot_init_stack_canary(void)
canary ^= LINUX_VERSION_CODE; canary ^= LINUX_VERSION_CODE;
current->stack_canary = canary; current->stack_canary = canary;
#ifndef CONFIG_STACKPROTECTOR_PER_TASK
__stack_chk_guard = current->stack_canary; __stack_chk_guard = current->stack_canary;
#else
current_thread_info()->stack_canary = current->stack_canary;
#endif
} }
#endif /* _ASM_STACKPROTECTOR_H */ #endif /* _ASM_STACKPROTECTOR_H */

View File

@ -53,6 +53,9 @@ struct thread_info {
struct task_struct *task; /* main task structure */ struct task_struct *task; /* main task structure */
__u32 cpu; /* cpu */ __u32 cpu; /* cpu */
__u32 cpu_domain; /* cpu domain */ __u32 cpu_domain; /* cpu domain */
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
unsigned long stack_canary;
#endif
struct cpu_context_save cpu_context; /* cpu context */ struct cpu_context_save cpu_context; /* cpu context */
__u32 syscall; /* syscall number */ __u32 syscall; /* syscall number */
__u8 used_cp[16]; /* thread used copro */ __u8 used_cp[16]; /* thread used copro */

View File

@ -79,6 +79,10 @@ int main(void)
#ifdef CONFIG_CRUNCH #ifdef CONFIG_CRUNCH
DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate)); DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate));
#endif #endif
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
DEFINE(TI_STACK_CANARY, offsetof(struct thread_info, stack_canary));
#endif
DEFINE(THREAD_SZ_ORDER, THREAD_SIZE_ORDER);
BLANK(); BLANK();
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0)); DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1)); DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));

View File

@ -39,7 +39,7 @@
#include <asm/tls.h> #include <asm/tls.h>
#include <asm/vdso.h> #include <asm/vdso.h>
#ifdef CONFIG_STACKPROTECTOR #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
#include <linux/stackprotector.h> #include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly; unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard); EXPORT_SYMBOL(__stack_chk_guard);
@ -267,6 +267,10 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,
thread_notify(THREAD_NOTIFY_COPY, thread); thread_notify(THREAD_NOTIFY_COPY, thread);
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
thread->stack_canary = p->stack_canary;
#endif
return 0; return 0;
} }

View File

@ -36,6 +36,12 @@ ifdef CONFIG_GCC_PLUGIN_STACKLEAK
endif endif
export DISABLE_STACKLEAK_PLUGIN export DISABLE_STACKLEAK_PLUGIN
gcc-plugin-$(CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK) += arm_ssp_per_task_plugin.so
ifdef CONFIG_GCC_PLUGIN_ARM_SSP_PER_TASK
DISABLE_ARM_SSP_PER_TASK_PLUGIN += -fplugin-arg-arm_ssp_per_task_plugin-disable
endif
export DISABLE_ARM_SSP_PER_TASK_PLUGIN
# All the plugin CFLAGS are collected here in case a build target needs to # All the plugin CFLAGS are collected here in case a build target needs to
# filter them out of the KBUILD_CFLAGS. # filter them out of the KBUILD_CFLAGS.
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))

View File

@ -190,4 +190,8 @@ config STACKLEAK_RUNTIME_DISABLE
runtime to control kernel stack erasing for kernels built with runtime to control kernel stack erasing for kernels built with
CONFIG_GCC_PLUGIN_STACKLEAK. CONFIG_GCC_PLUGIN_STACKLEAK.
config GCC_PLUGIN_ARM_SSP_PER_TASK
bool
depends on GCC_PLUGINS && ARM
endif endif

View File

@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
#include "gcc-common.h"
__visible int plugin_is_GPL_compatible;
static unsigned int sp_mask, canary_offset;
static unsigned int arm_pertask_ssp_rtl_execute(void)
{
rtx_insn *insn;
for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) {
const char *sym;
rtx body;
rtx masked_sp;
/*
* Find a SET insn involving a SYMBOL_REF to __stack_chk_guard
*/
if (!INSN_P(insn))
continue;
body = PATTERN(insn);
if (GET_CODE(body) != SET ||
GET_CODE(SET_SRC(body)) != SYMBOL_REF)
continue;
sym = XSTR(SET_SRC(body), 0);
if (strcmp(sym, "__stack_chk_guard"))
continue;
/*
* Replace the source of the SET insn with an expression that
* produces the address of the copy of the stack canary value
* stored in struct thread_info
*/
masked_sp = gen_reg_rtx(Pmode);
emit_insn_before(gen_rtx_SET(masked_sp,
gen_rtx_AND(Pmode,
stack_pointer_rtx,
GEN_INT(sp_mask))),
insn);
SET_SRC(body) = gen_rtx_PLUS(Pmode, masked_sp,
GEN_INT(canary_offset));
}
return 0;
}
#define PASS_NAME arm_pertask_ssp_rtl
#define NO_GATE
#include "gcc-generate-rtl-pass.h"
__visible int plugin_init(struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
const char * const plugin_name = plugin_info->base_name;
const int argc = plugin_info->argc;
const struct plugin_argument *argv = plugin_info->argv;
int tso = 0;
int i;
if (!plugin_default_version_check(version, &gcc_version)) {
error(G_("incompatible gcc/plugin versions"));
return 1;
}
for (i = 0; i < argc; ++i) {
if (!strcmp(argv[i].key, "disable"))
return 0;
/* all remaining options require a value */
if (!argv[i].value) {
error(G_("no value supplied for option '-fplugin-arg-%s-%s'"),
plugin_name, argv[i].key);
return 1;
}
if (!strcmp(argv[i].key, "tso")) {
tso = atoi(argv[i].value);
continue;
}
if (!strcmp(argv[i].key, "offset")) {
canary_offset = atoi(argv[i].value);
continue;
}
error(G_("unknown option '-fplugin-arg-%s-%s'"),
plugin_name, argv[i].key);
return 1;
}
/* create the mask that produces the base of the stack */
sp_mask = ~((1U << (12 + tso)) - 1);
PASS_INFO(arm_pertask_ssp_rtl, "expand", 1, PASS_POS_INSERT_AFTER);
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP,
NULL, &arm_pertask_ssp_rtl_pass_info);
return 0;
}