2015-08-05 23:49:18

by Leonid Yegoshin

[permalink] [raw]
Subject: [PATCH v4 0/3] MIPS executable stack protection

The following series implements an executable stack protection in MIPS.

It sets up a per-thread 'VDSO' page and appropriate TLB support.
Page is set write-protected from user and is maintained via kernel VA.
MIPS FPU emulation is shifted to new page and stack is relieved for
execute protection as is as all data pages in default setup during ELF
binary initialization. The real protection is controlled by GLIBC and
it can do stack protected now as it is done in other architectures and
I learned today that GLIBC team is ready for this.

Note: actual execute-protection depends from HW capability, of course.

This patch is required for MIPS32/64 R2 emulation on MIPS R6 architecture.
Without it 'ssh-keygen' crashes pretty fast on attempt to execute instruction
in stack.

v2 changes:
- Added an optimization during mmap switch - doesn't switch if the same
thread is rescheduled and other threads don't intervene (Peter Zijlstra)
- Fixed uMIPS support (Paul Burton)
- Added unwinding of VDSO emulation stack at signal handler invocation,
hiding an emulation page (Andy Lutomirski note in other patch comments)

V3 change: heavy preemption friendly.

V4 changes:
- Fixed bug in supplementary TLB flush (change KVA to user address space)
- Rebased to 4.X kernel
---

Leonid Yegoshin (3):
MIPS: mips_flush_cache_range is added
MIPS: Setup an instruction emulation in VDSO protected page instead of user stack
MIPS: set stack/data protection as non-executable


arch/mips/include/asm/cacheflush.h | 3 +
arch/mips/include/asm/fpu_emulator.h | 2
arch/mips/include/asm/mmu.h | 3 +
arch/mips/include/asm/page.h | 2
arch/mips/include/asm/processor.h | 2
arch/mips/include/asm/switch_to.h | 14 +++
arch/mips/include/asm/thread_info.h | 3 +
arch/mips/include/asm/tlbmisc.h | 1
arch/mips/include/asm/vdso.h | 3 +
arch/mips/kernel/mips-r2-to-r6-emul.c | 10 +-
arch/mips/kernel/process.c | 7 ++
arch/mips/kernel/signal.c | 4 +
arch/mips/kernel/vdso.c | 44 +++++++++
arch/mips/math-emu/cp1emu.c | 8 +-
arch/mips/math-emu/dsemul.c | 154 +++++++++++++++++++++++++++------
arch/mips/mm/c-octeon.c | 8 ++
arch/mips/mm/c-r3k.c | 8 ++
arch/mips/mm/c-r4k.c | 43 +++++++++
arch/mips/mm/c-tx39.c | 9 ++
arch/mips/mm/cache.c | 4 +
arch/mips/mm/fault.c | 5 +
arch/mips/mm/tlb-r4k.c | 42 +++++++++
22 files changed, 343 insertions(+), 36 deletions(-)

--
Signature


2015-08-05 23:49:28

by Leonid Yegoshin

[permalink] [raw]
Subject: [PATCH v4 1/3] MIPS: mips_flush_cache_range is added

New function mips_flush_cache_range() is added.
It flushes D-cache on kernel VA and I-cache on user VA.
It is significant in case of cache aliasing systems.
It can be used to flush a short sequence of newly written code
to user space and especially usefull in ptrace() and dsemul().
Today a full page is flushed by flush_cache_page in ptrace().

Signed-off-by: Leonid Yegoshin <[email protected]>
---
arch/mips/include/asm/cacheflush.h | 3 +++
arch/mips/mm/c-octeon.c | 8 +++++++
arch/mips/mm/c-r3k.c | 8 +++++++
arch/mips/mm/c-r4k.c | 43 ++++++++++++++++++++++++++++++++++++
arch/mips/mm/c-tx39.c | 9 ++++++++
arch/mips/mm/cache.c | 4 +++
6 files changed, 75 insertions(+)

diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
index 723229f4cf27..d47bbf129a45 100644
--- a/arch/mips/include/asm/cacheflush.h
+++ b/arch/mips/include/asm/cacheflush.h
@@ -115,6 +115,9 @@ extern void (*flush_cache_sigtramp)(unsigned long addr);
extern void (*flush_icache_all)(void);
extern void (*local_flush_data_cache_page)(void * addr);
extern void (*flush_data_cache_page)(unsigned long addr);
+extern void (*mips_flush_data_cache_range)(struct vm_area_struct *vma,
+ unsigned long vaddr, struct page *page, unsigned long addr,
+ unsigned long size);

/* Run kernel code uncached, useful for cache probing functions. */
unsigned long run_uncached(void *func);
diff --git a/arch/mips/mm/c-octeon.c b/arch/mips/mm/c-octeon.c
index 05b1d7cf9514..38349d177570 100644
--- a/arch/mips/mm/c-octeon.c
+++ b/arch/mips/mm/c-octeon.c
@@ -178,6 +178,13 @@ static void octeon_flush_kernel_vmap_range(unsigned long vaddr, int size)
BUG();
}

+static void octeon_flush_data_cache_range(struct vm_area_struct *vma,
+ unsigned long vaddr, struct page *page, unsigned long addr,
+ unsigned long size)
+{
+ octeon_flush_cache_page(vma, addr, page_to_pfn(page));
+}
+
/**
* Probe Octeon's caches
*
@@ -292,6 +299,7 @@ void octeon_cache_init(void)
flush_cache_sigtramp = octeon_flush_cache_sigtramp;
flush_icache_all = octeon_flush_icache_all;
flush_data_cache_page = octeon_flush_data_cache_page;
+ mips_flush_data_cache_range = octeon_flush_data_cache_range;
flush_icache_range = octeon_flush_icache_range;
local_flush_icache_range = local_octeon_flush_icache_range;

diff --git a/arch/mips/mm/c-r3k.c b/arch/mips/mm/c-r3k.c
index 135ec313c1f6..93b481046619 100644
--- a/arch/mips/mm/c-r3k.c
+++ b/arch/mips/mm/c-r3k.c
@@ -273,6 +273,13 @@ static void r3k_flush_data_cache_page(unsigned long addr)
{
}

+static void r3k_mips_flush_data_cache_range(struct vm_area_struct *vma,
+ unsigned long vaddr, struct page *page, unsigned long addr,
+ unsigned long size)
+{
+ r3k_flush_cache_page(vma, addr, page_to_pfn(page));
+}
+
static void r3k_flush_cache_sigtramp(unsigned long addr)
{
unsigned long flags;
@@ -322,6 +329,7 @@ void r3k_cache_init(void)
__flush_cache_all = r3k___flush_cache_all;
flush_cache_mm = r3k_flush_cache_mm;
flush_cache_range = r3k_flush_cache_range;
+ mips_flush_data_cache_range = r3k_mips_flush_data_cache_range;
flush_cache_page = r3k_flush_cache_page;
flush_icache_range = r3k_flush_icache_range;
local_flush_icache_range = r3k_flush_icache_range;
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 5d3a25e1cfae..164770a5c84c 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -645,6 +645,48 @@ static void r4k_flush_data_cache_page(unsigned long addr)
r4k_on_each_cpu(local_r4k_flush_data_cache_page, (void *) addr);
}

+struct mips_flush_data_cache_range_args {
+ struct vm_area_struct *vma;
+ unsigned long vaddr;
+ unsigned long start;
+ unsigned long len;
+};
+
+static inline void local_r4k_mips_flush_data_cache_range(void *args)
+{
+ struct mips_flush_data_cache_range_args *f_args = args;
+ unsigned long vaddr = f_args->vaddr;
+ unsigned long start = f_args->start;
+ unsigned long len = f_args->len;
+ struct vm_area_struct * vma = f_args->vma;
+
+ blast_dcache_range(start, start + len);
+
+ if ((vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc) {
+ wmb();
+
+ /* vma is given for exec check only, mmap is current,
+ so - no non-current vma page flush, just user or kernel */
+ protected_blast_icache_range(vaddr, vaddr + len);
+ }
+}
+
+/* flush dirty kernel data and a corresponding user instructions (if needed).
+ used in copy_to_user_page() */
+static void r4k_mips_flush_data_cache_range(struct vm_area_struct *vma,
+ unsigned long vaddr, struct page *page, unsigned long start,
+ unsigned long len)
+{
+ struct mips_flush_data_cache_range_args args;
+
+ args.vma = vma;
+ args.vaddr = vaddr;
+ args.start = start;
+ args.len = len;
+
+ r4k_on_each_cpu(local_r4k_mips_flush_data_cache_range, (void *)&args);
+}
+
struct flush_icache_range_args {
unsigned long start;
unsigned long end;
@@ -1692,6 +1734,7 @@ void r4k_cache_init(void)
flush_icache_all = r4k_flush_icache_all;
local_flush_data_cache_page = local_r4k_flush_data_cache_page;
flush_data_cache_page = r4k_flush_data_cache_page;
+ mips_flush_data_cache_range = r4k_mips_flush_data_cache_range;
flush_icache_range = r4k_flush_icache_range;
local_flush_icache_range = local_r4k_flush_icache_range;

diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c
index 596e18458e04..3b9369eb01de 100644
--- a/arch/mips/mm/c-tx39.c
+++ b/arch/mips/mm/c-tx39.c
@@ -228,6 +228,13 @@ static void tx39_flush_data_cache_page(unsigned long addr)
tx39_blast_dcache_page(addr);
}

+static void local_flush_data_cache_range(struct vm_area_struct *vma,
+ unsigned long vaddr, struct page *page, unsigned long addr,
+ unsigned long size)
+{
+ flush_cache_page(vma, addr, page_to_pfn(page));
+}
+
static void tx39_flush_icache_range(unsigned long start, unsigned long end)
{
if (end - start > dcache_size)
@@ -369,6 +376,7 @@ void tx39_cache_init(void)

flush_cache_sigtramp = (void *) tx39h_flush_icache_all;
local_flush_data_cache_page = (void *) tx39h_flush_icache_all;
+ mips_flush_data_cache_range = (void *) local_flush_data_cache_range;
flush_data_cache_page = (void *) tx39h_flush_icache_all;

_dma_cache_wback_inv = tx39h_dma_cache_wback_inv;
@@ -398,6 +406,7 @@ void tx39_cache_init(void)

flush_cache_sigtramp = tx39_flush_cache_sigtramp;
local_flush_data_cache_page = local_tx39_flush_data_cache_page;
+ mips_flush_data_cache_range = (void *) local_flush_data_cache_range;
flush_data_cache_page = tx39_flush_data_cache_page;

_dma_cache_wback_inv = tx39_dma_cache_wback_inv;
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index aab218c36e0d..e25500e62e0e 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -44,10 +44,14 @@ void (*__invalidate_kernel_vmap_range)(unsigned long vaddr, int size);
void (*flush_cache_sigtramp)(unsigned long addr);
void (*local_flush_data_cache_page)(void * addr);
void (*flush_data_cache_page)(unsigned long addr);
+void (*mips_flush_data_cache_range)(struct vm_area_struct *vma,
+ unsigned long vaddr, struct page *page, unsigned long addr,
+ unsigned long size);
void (*flush_icache_all)(void);

EXPORT_SYMBOL_GPL(local_flush_data_cache_page);
EXPORT_SYMBOL(flush_data_cache_page);
+EXPORT_SYMBOL(mips_flush_data_cache_range);
EXPORT_SYMBOL(flush_icache_all);

#if defined(CONFIG_DMA_NONCOHERENT) || defined(CONFIG_DMA_MAYBE_COHERENT)

2015-08-05 23:49:38

by Leonid Yegoshin

[permalink] [raw]
Subject: [PATCH v4 2/3] MIPS: Setup an instruction emulation in VDSO protected page instead of user stack

Historically, during FPU emulation MIPS runs live BD-slot instruction in stack.
This is needed because it was the only way to correctly handle branch
exceptions with unknown COP2 or ASE instructions in BD-slot. Now there is
an eXecuteInhibit feature and it is desirable to protect stack from execution
for security reasons.
This patch moves FPU emulation from stack area to VDSO-located page which is set
write-protected for application access. VDSO page itself is now per-thread and
it's addresses and offsets are stored in thread_info.
Small stack of emulation blocks is supported because nested traps are possible
in MIPS32/64 R6 emulation mix with FPU emulation.
Signal happend during run in emulation block is handled properly - EPC is
changed to before an emulated jump or to target address, depending from point of
signal.

Explanation of problem (current state before patch):

If I set eXecute-disabled stack in ELF binary initialisation then GLIBC ignores
it and may change stack protection at library load. If this library has
eXecute-disabled stack then anything is OK, but if this section (PT_GNU_STACK)
is just missed as usual, then GLIBC applies it's own default == eXecute-enabled
stack.
So, ssh_keygen is built explicitly with eXecute-disabled stack. But GLIBC
ignores it and set stack executable. And because of that - anything works,
FPU emulation and hacker tools.
However, if I use all *.SO libraries with eXecute-disabled stack in PT_GNU_STACK
section then GLIBC keeps stack non-executable but things fails at FPU emulation
later.

Here are two issues which are bind together and to solve an incorrect
behaviour of GLIBC (ignoring X ssh-keygen intention) the split of both issues
is needed. So, I did a kernel emulation protected and out of stack.

Signed-off-by: Leonid Yegoshin <[email protected]>
---
arch/mips/include/asm/fpu_emulator.h | 2
arch/mips/include/asm/mmu.h | 3 +
arch/mips/include/asm/processor.h | 2
arch/mips/include/asm/switch_to.h | 14 +++
arch/mips/include/asm/thread_info.h | 3 +
arch/mips/include/asm/tlbmisc.h | 1
arch/mips/include/asm/vdso.h | 3 +
arch/mips/kernel/mips-r2-to-r6-emul.c | 10 +-
arch/mips/kernel/process.c | 7 ++
arch/mips/kernel/signal.c | 4 +
arch/mips/kernel/vdso.c | 44 +++++++++
arch/mips/math-emu/cp1emu.c | 8 +-
arch/mips/math-emu/dsemul.c | 154 +++++++++++++++++++++++++++------
arch/mips/mm/fault.c | 5 +
arch/mips/mm/tlb-r4k.c | 42 +++++++++
15 files changed, 267 insertions(+), 35 deletions(-)

diff --git a/arch/mips/include/asm/fpu_emulator.h b/arch/mips/include/asm/fpu_emulator.h
index 2f021cdfba4f..0c8d1191fa44 100644
--- a/arch/mips/include/asm/fpu_emulator.h
+++ b/arch/mips/include/asm/fpu_emulator.h
@@ -61,7 +61,7 @@ do { \
#endif /* CONFIG_DEBUG_FS */

extern int mips_dsemul(struct pt_regs *regs, mips_instruction ir,
- unsigned long cpc);
+ unsigned long cpc, unsigned long bpc, unsigned long r31);
extern int do_dsemulret(struct pt_regs *xcp);
extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
struct mips_fpu_struct *ctx, int has_fpu,
diff --git a/arch/mips/include/asm/mmu.h b/arch/mips/include/asm/mmu.h
index 1afa1f986df8..8d2ce4d34005 100644
--- a/arch/mips/include/asm/mmu.h
+++ b/arch/mips/include/asm/mmu.h
@@ -5,7 +5,10 @@

typedef struct {
unsigned long asid[NR_CPUS];
+ unsigned long vdso_asid[NR_CPUS];
+ struct page *vdso_page[NR_CPUS];
void *vdso;
+ struct vm_area_struct *vdso_vma;
atomic_t fp_mode_switching;
} mm_context_t;

diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index 59ee6dcf6eed..cfe2e686bf9e 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -40,7 +40,7 @@ extern unsigned int vced_count, vcei_count;
* A special page (the vdso) is mapped into all processes at the very
* top of the virtual memory space.
*/
-#define SPECIAL_PAGES_SIZE PAGE_SIZE
+#define SPECIAL_PAGES_SIZE (PAGE_SIZE * 2)

#ifdef CONFIG_32BIT
#ifdef CONFIG_KVM_GUEST
diff --git a/arch/mips/include/asm/switch_to.h b/arch/mips/include/asm/switch_to.h
index 9733cd0266e4..d78469c68eef 100644
--- a/arch/mips/include/asm/switch_to.h
+++ b/arch/mips/include/asm/switch_to.h
@@ -17,6 +17,7 @@
#include <asm/dsp.h>
#include <asm/cop2.h>
#include <asm/msa.h>
+#include <asm/mmu_context.h>

struct task_struct;

@@ -83,6 +84,18 @@ do { if (cpu_has_rw_llb) { \
} \
} while (0)

+static inline void flush_vdso_page(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (current->mm && cpu_context(cpu, current->mm) &&
+ (current->mm->context.vdso_page[cpu] != current_thread_info()->vdso_page) &&
+ (current->mm->context.vdso_asid[cpu] == cpu_asid(cpu, current->mm))) {
+ local_flush_tlb_page(current->mm->mmap, (unsigned long)current->mm->context.vdso);
+ current->mm->context.vdso_asid[cpu] = 0;
+ }
+}
+
/*
* For newly created kernel threads switch_to() will return to
* ret_from_kernel_thread, newly created user threads to ret_from_fork.
@@ -117,6 +130,7 @@ do { \
__fpsave = FP_SAVE_VECTOR; \
if (cpu_has_userlocal) \
write_c0_userlocal(task_thread_info(next)->tp_value); \
+ flush_vdso_page(); \
__restore_watch(); \
disable_msa(); \
(last) = resume(prev, next, task_thread_info(next), __fpsave); \
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index e309d8fcb516..e449b4ddb14f 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -33,6 +33,8 @@ struct thread_info {
* 0x7fffffff for user-thead
* 0xffffffff for kernel-thread
*/
+ unsigned long vdso_offset;
+ struct page *vdso_page;
struct pt_regs *regs;
long syscall; /* syscall number */
};
@@ -47,6 +49,7 @@ struct thread_info {
.cpu = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
+ .vdso_page = NULL, \
}

#define init_thread_info (init_thread_union.thread_info)
diff --git a/arch/mips/include/asm/tlbmisc.h b/arch/mips/include/asm/tlbmisc.h
index 3a452282cba0..abd7bf6ac2c6 100644
--- a/arch/mips/include/asm/tlbmisc.h
+++ b/arch/mips/include/asm/tlbmisc.h
@@ -6,5 +6,6 @@
*/
extern void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
unsigned long entryhi, unsigned long pagemask);
+int install_vdso_tlb(void);

#endif /* __ASM_TLBMISC_H */
diff --git a/arch/mips/include/asm/vdso.h b/arch/mips/include/asm/vdso.h
index cca56aa40ff4..77056fc38df6 100644
--- a/arch/mips/include/asm/vdso.h
+++ b/arch/mips/include/asm/vdso.h
@@ -11,6 +11,9 @@

#include <linux/types.h>

+void mips_thread_vdso(struct thread_info *ti);
+void arch_release_thread_info(struct thread_info *info);
+void vdso_epc_adjust(struct pt_regs *xcp);

#ifdef CONFIG_32BIT
struct mips_vdso {
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index f2977f00911b..040842a3aec4 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -245,6 +245,7 @@ static int jr_func(struct pt_regs *regs, u32 ir)
{
int err;
unsigned long cepc, epc, nepc;
+ unsigned long r31;
u32 nir;

if (delay_slot(regs))
@@ -255,6 +256,7 @@ static int jr_func(struct pt_regs *regs, u32 ir)
/* Roll back to the reserved R2 JR instruction */
regs->cp0_epc -= 4;
epc = regs->cp0_epc;
+ r31 = regs->regs[31];
err = __compute_return_epc(regs);

if (err < 0)
@@ -281,7 +283,7 @@ static int jr_func(struct pt_regs *regs, u32 ir)
err = mipsr6_emul(regs, nir);
if (err > 0) {
regs->cp0_epc = nepc;
- err = mips_dsemul(regs, nir, cepc);
+ err = mips_dsemul(regs, nir, cepc, epc, r31);
if (err == SIGILL)
err = SIGEMT;
MIPS_R2_STATS(dsemul);
@@ -1031,7 +1033,7 @@ repeat:
if (nir) {
err = mipsr6_emul(regs, nir);
if (err > 0) {
- err = mips_dsemul(regs, nir, cpc);
+ err = mips_dsemul(regs, nir, cpc, epc, r31);
if (err == SIGILL)
err = SIGEMT;
MIPS_R2_STATS(dsemul);
@@ -1080,7 +1082,7 @@ repeat:
if (nir) {
err = mipsr6_emul(regs, nir);
if (err > 0) {
- err = mips_dsemul(regs, nir, cpc);
+ err = mips_dsemul(regs, nir, cpc, epc, r31);
if (err == SIGILL)
err = SIGEMT;
MIPS_R2_STATS(dsemul);
@@ -1147,7 +1149,7 @@ repeat:
if (nir) {
err = mipsr6_emul(regs, nir);
if (err > 0) {
- err = mips_dsemul(regs, nir, cpc);
+ err = mips_dsemul(regs, nir, cpc, epc, r31);
if (err == SIGILL)
err = SIGEMT;
MIPS_R2_STATS(dsemul);
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index f2975d4d1e44..cbbb4430cf1d 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -44,6 +44,7 @@
#include <asm/inst.h>
#include <asm/stacktrace.h>
#include <asm/irq_regs.h>
+#include <asm/vdso.h>

#ifdef CONFIG_HOTPLUG_CPU
void arch_cpu_idle_dead(void)
@@ -61,6 +62,8 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
{
unsigned long status;

+ mips_thread_vdso(current_thread_info());
+
/* New thread loses kernel privileges. */
status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|ST0_FR|KU_MASK);
status |= KU_USER;
@@ -77,6 +80,7 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)

void exit_thread(void)
{
+ arch_release_thread_info(current_thread_info());
}

void flush_thread(void)
@@ -120,6 +124,9 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,

childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32;

+ ti->vdso_page = NULL;
+ mips_thread_vdso(ti);
+
/* set up new TSS. */
childregs = (struct pt_regs *) childksp - 1;
/* Put the stack after the struct pt_regs. */
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index 2fec67bfc457..e88e164da5bb 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -800,6 +800,10 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
regs->regs[0] = 0; /* Don't deal with this again. */
}

+ /* adjust emulation stack if signal happens during emulation */
+ if (current_thread_info()->vdso_page)
+ vdso_epc_adjust(regs);
+
if (sig_uses_siginfo(&ksig->ka))
ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset,
ksig, regs, oldset);
diff --git a/arch/mips/kernel/vdso.c b/arch/mips/kernel/vdso.c
index ed2a278722a9..ecc884e051a4 100644
--- a/arch/mips/kernel/vdso.c
+++ b/arch/mips/kernel/vdso.c
@@ -21,6 +21,8 @@
#include <asm/vdso.h>
#include <asm/uasm.h>
#include <asm/processor.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>

/*
* Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
@@ -101,14 +103,18 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)

ret = install_special_mapping(mm, addr, PAGE_SIZE,
VM_READ|VM_EXEC|
- VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
+ VM_MAYREAD|VM_MAYEXEC,
&vdso_page);

if (ret)
goto up_fail;

mm->context.vdso = (void *)addr;
+ /* if cache aliasing - use a different cache flush later */
+ if (cpu_has_rixi && cpu_has_dc_aliases)
+ mm->context.vdso_vma = find_vma(mm,addr);

+ mips_thread_vdso(current_thread_info());
up_fail:
up_write(&mm->mmap_sem);
return ret;
@@ -120,3 +126,39 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return "[vdso]";
return NULL;
}
+
+void mips_thread_vdso(struct thread_info *ti)
+{
+ struct page *vdso;
+ unsigned long addr;
+
+ if (cpu_has_rixi && ti->task->mm && !ti->vdso_page) {
+ vdso = alloc_page(GFP_USER);
+ if (!vdso)
+ return;
+ ti->vdso_page = vdso;
+ ti->vdso_offset = PAGE_SIZE;
+ addr = (unsigned long)page_address(vdso);
+ copy_page((void *)addr, (void *)page_address(vdso_page));
+ if (!cpu_has_ic_fills_f_dc)
+ flush_data_cache_page(addr);
+ /* any vma in mmap is used, just to get ASIDs back from mm */
+ local_flush_tlb_page(ti->task->mm->mmap,
+ (unsigned long)ti->task->mm->context.vdso);
+ }
+}
+
+void arch_release_thread_info(struct thread_info *info)
+{
+ if (info->vdso_page) {
+ if (info->task->mm) {
+ preempt_disable();
+ /* any vma in mmap is used, just to get ASIDs */
+ local_flush_tlb_page(info->task->mm->mmap,(unsigned long)info->task->mm->context.vdso);
+ info->task->mm->context.vdso_asid[smp_processor_id()] = 0;
+ preempt_enable();
+ }
+ __free_page(info->vdso_page);
+ info->vdso_page = NULL;
+ }
+}
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index 8a5b0eb4ddef..ba91d4d01406 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -973,6 +973,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
struct mm_decoded_insn dec_insn, void *__user *fault_addr)
{
unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc;
+ unsigned long r31;
+ unsigned long s_epc;
unsigned int cond, cbit;
mips_instruction ir;
int likely, pc_inc;
@@ -989,6 +991,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
if (!cpu_has_mmips && dec_insn.micro_mips_mode)
unreachable();

+ s_epc = xcp->cp0_epc;
+ r31 = xcp->regs[31];
/* XXX NEC Vr54xx bug workaround */
if (delay_slot(xcp)) {
if (dec_insn.micro_mips_mode) {
@@ -1265,7 +1269,7 @@ branch_common:
* instruction in the dslot.
*/
sig = mips_dsemul(xcp, ir,
- contpc);
+ contpc, s_epc, r31);
if (sig)
xcp->cp0_epc = bcpc;
/*
@@ -1318,7 +1322,7 @@ branch_common:
* Single step the non-cp1
* instruction in the dslot
*/
- sig = mips_dsemul(xcp, ir, contpc);
+ sig = mips_dsemul(xcp, ir, contpc, s_epc, r31);
if (sig)
xcp->cp0_epc = bcpc;
/* SIGILL forces out of the emulation loop. */
diff --git a/arch/mips/math-emu/dsemul.c b/arch/mips/math-emu/dsemul.c
index e0b5cc27d78b..eac76a09d822 100644
--- a/arch/mips/math-emu/dsemul.c
+++ b/arch/mips/math-emu/dsemul.c
@@ -3,6 +3,7 @@
#include <asm/fpu_emulator.h>
#include <asm/inst.h>
#include <asm/mipsregs.h>
+#include <asm/vdso.h>
#include <asm/uaccess.h>

#include "ieee754.h"
@@ -29,13 +30,19 @@ struct emuframe {
mips_instruction badinst;
mips_instruction cookie;
unsigned long epc;
+ unsigned long bpc;
+ unsigned long r31;
};
+/* round structure size to N*8 to force a fit two instructions in a single cache line */
+#define EMULFRAME_ROUNDED_SIZE ((sizeof(struct emuframe) + 0x7) & ~0x7)

-int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
+int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc,
+ unsigned long bpc, unsigned long r31)
{
extern asmlinkage void handle_dsemulret(void);
struct emuframe __user *fr;
int err;
+ unsigned char *pg_addr;

if ((get_isa16_mode(regs->cp0_epc) && ((ir >> 16) == MM_NOP16)) ||
(ir == 0)) {
@@ -48,7 +55,7 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);

/*
- * The strategy is to push the instruction onto the user stack
+ * The strategy is to push the instruction onto the user stack/VDSO page
* and put a trap after it which we can catch and jump to
* the required address any alternative apart from full
* instruction emulation!!.
@@ -65,36 +72,81 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
* handler (single entry point).
*/

- /* Ensure that the two instructions are in the same cache line */
- fr = (struct emuframe __user *)
- ((regs->regs[29] - sizeof(struct emuframe)) & ~0x7);
+ if (current_thread_info()->vdso_page) {
+ /*
+ * Use VDSO page and fill structure via kernel VA,
+ * user write is disabled
+ */
+ pg_addr = (unsigned char *)page_address(current_thread_info()->vdso_page);
+ fr = (struct emuframe __user *)
+ (pg_addr + current_thread_info()->vdso_offset -
+ EMULFRAME_ROUNDED_SIZE);
+
+ /* verify that we don't overflow into trampoline areas */
+ if ((unsigned char *)fr < (unsigned char *)(((struct mips_vdso *)pg_addr) + 1)) {
+ MIPS_FPU_EMU_INC_STATS(errors);
+ return SIGBUS;
+ }
+
+ current_thread_info()->vdso_offset -= EMULFRAME_ROUNDED_SIZE;

- /* Verify that the stack pointer is not competely insane */
- if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe))))
- return SIGBUS;
+ if (get_isa16_mode(regs->cp0_epc)) {
+ *(u16 *)&fr->emul = (u16)(ir >> 16);
+ *((u16 *)(&fr->emul) + 1) = (u16)(ir & 0xffff);
+ *((u16 *)(&fr->emul) + 2) = (u16)(BREAK_MATH >> 16);
+ *((u16 *)(&fr->emul) + 3) = (u16)(BREAK_MATH &0xffff);
+ } else {
+ fr->emul = ir;
+ fr->badinst = (mips_instruction)BREAK_MATH;
+ }
+ fr->cookie = (mips_instruction)BD_COOKIE;
+ fr->epc = cpc;
+ fr->bpc = bpc;
+ fr->r31 = r31;

- if (get_isa16_mode(regs->cp0_epc)) {
- err = __put_user(ir >> 16, (u16 __user *)(&fr->emul));
- err |= __put_user(ir & 0xffff, (u16 __user *)((long)(&fr->emul) + 2));
- err |= __put_user(BREAK_MATH >> 16, (u16 __user *)(&fr->badinst));
- err |= __put_user(BREAK_MATH & 0xffff, (u16 __user *)((long)(&fr->badinst) + 2));
+ /* fill CP0_EPC with user VA */
+ regs->cp0_epc = ((unsigned long)(current->mm->context.vdso +
+ current_thread_info()->vdso_offset)) |
+ get_isa16_mode(regs->cp0_epc);
+ if (cpu_has_dc_aliases)
+ mips_flush_data_cache_range(current->mm->context.vdso_vma,
+ regs->cp0_epc, current_thread_info()->vdso_page,
+ (unsigned long)fr, sizeof(struct emuframe));
+ else
+ /* it is a less expensive on CPU with correct SYNCI */
+ flush_cache_sigtramp((unsigned long)fr);
} else {
- err = __put_user(ir, &fr->emul);
- err |= __put_user((mips_instruction)BREAK_MATH, &fr->badinst);
- }
+ /* Ensure that the two instructions are in the same cache line */
+ fr = (struct emuframe __user *)
+ ((regs->regs[29] - sizeof(struct emuframe)) & ~0x7);

- err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie);
- err |= __put_user(cpc, &fr->epc);
+ /* Verify that the stack pointer is not competely insane */
+ if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe))))
+ return SIGBUS;

- if (unlikely(err)) {
- MIPS_FPU_EMU_INC_STATS(errors);
- return SIGBUS;
- }
+ if (get_isa16_mode(regs->cp0_epc)) {
+ err = __put_user(ir >> 16, (u16 __user *)(&fr->emul));
+ err |= __put_user(ir & 0xffff, (u16 __user *)((long)(&fr->emul) + 2));
+ err |= __put_user(BREAK_MATH >> 16, (u16 __user *)(&fr->badinst));
+ err |= __put_user(BREAK_MATH & 0xffff, (u16 __user *)((long)(&fr->badinst) + 2));
+ } else {
+ err = __put_user(ir, &fr->emul);
+ err |= __put_user((mips_instruction)BREAK_MATH, &fr->badinst);
+ }

- regs->cp0_epc = ((unsigned long) &fr->emul) |
- get_isa16_mode(regs->cp0_epc);
+ err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie);
+ err |= __put_user(cpc, &fr->epc);

- flush_cache_sigtramp((unsigned long)&fr->emul);
+ if (unlikely(err)) {
+ MIPS_FPU_EMU_INC_STATS(errors);
+ return SIGBUS;
+ }
+
+ regs->cp0_epc = ((unsigned long) &fr->emul) |
+ get_isa16_mode(regs->cp0_epc);
+
+ flush_cache_sigtramp((unsigned long)&fr->emul);
+ }

return 0;
}
@@ -132,7 +184,10 @@ int do_dsemulret(struct pt_regs *xcp)
}
err |= __get_user(cookie, &fr->cookie);

- if (unlikely(err || (insn != BREAK_MATH) || (cookie != BD_COOKIE))) {
+ if (unlikely(err || (insn != BREAK_MATH) || (cookie != BD_COOKIE) ||
+ (current_thread_info()->vdso_page &&
+ ((xcp->cp0_epc & PAGE_MASK) !=
+ (unsigned long)current->mm->context.vdso)))) {
MIPS_FPU_EMU_INC_STATS(errors);
return 0;
}
@@ -156,8 +211,55 @@ int do_dsemulret(struct pt_regs *xcp)
return 0;
}

+ if (current_thread_info()->vdso_page) {
+ /* restore VDSO stack level */
+ current_thread_info()->vdso_offset += EMULFRAME_ROUNDED_SIZE;
+ if (current_thread_info()->vdso_offset > PAGE_SIZE) {
+ /* This is not a good situation to be in */
+ current_thread_info()->vdso_offset -= EMULFRAME_ROUNDED_SIZE;
+ force_sig(SIGBUS, current);
+
+ return 0;
+ }
+ }
+
/* Set EPC to return to post-branch instruction */
xcp->cp0_epc = epc;
MIPS_FPU_EMU_INC_STATS(ds_emul);
return 1;
}
+
+/* check and adjust an emulation stack before start a signal handler */
+void vdso_epc_adjust(struct pt_regs *xcp)
+{
+ struct emuframe __user *fr;
+ unsigned long epc;
+ unsigned long r31;
+
+ while (current_thread_info()->vdso_offset < PAGE_SIZE) {
+ epc = msk_isa16_mode(xcp->cp0_epc);
+ if ((epc >= ((unsigned long)current->mm->context.vdso + PAGE_SIZE)) ||
+ (epc < (unsigned long)((struct mips_vdso *)current->mm->context.vdso + 1)))
+ return;
+
+ fr = (struct emuframe __user *)
+ ((unsigned long)current->mm->context.vdso +
+ current_thread_info()->vdso_offset);
+
+ /*
+ * epc must point to emul instruction or badinst
+ * in case of emul - it is not executed, so return to start
+ * and restore GPR31
+ * in case of badinst - instruction is executed, return to destination
+ */
+ if (epc == (unsigned long)&fr->emul) {
+ __get_user(r31, &fr->r31);
+ xcp->regs[31] = r31;
+ __get_user(epc, &fr->bpc);
+ } else {
+ __get_user(epc, &fr->epc);
+ }
+ xcp->cp0_epc = epc;
+ current_thread_info()->vdso_offset += EMULFRAME_ROUNDED_SIZE;
+ }
+}
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 4b88fa031891..cdbf0cefe997 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -27,6 +27,7 @@
#include <asm/mmu_context.h>
#include <asm/ptrace.h>
#include <asm/highmem.h> /* For VMALLOC_END */
+#include <asm/tlbmisc.h>
#include <linux/kdebug.h>

int show_unhandled_signals = 1;
@@ -142,6 +143,9 @@ good_area:
#endif
goto bad_area;
}
+ if (((address & PAGE_MASK) == (unsigned long)(mm->context.vdso)) &&
+ install_vdso_tlb())
+ goto up_return;
} else {
if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
goto bad_area;
@@ -192,6 +196,7 @@ good_area:
}
}

+up_return:
up_read(&mm->mmap_sem);
return;

diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c
index 5037d5868cef..9e3f9d3235c7 100644
--- a/arch/mips/mm/tlb-r4k.c
+++ b/arch/mips/mm/tlb-r4k.c
@@ -360,6 +360,48 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
local_irq_restore(flags);
}

+int install_vdso_tlb(void)
+{
+ int tlbidx;
+ int cpu;
+ unsigned long flags;
+
+ if (!current_thread_info()->vdso_page)
+ return(0);
+
+ local_irq_save(flags);
+ cpu = smp_processor_id();
+ write_c0_entryhi(((unsigned long)current->mm->context.vdso & (PAGE_MASK << 1)) |
+ cpu_asid(cpu, current->mm));
+
+ mtc0_tlbw_hazard();
+ tlb_probe();
+ tlb_probe_hazard();
+ tlbidx = read_c0_index();
+#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32)
+ write_c0_entrylo0(pte_val(pfn_pte(
+ page_to_pfn(current_thread_info()->vdso_page),
+ __pgprot(_page_cachable_default|_PAGE_VALID)))>>32);
+#else
+ write_c0_entrylo0(pte_to_entrylo(pte_val(pfn_pte(
+ page_to_pfn(current_thread_info()->vdso_page),
+ __pgprot(_page_cachable_default|_PAGE_VALID)))));
+#endif
+ write_c0_entrylo1(0);
+ mtc0_tlbw_hazard();
+ if (tlbidx < 0)
+ tlb_write_random();
+ else
+ tlb_write_indexed();
+ tlbw_use_hazard();
+
+ current->mm->context.vdso_asid[cpu] = cpu_asid(cpu, current->mm);
+ current->mm->context.vdso_page[cpu] = current_thread_info()->vdso_page;
+ local_irq_restore(flags);
+
+ return(1);
+}
+
void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
unsigned long entryhi, unsigned long pagemask)
{

2015-08-05 23:49:49

by Leonid Yegoshin

[permalink] [raw]
Subject: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

This is a last step of 3 patches which shift FPU emulation out of
stack into protected area. So, it disables a default executable stack.

Additionally, it sets a default data area non-executable protection.

Signed-off-by: Leonid Yegoshin <[email protected]>
---
arch/mips/include/asm/page.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
index 89dd7fed1a57..0b6cec4a1b80 100644
--- a/arch/mips/include/asm/page.h
+++ b/arch/mips/include/asm/page.h
@@ -228,7 +228,7 @@ extern int __virt_addr_valid(const volatile void *kaddr);
#define virt_addr_valid(kaddr) \
__virt_addr_valid((const volatile void *) (kaddr))

-#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
+#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)

#define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE)

2015-08-05 23:55:49

by Paul Burton

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On Wed, Aug 05, 2015 at 04:49:36PM -0700, Leonid Yegoshin wrote:
> This is a last step of 3 patches which shift FPU emulation out of
> stack into protected area. So, it disables a default executable stack.
>
> Additionally, it sets a default data area non-executable protection.
>
> Signed-off-by: Leonid Yegoshin <[email protected]>
> ---
> arch/mips/include/asm/page.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
> index 89dd7fed1a57..0b6cec4a1b80 100644
> --- a/arch/mips/include/asm/page.h
> +++ b/arch/mips/include/asm/page.h
> @@ -228,7 +228,7 @@ extern int __virt_addr_valid(const volatile void *kaddr);
> #define virt_addr_valid(kaddr) \
> __virt_addr_valid((const volatile void *) (kaddr))
>
> -#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
> +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
> VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
>
> #define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE)
>

Hi Leonid,

As was pointed out last time you posted this, it breaks backwards
compatibility with userland & thus cannot be applied. We should only be
changing executability of memory in the presence of a PT_GNU_STACK
header indicating that it's safe to do so, with cooperation from the
toolchain team to begin emitting it for MIPS. See the way ARM did it, or
the patches I've posted for this in the past.

Thanks,
Paul

2015-08-05 23:56:09

by David Daney

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 04:49 PM, Leonid Yegoshin wrote:
> This is a last step of 3 patches which shift FPU emulation out of
> stack into protected area. So, it disables a default executable stack.
>

NAK.

You cannot change the default.

If you want a non-executable stack, the program has to request it with
the proper annotations in its ELF file.

David Daney.


> Additionally, it sets a default data area non-executable protection.
>
> Signed-off-by: Leonid Yegoshin <[email protected]>
> ---
> arch/mips/include/asm/page.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
> index 89dd7fed1a57..0b6cec4a1b80 100644
> --- a/arch/mips/include/asm/page.h
> +++ b/arch/mips/include/asm/page.h
> @@ -228,7 +228,7 @@ extern int __virt_addr_valid(const volatile void *kaddr);
> #define virt_addr_valid(kaddr) \
> __virt_addr_valid((const volatile void *) (kaddr))
>
> -#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
> +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
> VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
>
> #define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE)
>

2015-08-06 00:15:47

by David Daney

[permalink] [raw]
Subject: Re: [PATCH v4 0/3] MIPS executable stack protection

On 08/05/2015 04:49 PM, Leonid Yegoshin wrote:
> The following series implements an executable stack protection in MIPS.
>
> It sets up a per-thread 'VDSO' page and appropriate TLB support.
> Page is set write-protected from user and is maintained via kernel VA.
> MIPS FPU emulation is shifted to new page and stack is relieved for
> execute protection as is as all data pages in default setup during ELF
> binary initialization.

Does it handle nested emulation?

1) Emulation started on instruction in main program flow, we are
about to re-enter user space..


2) Asynchronous signal is received.


3) Return to user space. Where do we go? Is it the signal handler
or the instruction emulation?

If we go to the signal handler and it needs emulation, what happens?

David Daney



> The real protection is controlled by GLIBC and
> it can do stack protected now as it is done in other architectures and
> I learned today that GLIBC team is ready for this.
>
> Note: actual execute-protection depends from HW capability, of course.
>
> This patch is required for MIPS32/64 R2 emulation on MIPS R6 architecture.
> Without it 'ssh-keygen' crashes pretty fast on attempt to execute instruction
> in stack.
>
> v2 changes:
> - Added an optimization during mmap switch - doesn't switch if the same
> thread is rescheduled and other threads don't intervene (Peter Zijlstra)
> - Fixed uMIPS support (Paul Burton)
> - Added unwinding of VDSO emulation stack at signal handler invocation,
> hiding an emulation page (Andy Lutomirski note in other patch comments)
>
> V3 change: heavy preemption friendly.
>
> V4 changes:
> - Fixed bug in supplementary TLB flush (change KVA to user address space)
> - Rebased to 4.X kernel
> ---
>
> Leonid Yegoshin (3):
> MIPS: mips_flush_cache_range is added
> MIPS: Setup an instruction emulation in VDSO protected page instead of user stack
> MIPS: set stack/data protection as non-executable
>
>
> arch/mips/include/asm/cacheflush.h | 3 +
> arch/mips/include/asm/fpu_emulator.h | 2
> arch/mips/include/asm/mmu.h | 3 +
> arch/mips/include/asm/page.h | 2
> arch/mips/include/asm/processor.h | 2
> arch/mips/include/asm/switch_to.h | 14 +++
> arch/mips/include/asm/thread_info.h | 3 +
> arch/mips/include/asm/tlbmisc.h | 1
> arch/mips/include/asm/vdso.h | 3 +
> arch/mips/kernel/mips-r2-to-r6-emul.c | 10 +-
> arch/mips/kernel/process.c | 7 ++
> arch/mips/kernel/signal.c | 4 +
> arch/mips/kernel/vdso.c | 44 +++++++++
> arch/mips/math-emu/cp1emu.c | 8 +-
> arch/mips/math-emu/dsemul.c | 154 +++++++++++++++++++++++++++------
> arch/mips/mm/c-octeon.c | 8 ++
> arch/mips/mm/c-r3k.c | 8 ++
> arch/mips/mm/c-r4k.c | 43 +++++++++
> arch/mips/mm/c-tx39.c | 9 ++
> arch/mips/mm/cache.c | 4 +
> arch/mips/mm/fault.c | 5 +
> arch/mips/mm/tlb-r4k.c | 42 +++++++++
> 22 files changed, 343 insertions(+), 36 deletions(-)
>
> --
> Signature
>

2015-08-06 00:03:00

by Leonid Yegoshin

[permalink] [raw]
Subject: Re: [PATCH v4 0/3] MIPS executable stack protection

On 08/05/2015 05:00 PM, David Daney wrote:
>
> Does it handle nested emulation?

Yes, it does since v2:

"- Added unwinding of VDSO emulation stack at signal handler
invocation, hiding an emulation page (Andy Lutomirski note in other
patch comments)"

- Leonid.

2015-08-06 00:06:39

by Leonid Yegoshin

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 04:55 PM, Paul Burton wrote:
>
>
> As was pointed out last time you posted this, it breaks backwards
> compatibility with userland & thus cannot be applied.

Never observed since first version.

In other side, the problem with apps like ssh_keygen is observed in
absence of executable stack protection.

- Leonid.

2015-08-06 00:15:08

by David Daney

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 05:06 PM, Leonid Yegoshin wrote:
> On 08/05/2015 04:55 PM, Paul Burton wrote:
>>
>>
>> As was pointed out last time you posted this, it breaks backwards
>> compatibility with userland & thus cannot be applied.
>
> Never observed since first version.
>
> In other side, the problem with apps like ssh_keygen is observed in
> absence of executable stack protection.

You cannot change the default.

If your ssh_keygen is broken, get a working version.

I have never had a problem running ssh_keygen (on platforms requiring
emulation).

>
> - Leonid.

2015-08-06 00:24:01

by Leonid Yegoshin

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 05:14 PM, David Daney wrote:
> On 08/05/2015 05:06 PM, Leonid Yegoshin wrote:
>> On 08/05/2015 04:55 PM, Paul Burton wrote:
>>>
>>>
>>> As was pointed out last time you posted this, it breaks backwards
>>> compatibility with userland & thus cannot be applied.
>>
>> Never observed since first version.
>>
>> In other side, the problem with apps like ssh_keygen is observed in
>> absence of executable stack protection.
>
> You cannot change the default.
>
> If your ssh_keygen is broken, get a working version.

It is actually any application which requests non-executable stack
protection and needs some emulation BEFORE GLIBC cancels that
non-executable stack protection due to libraries.

If you build all libraries with PT_GNU_STACK 'non-executable' and use
application with the same protection then you can't emulate even a
single instruction - it crashes immediately. So, it is not a bad
application, it is a bad choice for emulation space in past.

>
> I have never had a problem running ssh_keygen (on platforms requiring
> emulation).

Create a buildroot FS with PT_GNU_STACK 'non-executable' libraries. Then
run ssh_keygen on CPU without FPU and look.

You also may try to run MIPS R2 Debian on MIPS R6 CPU, and see a
spectacular failure of ssh_keygen (it tries to emulate MIPS R2
instruction before first library is loaded and that fails due to
non-executable stack protection.

- Leonid.

2015-08-06 00:37:58

by David Daney

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 05:23 PM, Leonid Yegoshin wrote:
> On 08/05/2015 05:14 PM, David Daney wrote:
>> On 08/05/2015 05:06 PM, Leonid Yegoshin wrote:
>>> On 08/05/2015 04:55 PM, Paul Burton wrote:
>>>>
>>>>
>>>> As was pointed out last time you posted this, it breaks backwards
>>>> compatibility with userland & thus cannot be applied.
>>>
>>> Never observed since first version.
>>>
>>> In other side, the problem with apps like ssh_keygen is observed in
>>> absence of executable stack protection.
>>
>> You cannot change the default.
>>
>> If your ssh_keygen is broken, get a working version.
>
> It is actually any application which requests non-executable stack
> protection and needs some emulation BEFORE GLIBC cancels that
> non-executable stack protection due to libraries.
>
> If you build all libraries with PT_GNU_STACK 'non-executable' and use
> application with the same protection then you can't emulate even a
> single instruction - it crashes immediately. So, it is not a bad
> application, it is a bad choice for emulation space in past.
>

This just means that your userspace is broken.

If GLibC cannot do the right thing then it should be fixed.

The very first thing that is executed is ld.so, you need to make your
ld.so do the right thing before transferring control to your program's
entry point.

You cannot change the default setting for executable stack just because
you have created a broken userspace.

The ability of legacy userspace to continue functioning cannot be
sacrificed.

David Daney

2015-08-06 00:40:38

by Paul Burton

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On Wed, Aug 05, 2015 at 05:23:55PM -0700, Leonid Yegoshin wrote:
> It is actually any application which requests non-executable stack
> protection and needs some emulation BEFORE GLIBC cancels that non-executable
> stack protection due to libraries.
>
> If you build all libraries with PT_GNU_STACK 'non-executable' and use
> application with the same protection then you can't emulate even a single
> instruction - it crashes immediately. So, it is not a bad application, it is
> a bad choice for emulation space in past.

...snip...

> Create a buildroot FS with PT_GNU_STACK 'non-executable' libraries. Then run
> ssh_keygen on CPU without FPU and look.
>
> You also may try to run MIPS R2 Debian on MIPS R6 CPU, and see a spectacular
> failure of ssh_keygen (it tries to emulate MIPS R2 instruction before first
> library is loaded and that fails due to non-executable stack protection.

All of that sounds like perfectly valid reasons to move the FP branch
delay emulation away from using the stack, which we absolutely do need
to do. They do not however justify changing the default flags & breaking
backwards compatibility.

Thanks,
Paul

2015-08-06 00:46:57

by Leonid Yegoshin

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 05:37 PM, David Daney wrote:
> This just means that your userspace is broken.
>
> If GLibC cannot do the right thing then it should be fixed.

Let's skip this until you explain how to create a fully
non-executable-stack process. GLIBC people is ready to do something but
after we remove emulation from stack.

>
>
> You cannot change the default setting for executable stack just
> because you have created a broken userspace.

Please give me at least one example, one existing application which
would suffer.

I remember that people already wrote here that this kind of apps (which
is based on eXecutable stack and doesn't announce it in PT_GNU_STACK)
need to be eliminated.

>
> The ability of legacy userspace to continue functioning cannot be
> sacrificed.
>

Not at any price.

However, this switch is a separate patch from others. It can be not
applied or it can be applied, depending from prevailing mind - what is
more significant, some (unknown) app or non-executable stack protection.

- Leonid.

2015-08-06 01:08:07

by David Daney

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] MIPS: set stack/data protection as non-executable

On 08/05/2015 05:46 PM, Leonid Yegoshin wrote:
> On 08/05/2015 05:37 PM, David Daney wrote:
>> This just means that your userspace is broken.
>>
>> If GLibC cannot do the right thing then it should be fixed.
>
> Let's skip this until you explain how to create a fully
> non-executable-stack process.

Build almost any program with a gcc/glibc from a recent Cavium SDK
toolchain.

Something like this:

mips64-octeon-linux-gnu-gcc -o myprogram myprogram.c


>
>>
>>
>> You cannot change the default setting for executable stack just
>> because you have created a broken userspace.
>
> Please give me at least one example, one existing application which
> would suffer.

Anything compiled with gcj that uses java.lang.reflect.Method.invoke()

Anything that uses gcc nested functions.

Anything that uses libffi

Where an older toolchain that doesn't set PT_GNU_STACK was used.

>
> I remember that people already wrote here that this kind of apps (which
> is based on eXecutable stack and doesn't announce it in PT_GNU_STACK)
> need to be eliminated.
>
>>
>> The ability of legacy userspace to continue functioning cannot be
>> sacrificed.
>>
>
> Not at any price.
>
> However, this switch is a separate patch from others. It can be not
> applied or it can be applied, depending from prevailing mind - what is
> more significant, some (unknown) app or non-executable stack protection.
>
> - Leonid.