Skip to content

Commit

Permalink
parisc: Add vDSO support
Browse files Browse the repository at this point in the history
Add minimal vDSO support, which provides the signal trampoline helpers,
but none of the userspace syscall helpers like time wrappers.

The big benefit of this vDSO implementation is, that we now don't need
an executeable stack any longer. PA-RISC is one of the last
architectures where an executeable stack was needed in oder to implement
the signal trampolines by putting assembly instructions on the stack
which then gets executed. Instead the kernel will provide the relevant
code in the vDSO page and only put the pointers to the signal
information on the stack.

By dropping the need for executable stacks we avoid running into issues
with applications which want non executable stacks for security reasons.
Additionally, alternative stacks on memory areas without exec
permissions are supported too.

This code is based on an initial implementation by Randolph Chung from 2006:
https://lore.kernel.org/linux-parisc/4544A34A.6080700@tausq.org/

I did the porting and lifted the code to current code base. Dave fixed
the unwind code so that gdb and glibc are able to backtrace through the
code. An additional patch to gdb will be pushed upstream by Dave.

Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Dave Anglin <dave.anglin@bell.net>
Cc: Randolph Chung <randolph@tausq.org>
Signed-off-by: Helge Deller <deller@gmx.de>
  • Loading branch information
Helge Deller committed Mar 11, 2022
1 parent 14615ec commit df24e17
Show file tree
Hide file tree
Showing 32 changed files with 1,149 additions and 165 deletions.
1 change: 1 addition & 0 deletions arch/parisc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ config PARISC
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_STRICT_KERNEL_RWX
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_PTE_SPECIAL
select ARCH_NO_SG_CHAIN
select ARCH_SUPPORTS_HUGETLBFS if PA20
select ARCH_SUPPORTS_MEMORY_FAILURE
Expand Down
30 changes: 30 additions & 0 deletions arch/parisc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ endif

export LD_BFD

# Set default 32 bits cross compilers for vdso
CC_ARCHES_32 = hppa hppa2.0 hppa1.1
CC_SUFFIXES = linux linux-gnu unknown-linux-gnu
CROSS32_COMPILE := $(call cc-cross-prefix, \
$(foreach a,$(CC_ARCHES_32), \
$(foreach s,$(CC_SUFFIXES),$(a)-$(s)-)))
CROSS32CC := $(CROSS32_COMPILE)gcc
export CROSS32CC

# Set default cross compiler for kernel build
ifdef cross_compiling
ifeq ($(CROSS_COMPILE),)
CC_SUFFIXES = linux linux-gnu unknown-linux-gnu
Expand Down Expand Up @@ -163,6 +173,26 @@ vmlinuz: vmlinux
@$(KGZIP) -cf -9 $< > $@
endif

ifeq ($(KBUILD_EXTMOD),)
# We need to generate vdso-offsets.h before compiling certain files in kernel/.
# In order to do that, we should use the archprepare target, but we can't since
# asm-offsets.h is included in some files used to generate vdso-offsets.h, and
# asm-offsets.h is built in prepare0, for which archprepare is a dependency.
# Therefore we need to generate the header after prepare0 has been made, hence
# this hack.
prepare: vdso_prepare
vdso_prepare: prepare0
$(if $(CONFIG_64BIT),$(Q)$(MAKE) \
$(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h)
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h
endif

PHONY += vdso_install

vdso_install:
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso $@
$(if $(CONFIG_COMPAT_VDSO), \
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 $@)
install:
$(CONFIG_SHELL) $(srctree)/arch/parisc/install.sh \
$(KERNELRELEASE) vmlinux System.map "$(INSTALL_PATH)"
Expand Down
15 changes: 15 additions & 0 deletions arch/parisc/include/asm/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,19 @@ struct mm_struct;
extern unsigned long arch_randomize_brk(struct mm_struct *);
#define arch_randomize_brk arch_randomize_brk


#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int executable_stack);
#define VDSO_AUX_ENT(a, b) NEW_AUX_ENT(a, b)
#define VDSO_CURRENT_BASE current->mm->context.vdso_base

#define ARCH_DLINFO \
do { \
if (VDSO_CURRENT_BASE) { \
NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE);\
} \
} while (0)

#endif
6 changes: 4 additions & 2 deletions arch/parisc/include/asm/mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#ifndef _PARISC_MMU_H_
#define _PARISC_MMU_H_

/* On parisc, we store the space id here */
typedef unsigned long mm_context_t;
typedef struct {
unsigned long space_id;
unsigned long vdso_base;
} mm_context_t;

#endif /* _PARISC_MMU_H_ */
16 changes: 8 additions & 8 deletions arch/parisc/include/asm/mmu_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,30 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
{
BUG_ON(atomic_read(&mm->mm_users) != 1);

mm->context = alloc_sid();
mm->context.space_id = alloc_sid();
return 0;
}

#define destroy_context destroy_context
static inline void
destroy_context(struct mm_struct *mm)
{
free_sid(mm->context);
mm->context = 0;
free_sid(mm->context.space_id);
mm->context.space_id = 0;
}

static inline unsigned long __space_to_prot(mm_context_t context)
{
#if SPACEID_SHIFT == 0
return context << 1;
return context.space_id << 1;
#else
return context >> (SPACEID_SHIFT - 1);
return context.space_id >> (SPACEID_SHIFT - 1);
#endif
}

static inline void load_context(mm_context_t context)
{
mtsp(context, 3);
mtsp(context.space_id, 3);
mtctl(__space_to_prot(context), 8);
}

Expand Down Expand Up @@ -89,8 +89,8 @@ static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)

BUG_ON(next == &init_mm); /* Should never happen */

if (next->context == 0)
next->context = alloc_sid();
if (next->context.space_id == 0)
next->context.space_id = alloc_sid();

switch_mm(prev,next,current);
}
Expand Down
7 changes: 5 additions & 2 deletions arch/parisc/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
unsigned long flags;

purge_tlb_start(flags);
mtsp(mm->context, 1);
mtsp(mm->context.space_id, 1);
pdtlb(addr);
pitlb(addr);
purge_tlb_end(flags);
Expand Down Expand Up @@ -219,9 +219,10 @@ extern void __update_cache(pte_t pte);
#define _PAGE_PRESENT (1 << xlate_pabit(_PAGE_PRESENT_BIT))
#define _PAGE_HUGE (1 << xlate_pabit(_PAGE_HPAGE_BIT))
#define _PAGE_USER (1 << xlate_pabit(_PAGE_USER_BIT))
#define _PAGE_SPECIAL (_PAGE_DMB)

#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | _PAGE_DIRTY | _PAGE_ACCESSED)
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_SPECIAL)
#define _PAGE_KERNEL_RO (_PAGE_PRESENT | _PAGE_READ | _PAGE_DIRTY | _PAGE_ACCESSED)
#define _PAGE_KERNEL_EXEC (_PAGE_KERNEL_RO | _PAGE_EXEC)
#define _PAGE_KERNEL_RWX (_PAGE_KERNEL_EXEC | _PAGE_WRITE)
Expand Down Expand Up @@ -348,13 +349,15 @@ static inline void pud_clear(pud_t *pud) {
static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; }
static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; }
static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; }
static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; }

static inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
static inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
static inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_WRITE; return pte; }
static inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; }
static inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_ACCESSED; return pte; }
static inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_WRITE; return pte; }
static inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; }

/*
* Huge pte definitions.
Expand Down
2 changes: 1 addition & 1 deletion arch/parisc/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ on downward growing arches, it looks like this:

#define start_thread(regs, new_pc, new_sp) do { \
elf_addr_t *sp = (elf_addr_t *)new_sp; \
__u32 spaceid = (__u32)current->mm->context; \
__u32 spaceid = (__u32)current->mm->context.space_id; \
elf_addr_t pc = (elf_addr_t)new_pc | 3; \
elf_caddr_t *argv = (elf_caddr_t *)bprm->exec + 1; \
\
Expand Down
10 changes: 1 addition & 9 deletions arch/parisc/include/asm/rt_sigframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,8 @@
#ifndef _ASM_PARISC_RT_SIGFRAME_H
#define _ASM_PARISC_RT_SIGFRAME_H

#define SIGRETURN_TRAMP 4
#define SIGRESTARTBLOCK_TRAMP 5
#define TRAMP_SIZE (SIGRETURN_TRAMP + SIGRESTARTBLOCK_TRAMP)

struct rt_sigframe {
/* XXX: Must match trampoline size in arch/parisc/kernel/signal.c
Secondary to that it must protect the ERESTART_RESTARTBLOCK
trampoline we left on the stack (we were bad and didn't
change sp so we could run really fast.) */
unsigned int tramp[TRAMP_SIZE];
unsigned int tramp[2]; /* holds original return address */
struct siginfo info;
struct ucontext uc;
};
Expand Down
2 changes: 1 addition & 1 deletion arch/parisc/include/asm/tlbflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int __flush_tlb_range(unsigned long sid,
unsigned long start, unsigned long end);

#define flush_tlb_range(vma, start, end) \
__flush_tlb_range((vma)->vm_mm->context, start, end)
__flush_tlb_range((vma)->vm_mm->context.space_id, start, end)

#define flush_tlb_kernel_range(start, end) \
__flush_tlb_range(0, start, end)
Expand Down
4 changes: 0 additions & 4 deletions arch/parisc/include/asm/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@
); \
__sys_res = (long)__res; \
} \
if ( (unsigned long)__sys_res >= (unsigned long)-4095 ){ \
errno = -__sys_res; \
__sys_res = -1; \
} \
__sys_res; \
})

Expand Down
24 changes: 24 additions & 0 deletions arch/parisc/include/asm/vdso.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PARISC_VDSO_H__
#define __PARISC_VDSO_H__

#ifndef __ASSEMBLY__

#ifdef CONFIG_64BIT
#include <generated/vdso64-offsets.h>
#endif
#include <generated/vdso32-offsets.h>

#define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name))
#define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name))

extern struct vdso_data *vdso_data;

#endif /* __ASSEMBLY __ */

/* Default link addresses for the vDSOs */
#define VDSO_LBASE 0

#define VDSO_VERSION_STRING LINUX_5.18

#endif /* __PARISC_VDSO_H__ */
8 changes: 8 additions & 0 deletions arch/parisc/include/uapi/asm/auxvec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _UAPI_PARISC_AUXVEC_H
#define _UAPI_PARISC_AUXVEC_H

/* The vDSO location. */
#define AT_SYSINFO_EHDR 33

#endif /* _UAPI_PARISC_AUXVEC_H */
5 changes: 5 additions & 0 deletions arch/parisc/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KEXEC_CORE) += kexec.o relocate_kernel.o
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o

# vdso
obj-y += vdso.o
obj-$(CONFIG_64BIT) += vdso64/
obj-y += vdso32/
9 changes: 9 additions & 0 deletions arch/parisc/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
#include <asm/ptrace.h>
#include <asm/processor.h>
#include <asm/pdc.h>
#include <uapi/asm/sigcontext.h>
#include <asm/ucontext.h>
#include <asm/rt_sigframe.h>
#include <linux/uaccess.h>
#include "signal32.h"

/* Add FRAME_SIZE to the size x and align it to y. All definitions
* that use align_frame will include space for a frame.
Expand Down Expand Up @@ -218,6 +222,11 @@ int main(void)
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PRE_COUNT, offsetof(struct task_struct, thread_info.preempt_count));
BLANK();
DEFINE(ASM_SIGFRAME_SIZE, PARISC_RT_SIGFRAME_SIZE);
DEFINE(SIGFRAME_CONTEXT_REGS, offsetof(struct rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE);
DEFINE(ASM_SIGFRAME_SIZE32, PARISC_RT_SIGFRAME_SIZE32);
DEFINE(SIGFRAME_CONTEXT_REGS32, offsetof(struct compat_rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE32);
BLANK();
DEFINE(ICACHE_BASE, offsetof(struct pdc_cache_info, ic_base));
DEFINE(ICACHE_STRIDE, offsetof(struct pdc_cache_info, ic_stride));
DEFINE(ICACHE_COUNT, offsetof(struct pdc_cache_info, ic_count));
Expand Down
6 changes: 3 additions & 3 deletions arch/parisc/kernel/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ void flush_cache_mm(struct mm_struct *mm)
rp3440, etc. So, avoid it if the mm isn't too big. */
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
mm_total_size(mm) >= parisc_cache_flush_threshold) {
if (mm->context)
if (mm->context.space_id)
flush_tlb_all();
flush_cache_all();
return;
Expand All @@ -581,7 +581,7 @@ void flush_cache_range(struct vm_area_struct *vma,
{
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
end - start >= parisc_cache_flush_threshold) {
if (vma->vm_mm->context)
if (vma->vm_mm->context.space_id)
flush_tlb_range(vma, start, end);
flush_cache_all();
return;
Expand All @@ -594,7 +594,7 @@ void
flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
{
if (pfn_valid(pfn)) {
if (likely(vma->vm_mm->context)) {
if (likely(vma->vm_mm->context.space_id)) {
flush_tlb_page(vma, vmaddr);
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
} else {
Expand Down
Loading

0 comments on commit df24e17

Please sign in to comment.