From: Philipp Tomsich philipp.tomsich@theobroma-systems.com
maillist inclusion category: feature bugzilla: 46790 CVE: NA
Reference: https://github.com/norov/linux/commits/ilp32-5.2
--------------------------------
ILP32 VDSO exports following symbols: __kernel_rt_sigreturn; __kernel_gettimeofday; __kernel_clock_gettime; __kernel_clock_getres.
What shared object to use, kernel selects depending on result of is_ilp32_compat_task() in arch/arm64/kernel/vdso.c, so it substitutes correct pages and spec.
Adjusted to move the data page before code pages in sync with commit 601255ae3c98 ("arm64: vdso: move data page before code pages")
Signed-off-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com Signed-off-by: Christoph Muellner christoph.muellner@theobroma-systems.com Signed-off-by: Yury Norov ynorov@caviumnetworks.com Signed-off-by: Bamvor Jian Zhang bamv2005@gmail.com Signed-off-by: Yury Norov ynorov@marvell.com Signed-off-by: Xiongfeng Wang wangxiongfeng2@huawei.com Acked-by: Xie XiuQi xiexiuqi@huawei.com Signed-off-by: Chen Jun chenjun102@huawei.com
Conflicts: arch/arm64/Makefile arch/arm64/kernel/Makefile arch/arm64/kernel/asm-offsets.c arch/arm64/kernel/vdso.c arch/arm64/include/asm/vdso.h
Signed-off-by: Chen Jiahao chenjiahao16@huawei.com --- arch/arm64/Makefile | 3 + arch/arm64/include/asm/vdso.h | 9 ++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/asm-offsets.c | 7 ++ arch/arm64/kernel/vdso-ilp32/.gitignore | 2 + arch/arm64/kernel/vdso-ilp32/Makefile | 108 ++++++++++++++++++ arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S | 22 ++++ arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S | 88 ++++++++++++++ arch/arm64/kernel/vdso.c | 42 ++++++- 9 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/vdso-ilp32/.gitignore create mode 100644 arch/arm64/kernel/vdso-ilp32/Makefile create mode 100644 arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S create mode 100644 arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 2d49aea0ff67..efd4b861fb86 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -203,6 +203,9 @@ ifdef CONFIG_COMPAT_VDSO $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 \ include/generated/vdso32-offsets.h arch/arm64/kernel/vdso32/vdso.so endif +ifeq ($(CONFIG_ARM64_ILP32), y) + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-ilp32 include/generated/vdso-ilp32-offsets.h +endif endif
include $(srctree)/scripts/Makefile.defconf diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h index b4ae32109932..0cedfa1cce8a 100644 --- a/arch/arm64/include/asm/vdso.h +++ b/arch/arm64/include/asm/vdso.h @@ -21,6 +21,12 @@ #include <generated/vdso32-offsets.h> #endif
+#ifdef CONFIG_ARM64_ILP32 +#include <generated/vdso-ilp32-offsets.h> +#else +#define vdso_offset_sigtramp_ilp32 ({ BUILD_BUG(); 0; }) +#endif + #define VDSO_SYMBOL(base, name) \ ({ \ (void *)(vdso_offset_##name - VDSO_LBASE + (unsigned long)(base)); \ @@ -28,6 +34,9 @@
extern char vdso_start[], vdso_end[]; extern char vdso32_start[], vdso32_end[]; +#ifdef CONFIG_ARM64_ILP32 +extern char vdso_ilp32_start[], vdso_ilp32_end[]; +#endif
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 12d8c60404ed..8b3c49d706ea 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o obj-$(CONFIG_ARM64_MTE) += mte.o obj-y += vdso-wrap.o obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o +obj-$(CONFIG_ARM64_ILP32) += vdso-ilp32/ obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.o CFLAGS_patch-scs.o += -mbranch-protection=none
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 377791aecffa..e1adb628988e 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -120,6 +120,13 @@ int main(void) DEFINE(SOFTIRQ_SHIFT, SOFTIRQ_SHIFT); DEFINE(IRQ_CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); BLANK(); +#ifdef CONFIG_COMPAT + DEFINE(COMPAT_TVAL_TV_SEC, offsetof(struct old_timeval32, tv_sec)); + DEFINE(COMPAT_TVAL_TV_USEC, offsetof(struct old_timeval32, tv_usec)); + DEFINE(COMPAT_TSPEC_TV_SEC, offsetof(struct old_timespec32, tv_sec)); + DEFINE(COMPAT_TSPEC_TV_NSEC, offsetof(struct old_timespec32, tv_nsec)); + BLANK(); +#endif DEFINE(CPU_BOOT_TASK, offsetof(struct secondary_data, task)); BLANK(); DEFINE(FTR_OVR_VAL_OFFSET, offsetof(struct arm64_ftr_override, val)); diff --git a/arch/arm64/kernel/vdso-ilp32/.gitignore b/arch/arm64/kernel/vdso-ilp32/.gitignore new file mode 100644 index 000000000000..61806c3fd68b --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/.gitignore @@ -0,0 +1,2 @@ +vdso-ilp32.lds +vdso-ilp32-offsets.h diff --git a/arch/arm64/kernel/vdso-ilp32/Makefile b/arch/arm64/kernel/vdso-ilp32/Makefile new file mode 100644 index 000000000000..9a5bbe313769 --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/Makefile @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Building a vDSO image for AArch64. +# +# Author: Will Deacon will.deacon@arm.com +# Heavily based on the vDSO Makefiles for other archs. +# + +# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before +# the inclusion of generic Makefile. +ARCH_REL_TYPE_ABS := R_AARCH64_JUMP_SLOT|R_AARCH64_GLOB_DAT|R_AARCH64_ABS64 +include $(srctree)/lib/vdso/Makefile + +obj-ilp32-vdso := vgettimeofday-ilp32.o note-ilp32.o sigreturn-ilp32.o + +# Build rules +targets := $(obj-ilp32-vdso) vdso-ilp32.so vdso-ilp32.so.dbg +obj-ilp32-vdso := $(addprefix $(obj)/, $(obj-ilp32-vdso)) + +btildflags-$(CONFIG_ARM64_BTI_KERNEL) += -z force-bti + +# -Bsymbolic has been added for consistency with arm, the compat vDSO and +# potential future proofing if we end up with internal calls to the exported +# routines, as x86 does (see 6f121e548f83 ("x86, vdso: Reimplement vdso.so +# preparation in build-time C")). +ldflags-y := -shared -nostdlib -soname=linux-ilp32-vdso.so.1 --hash-style=sysv \ + -Bsymbolic $(call ld-option, --no-eh-frame-hdr) --build-id -n \ + $(btildflags-y) -T + +ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18 +ccflags-y += -DDISABLE_BRANCH_PROFILING +#ccflags-y += -nostdlib +ccflags-y += -nostdlib -Wl,-soname=linux-ilp32-vdso.so.1 \ + $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) + +CFLAGS_REMOVE_vgettimeofday-ilp32.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) $(GCC_PLUGINS_CFLAGS) +KBUILD_CFLAGS += $(DISABLE_LTO) +KASAN_SANITIZE := n +UBSAN_SANITIZE := n +OBJECT_FILES_NON_STANDARD := y +KCOV_INSTRUMENT := n + +CFLAGS_vgettimeofday-ilp32.o = -O2 -mcmodel=tiny -fasynchronous-unwind-tables -mabi=ilp32 + +ifneq ($(c-gettimeofday-y),) + CFLAGS_vgettimeofday-ilp32.o += -include $(c-gettimeofday-y) +endif + +# Clang versions less than 8 do not support -mcmodel=tiny +ifeq ($(CONFIG_CC_IS_CLANG), y) + ifeq ($(shell test $(CONFIG_CLANG_VERSION) -lt 80000; echo $$?),0) + CFLAGS_REMOVE_vgettimeofday-ilp32.o += -mcmodel=tiny + endif +endif + +# Disable gcov profiling for VDSO code +GCOV_PROFILE := n + +obj-y += vdso-ilp32.o +extra-y += vdso-ilp32.lds +CPPFLAGS_vdso-ilp32.lds += -P -C -U$(ARCH) -mabi=ilp32 + +# Force dependency (incbin is bad) +$(obj)/vdso-ilp32.o : $(obj)/vdso-ilp32.so + +# Link rule for the .so file, .lds has to be first +$(obj)/vdso-ilp32.so.dbg: $(obj)/vdso-ilp32.lds $(obj-ilp32-vdso) + $(call if_changed,vdso-ilp32ld_and_vdso_check) + +# Strip rule for the .so file +$(obj)/%.so: OBJCOPYFLAGS := -S +$(obj)/%.so: $(obj)/%.so.dbg FORCE + $(call if_changed,objcopy) + +# Generate VDSO offsets using helper script +gen-vdsosym := $(srctree)/$(src)/../vdso/gen_vdso_offsets.sh +quiet_cmd_vdsosym = VDSOSYM $@ + cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ + +include/generated/vdso-ilp32-offsets.h: $(obj)/vdso-ilp32.so.dbg FORCE + $(call if_changed,vdsosym) + +$(obj)/vgettimeofday-ilp32.o: $(src)/../vdso/vgettimeofday.c + $(call if_changed_dep,vdso-ilp32cc) + +$(obj)/note-ilp32.o: $(src)/../vdso/note.S + $(call if_changed_dep,vdso-ilp32as) + +$(obj)/sigreturn-ilp32.o: $(src)/../vdso/sigreturn.S + $(call if_changed_dep,vdso-ilp32as) + +# Actual build commands +quiet_cmd_vdso-ilp32ld_and_vdso_check = LD $@ + cmd_vdso-ilp32ld_and_vdso_check = $(CC) $(c_flags) -mabi=ilp32 -Wl,-n -Wl,-T $^ -o $@ +quiet_cmd_vdso-ilp32cc = VDSOILP32C $@ + cmd_vdso-ilp32cc= $(CC) $(c_flags) -mabi=ilp32 -c -o $@ $< +quiet_cmd_vdso-ilp32as = VDSOILP32A $@ + cmd_vdso-ilp32as = $(CC) $(a_flags) -mabi=ilp32 -c -o $@ $< + +# Install commands for the unstripped file +quiet_cmd_vdso_install = INSTALL $@ + cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ + +vdso-ilp32.so: $(obj)/vdso-ilp32.so.dbg + @mkdir -p $(MODLIB)/vdso + $(call cmd,vdso_install) + +vdso_install: vdso-ilp32.so diff --git a/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S new file mode 100644 index 000000000000..52509a507d26 --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.S @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (C) 2012 ARM Limited + * Author: Will Deacon will.deacon@arm.com + */ + +#include <linux/init.h> +#include <linux/linkage.h> +#include <linux/const.h> +#include <asm/page.h> + + __PAGE_ALIGNED_DATA + + .globl vdso_ilp32_start, vdso_ilp32_end + .balign PAGE_SIZE +vdso_ilp32_start: + .incbin "arch/arm64/kernel/vdso-ilp32/vdso-ilp32.so" + .balign PAGE_SIZE +vdso_ilp32_end: + + .previous diff --git a/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S new file mode 100644 index 000000000000..831a68a8d7b3 --- /dev/null +++ b/arch/arm64/kernel/vdso-ilp32/vdso-ilp32.lds.S @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * GNU linker script for the VDSO library. + * + * Copyright (C) 2012 ARM Limited + * Author: Will Deacon will.deacon@arm.com + * Heavily based on the vDSO linker scripts for other archs. + */ + +#include <linux/const.h> +#include <asm/page.h> +#include <asm/vdso.h> + +SECTIONS +{ + PROVIDE(_vdso_data = . - PAGE_SIZE); + PROVIDE(_vdso_data = . - __VVAR_PAGES * PAGE_SIZE); +#ifdef CONFIG_TIME_NS + PROVIDE(_timens_data = _vdso_data + PAGE_SIZE); +#endif + . = VDSO_LBASE + SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + .note : { *(.note.*) } :text :note + + . = ALIGN(16); + + .text : { *(.text*) } :text =0xd503201f + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + + .dynamic : { *(.dynamic) } :text :dynamic + + .rodata : { *(.rodata*) } :text + + _end = .; + PROVIDE(end = .); + + /DISCARD/ : { + *(.note.GNU-stack) + *(.data .data.* .gnu.linkonce.d.* .sdata*) + *(.bss .sbss .dynbss .dynsbss) + } +} + +/* + * We must supply the ELF program headers explicitly to get just one + * PT_LOAD segment, and set the flags explicitly to make segments read-only. + */ +PHDRS +{ + text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + note PT_NOTE FLAGS(4); /* PF_R */ + eh_frame_hdr PT_GNU_EH_FRAME; +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_4.12 { + global: + __kernel_rt_sigreturn; + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + local: *; + }; +} + +/* + * Make the sigreturn code visible to the kernel. + */ +VDSO_sigtramp_ilp32 = __kernel_rt_sigreturn; diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 47c2eb75f591..fff989a09f75 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -32,6 +32,9 @@ enum vdso_abi { VDSO_ABI_AA64, VDSO_ABI_AA32, +#ifdef CONFIG_ARM64_ILP32 + VDSO_ABI_ILP32 +#endif };
enum vvar_pages { @@ -64,6 +67,13 @@ static struct vdso_abi_info vdso_info[] __ro_after_init = { .vdso_code_end = vdso32_end, }, #endif /* CONFIG_COMPAT_VDSO */ +#ifdef CONFIG_ARM64_ILP32 + [VDSO_ABI_ILP32] = { + .name = "vdso", + .vdso_code_start = vdso_ilp32_start, + .vdso_code_end = vdso_ilp32_end, + }, +#endif };
/* @@ -427,6 +437,19 @@ static struct vm_special_mapping aarch64_vdso_maps[] __ro_after_init = { }, };
+#ifdef CONFIG_ARM64_ILP32 +static struct vm_special_mapping ilp32_vdso_maps[] __ro_after_init = { + [AA64_MAP_VVAR] = { + .name = "[vvar]", + .fault = vvar_fault, + }, + [AA64_MAP_VDSO] = { + .name = "[vdso]", + .mremap = vdso_mremap, + }, +}; +#endif + static int __init vdso_init(void) { vdso_info[VDSO_ABI_AA64].dm = &aarch64_vdso_maps[AA64_MAP_VVAR]; @@ -436,15 +459,32 @@ static int __init vdso_init(void) } arch_initcall(vdso_init);
+#ifdef CONFIG_ARM64_ILP32 +static int __init vdso_ilp32_init(void) +{ + vdso_info[VDSO_ABI_ILP32].dm = &ilp32_vdso_maps[AA64_MAP_VVAR]; + vdso_info[VDSO_ABI_ILP32].cm = &ilp32_vdso_maps[AA64_MAP_VDSO]; + + return __vdso_init(VDSO_ABI_ILP32); +} +arch_initcall(vdso_ilp32_init); +#endif + int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; + enum vdso_abi abi = VDSO_ABI_AA64; int ret;
if (mmap_write_lock_killable(mm)) return -EINTR;
- ret = __setup_additional_pages(VDSO_ABI_AA64, mm, bprm, uses_interp); +#ifdef CONFIG_ARM64_ILP32 + if (is_ilp32_compat_task()) + abi = VDSO_ABI_ILP32; +#endif + ret = __setup_additional_pages(abi, mm, bprm, uses_interp); + mmap_write_unlock(mm);
return ret;