summaryrefslogtreecommitdiffstats
path: root/arch/sh
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh')
-rw-r--r--arch/sh/Kconfig14
-rw-r--r--arch/sh/Makefile4
-rw-r--r--arch/sh/boards/Kconfig4
-rw-r--r--arch/sh/include/asm/stackprotector.h27
-rw-r--r--arch/sh/kernel/process.c7
-rw-r--r--arch/sh/kernel/process_32.c5
-rw-r--r--arch/sh/mm/fault_32.c99
7 files changed, 134 insertions, 26 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index ed2419f..98405fa 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -695,6 +695,20 @@ config SECCOMP
If unsure, say N.
+config CC_STACKPROTECTOR
+ bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
+ depends on SUPERH32 && EXPERIMENTAL
+ help
+ This option turns on the -fstack-protector GCC feature. This
+ feature puts, at the beginning of functions, a canary value on
+ the stack just before the return address, and validates
+ the value just before actually returning. Stack based buffer
+ overflows (that need to overwrite this return address) now also
+ overwrite the canary, which gets detected and the attack is then
+ neutralized via a kernel panic.
+
+ This feature requires gcc version 4.2 or above.
+
config SMP
bool "Symmetric multi-processing support"
depends on SYS_SUPPORTS_SMP
diff --git a/arch/sh/Makefile b/arch/sh/Makefile
index 3fc0f41..24875c8 100644
--- a/arch/sh/Makefile
+++ b/arch/sh/Makefile
@@ -199,6 +199,10 @@ ifeq ($(CONFIG_DWARF_UNWINDER),y)
KBUILD_CFLAGS += -fasynchronous-unwind-tables
endif
+ifeq ($(CONFIG_CC_STACKPROTECTOR),y)
+ KBUILD_CFLAGS += -fstack-protector
+endif
+
libs-$(CONFIG_SUPERH32) := arch/sh/lib/ $(libs-y)
libs-$(CONFIG_SUPERH64) := arch/sh/lib64/ $(libs-y)
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index d893411..6c96daa 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -338,8 +338,6 @@ config SH_APSH4AD0A
help
Select AP-SH4AD-0A if configuring for an ALPHAPROJECT AP-SH4AD-0A.
-endmenu
-
source "arch/sh/boards/mach-r2d/Kconfig"
source "arch/sh/boards/mach-highlander/Kconfig"
source "arch/sh/boards/mach-sdk7780/Kconfig"
@@ -359,3 +357,5 @@ config SH_MAGIC_PANEL_R2_VERSION
endmenu
endif
+
+endmenu
diff --git a/arch/sh/include/asm/stackprotector.h b/arch/sh/include/asm/stackprotector.h
new file mode 100644
index 0000000..d9df3a7
--- /dev/null
+++ b/arch/sh/include/asm/stackprotector.h
@@ -0,0 +1,27 @@
+#ifndef __ASM_SH_STACKPROTECTOR_H
+#define __ASM_SH_STACKPROTECTOR_H
+
+#include <linux/random.h>
+#include <linux/version.h>
+
+extern unsigned long __stack_chk_guard;
+
+/*
+ * Initialize the stackprotector canary value.
+ *
+ * NOTE: this must only be called from functions that never return,
+ * and it must always be inlined.
+ */
+static __always_inline void boot_init_stack_canary(void)
+{
+ unsigned long canary;
+
+ /* Try to get a semi random initial value. */
+ get_random_bytes(&canary, sizeof(canary));
+ canary ^= LINUX_VERSION_CODE;
+
+ current->stack_canary = canary;
+ __stack_chk_guard = current->stack_canary;
+}
+
+#endif /* __ASM_SH_STACKPROTECTOR_H */
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c
index 325f98b..f3f03e4 100644
--- a/arch/sh/kernel/process.c
+++ b/arch/sh/kernel/process.c
@@ -2,10 +2,17 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/export.h>
+#include <linux/stackprotector.h>
struct kmem_cache *task_xstate_cachep = NULL;
unsigned int xstate_size;
+#ifdef CONFIG_CC_STACKPROTECTOR
+unsigned long __stack_chk_guard __read_mostly;
+EXPORT_SYMBOL(__stack_chk_guard);
+#endif
+
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
*dst = *src;
diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c
index 94273aa..f78cc42 100644
--- a/arch/sh/kernel/process_32.c
+++ b/arch/sh/kernel/process_32.c
@@ -22,6 +22,7 @@
#include <linux/ftrace.h>
#include <linux/hw_breakpoint.h>
#include <linux/prefetch.h>
+#include <linux/stackprotector.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/fpu.h>
@@ -220,6 +221,10 @@ __switch_to(struct task_struct *prev, struct task_struct *next)
{
struct thread_struct *next_t = &next->thread;
+#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+ __stack_chk_guard = next->stack_canary;
+#endif
+
unlazy_fpu(prev, task_pt_regs(prev));
/* we're going to use this soon, after a few expensive things */
diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c
index 98e88a6..8ce856d 100644
--- a/arch/sh/mm/fault_32.c
+++ b/arch/sh/mm/fault_32.c
@@ -35,6 +35,74 @@ static inline int notify_page_fault(struct pt_regs *regs, int trap)
return ret;
}
+/*
+ * This is useful to dump out the page tables associated with
+ * 'addr' in mm 'mm'.
+ */
+static void show_pte(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t *pgd;
+
+ if (mm)
+ pgd = mm->pgd;
+ else
+ pgd = get_TTB();
+
+ printk(KERN_ALERT "pgd = %p\n", pgd);
+ pgd += pgd_index(addr);
+ printk(KERN_ALERT "[%08lx] *pgd=%0*Lx", addr,
+ sizeof(*pgd) * 2, (u64)pgd_val(*pgd));
+
+ do {
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (pgd_none(*pgd))
+ break;
+
+ if (pgd_bad(*pgd)) {
+ printk("(bad)");
+ break;
+ }
+
+ pud = pud_offset(pgd, addr);
+ if (PTRS_PER_PUD != 1)
+ printk(", *pud=%0*Lx", sizeof(*pud) * 2,
+ (u64)pud_val(*pud));
+
+ if (pud_none(*pud))
+ break;
+
+ if (pud_bad(*pud)) {
+ printk("(bad)");
+ break;
+ }
+
+ pmd = pmd_offset(pud, addr);
+ if (PTRS_PER_PMD != 1)
+ printk(", *pmd=%0*Lx", sizeof(*pmd) * 2,
+ (u64)pmd_val(*pmd));
+
+ if (pmd_none(*pmd))
+ break;
+
+ if (pmd_bad(*pmd)) {
+ printk("(bad)");
+ break;
+ }
+
+ /* We must not map this if we have highmem enabled */
+ if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT)))
+ break;
+
+ pte = pte_offset_kernel(pmd, addr);
+ printk(", *pte=%0*Lx", sizeof(*pte) * 2, (u64)pte_val(*pte));
+ } while (0);
+
+ printk("\n");
+}
+
static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
{
unsigned index = pgd_index(address);
@@ -86,7 +154,7 @@ static noinline int vmalloc_fault(unsigned long address)
pte_t *pte_k;
/* Make sure we are in vmalloc/module/P3 area: */
- if (!(address >= VMALLOC_START && address < P3_ADDR_MAX))
+ if (!(address >= P3SEG && address < P3_ADDR_MAX))
return -1;
/*
@@ -274,29 +342,12 @@ no_context:
bust_spinlocks(1);
if (oops_may_print()) {
- unsigned long page;
-
- if (address < PAGE_SIZE)
- printk(KERN_ALERT "Unable to handle kernel NULL "
- "pointer dereference");
- else
- printk(KERN_ALERT "Unable to handle kernel paging "
- "request");
- printk(" at virtual address %08lx\n", address);
- printk(KERN_ALERT "pc = %08lx\n", regs->pc);
- page = (unsigned long)get_TTB();
- if (page) {
- page = ((__typeof__(page) *)page)[address >> PGDIR_SHIFT];
- printk(KERN_ALERT "*pde = %08lx\n", page);
- if (page & _PAGE_PRESENT) {
- page &= PAGE_MASK;
- address &= 0x003ff000;
- page = ((__typeof__(page) *)
- __va(page))[address >>
- PAGE_SHIFT];
- printk(KERN_ALERT "*pte = %08lx\n", page);
- }
- }
+ printk(KERN_ALERT
+ "Unable to handle kernel %s at virtual address %08lx\n",
+ (address < PAGE_SIZE) ? "NULL pointer dereference" :
+ "paging request", address);
+
+ show_pte(mm, address);
}
die("Oops", regs, writeaccess);
OpenPOWER on IntegriCloud