Hello,
This patch series implements a pure-capability vDSO which purecap userspace applications can directly use. It also fixes the vDSO selftests so that they can be built as purecap binaries, therefore allowing the new purecap vDSO to be tested. The existing aarch64 vDSO implementation is unchanged - it has simply been redefined as the compat vDSO. Therefore, processes using both the standard and purecap ABIs will have a vDSO to use.
I have tested both vDSOs using the patched versions of the selftests. I have tested the selftests by by feeding it broken vDSOs and seeing ensuring they fail appropriately.
The first patch fixes the tests, the second patch builds the purecap vDSO, and the third patch sets up the mapping for it and makes it available for use. If you think the patches should be ordered differently, and/or if we should possibly combine the building and mapping patches into one vDSO implementation patch, please let me know.
Thanks, Aditya
GitLab Issue: - https://git.morello-project.org/morello/kernel/linux/-/issues/24
GitLab Review Branch: - https://git.morello-project.org/aditya/linux/-/tree/purecap_vdso?ref_type=he...
Aditya Deshpande (3): selftests/vDSO: Add support for purecap vDSO testing arm64: vdso: Build a pure-capability vDSO arm64: vDSO: Map the purecap vDSO into userspace application address space
arch/arm64/Makefile | 6 + arch/arm64/include/asm/elf.h | 16 ++- arch/arm64/include/asm/vdso.h | 1 + arch/arm64/include/asm/vdso/gettimeofday.h | 92 ++++++++++++++ arch/arm64/kernel/Makefile | 4 +- arch/arm64/kernel/vdso-purecap-wrap.S | 22 ++++ arch/arm64/kernel/vdso-purecap/.gitignore | 2 + arch/arm64/kernel/vdso-purecap/Makefile | 101 ++++++++++++++++ arch/arm64/kernel/vdso-purecap/note.S | 23 ++++ arch/arm64/kernel/vdso-purecap/sigreturn.S | 80 ++++++++++++ arch/arm64/kernel/vdso-purecap/vdso.lds.S | 114 ++++++++++++++++++ .../arm64/kernel/vdso-purecap/vgettimeofday.c | 25 ++++ arch/arm64/kernel/vdso.c | 58 ++++++++- fs/compat_binfmt_elf.c | 16 +++ include/linux/compiler.h | 9 ++ include/linux/elf.h | 2 +- include/linux/user_ptr.h | 1 + tools/testing/selftests/vDSO/Makefile | 16 +-- tools/testing/selftests/vDSO/vdso_config.h | 11 ++ tools/testing/selftests/vDSO/vdso_test_abi.c | 4 +- .../selftests/vDSO/vdso_test_gettimeofday.c | 6 +- 21 files changed, 587 insertions(+), 22 deletions(-) create mode 100644 arch/arm64/kernel/vdso-purecap-wrap.S create mode 100644 arch/arm64/kernel/vdso-purecap/.gitignore create mode 100644 arch/arm64/kernel/vdso-purecap/Makefile create mode 100644 arch/arm64/kernel/vdso-purecap/note.S create mode 100644 arch/arm64/kernel/vdso-purecap/sigreturn.S create mode 100644 arch/arm64/kernel/vdso-purecap/vdso.lds.S create mode 100644 arch/arm64/kernel/vdso-purecap/vgettimeofday.c
Replace getauxval() with getauxptr() when building the tests for purecap. By building the vDSO selftests as purecap binaries, we can test the purecap vDSO. Purecap vDSO tests can be built by passing -DPURECAP_VDSO to CFLAGS in addition to the other flags required for purecap compilation.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com --- tools/testing/selftests/vDSO/Makefile | 16 ++++++++-------- tools/testing/selftests/vDSO/vdso_config.h | 11 +++++++++++ tools/testing/selftests/vDSO/vdso_test_abi.c | 4 ++-- .../selftests/vDSO/vdso_test_gettimeofday.c | 6 ++---- 4 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index d53a4d8008f9..19145210d044 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -1,16 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 -include ../lib.mk - uname_M := $(shell uname -m 2>/dev/null || echo not) ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
-TEST_GEN_PROGS := $(OUTPUT)/vdso_test_gettimeofday $(OUTPUT)/vdso_test_getcpu -TEST_GEN_PROGS += $(OUTPUT)/vdso_test_abi -TEST_GEN_PROGS += $(OUTPUT)/vdso_test_clock_getres +TEST_GEN_PROGS := vdso_test_gettimeofday +TEST_GEN_PROGS += vdso_test_getcpu +TEST_GEN_PROGS += vdso_test_abi +TEST_GEN_PROGS += vdso_test_clock_getres ifeq ($(ARCH),$(filter $(ARCH),x86 x86_64)) -TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 +TEST_GEN_PROGS += vdso_standalone_test_x86 endif -TEST_GEN_PROGS += $(OUTPUT)/vdso_test_correctness +TEST_GEN_PROGS += vdso_test_correctness
CFLAGS := -std=gnu99 CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector @@ -19,7 +18,8 @@ ifeq ($(CONFIG_X86_32),y) LDLIBS += -lgcc_s endif
-all: $(TEST_GEN_PROGS) +include ../lib.mk + $(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h index cdfed403ba13..90082a60a2b1 100644 --- a/tools/testing/selftests/vDSO/vdso_config.h +++ b/tools/testing/selftests/vDSO/vdso_config.h @@ -87,4 +87,15 @@ static const char *names[2][6] = { }, };
+/* Under PCuABI, pointers in the auxiliary vector can no longer be represented + * as unsigned long as they are now capabilities. Get the capability to the + * vDSO using getauxptr() instead, which returns a a capability instead of + * unsigned long. + */ +#if defined(PURECAP_VDSO) +#define get_sysinfo_ehdr() (getauxptr(AT_SYSINFO_EHDR)) +#else +#define get_sysinfo_ehdr() (getauxval(AT_SYSINFO_EHDR)) +#endif + #endif /* __VDSO_CONFIG_H__ */ diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index 883ca85424bc..07a12c4984e6 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -175,7 +175,7 @@ static inline void vdso_test_clock(clockid_t clock_id)
int main(int argc, char **argv) { - unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); + unsigned long sysinfo_ehdr = get_sysinfo_ehdr();
ksft_print_header(); ksft_set_plan(VDSO_TEST_PLAN); @@ -190,7 +190,7 @@ int main(int argc, char **argv)
printf("[vDSO kselftest] VDSO_VERSION: %s\n", version);
- vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); + vdso_init_from_sysinfo_ehdr((uintptr_t)get_sysinfo_ehdr());
vdso_test_gettimeofday();
diff --git a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c index e411f287a426..d0c72b04f9dd 100644 --- a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c +++ b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c @@ -37,14 +37,12 @@ const char *name = "__vdso_gettimeofday";
int main(int argc, char **argv) { - unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); + void* sysinfo_ehdr = get_sysinfo_ehdr(); if (!sysinfo_ehdr) { printf("AT_SYSINFO_EHDR is not present!\n"); return KSFT_SKIP; } - - vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); - + vdso_init_from_sysinfo_ehdr((uintptr_t)get_sysinfo_ehdr()); /* Find gettimeofday. */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); gtod_t gtod = (gtod_t)vdso_sym(version, name);
On 12/10/2023 12:36, Aditya Deshpande wrote:
Replace getauxval() with getauxptr() when building the tests for purecap. By building the vDSO selftests as purecap binaries, we can test the purecap vDSO. Purecap vDSO tests can be built by passing -DPURECAP_VDSO to CFLAGS in addition to the other flags required for purecap compilation.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com
tools/testing/selftests/vDSO/Makefile | 16 ++++++++-------- tools/testing/selftests/vDSO/vdso_config.h | 11 +++++++++++ tools/testing/selftests/vDSO/vdso_test_abi.c | 4 ++-- .../selftests/vDSO/vdso_test_gettimeofday.c | 6 ++---- 4 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index d53a4d8008f9..19145210d044 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -1,16 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 -include ../lib.mk
uname_M := $(shell uname -m 2>/dev/null || echo not) ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) -TEST_GEN_PROGS := $(OUTPUT)/vdso_test_gettimeofday $(OUTPUT)/vdso_test_getcpu -TEST_GEN_PROGS += $(OUTPUT)/vdso_test_abi -TEST_GEN_PROGS += $(OUTPUT)/vdso_test_clock_getres +TEST_GEN_PROGS := vdso_test_gettimeofday +TEST_GEN_PROGS += vdso_test_getcpu +TEST_GEN_PROGS += vdso_test_abi +TEST_GEN_PROGS += vdso_test_clock_getres ifeq ($(ARCH),$(filter $(ARCH),x86 x86_64)) -TEST_GEN_PROGS += $(OUTPUT)/vdso_standalone_test_x86 +TEST_GEN_PROGS += vdso_standalone_test_x86 endif -TEST_GEN_PROGS += $(OUTPUT)/vdso_test_correctness +TEST_GEN_PROGS += vdso_test_correctness CFLAGS := -std=gnu99 CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector @@ -19,7 +18,8 @@ ifeq ($(CONFIG_X86_32),y) LDLIBS += -lgcc_s endif -all: $(TEST_GEN_PROGS) +include ../lib.mk
$(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h index cdfed403ba13..90082a60a2b1 100644 --- a/tools/testing/selftests/vDSO/vdso_config.h +++ b/tools/testing/selftests/vDSO/vdso_config.h
I'm confused, vdso_test_gettimeofday.c doesn't seem to be including this file?
@@ -87,4 +87,15 @@ static const char *names[2][6] = { }, }; +/* Under PCuABI, pointers in the auxiliary vector can no longer be represented
- as unsigned long as they are now capabilities. Get the capability to the
- vDSO using getauxptr() instead, which returns a a capability instead of
- unsigned long.
- */
+#if defined(PURECAP_VDSO)
We don't need a new macro for such cases where we just need to know whether we are in purecap or not. We can use the existing __CHERI_PURE_CAPABILITY__ .
+#define get_sysinfo_ehdr() (getauxptr(AT_SYSINFO_EHDR))
Using inline functions is generally preferred over macros, if possible, which seems to be the case here. Something like:
static inline uintptr_t get_sysinfo_ehdr(void)
+#else +#define get_sysinfo_ehdr() (getauxval(AT_SYSINFO_EHDR)) +#endif
#endif /* __VDSO_CONFIG_H__ */ diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index 883ca85424bc..07a12c4984e6 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -175,7 +175,7 @@ static inline void vdso_test_clock(clockid_t clock_id) int main(int argc, char **argv) {
- unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
- unsigned long sysinfo_ehdr = get_sysinfo_ehdr();
ksft_print_header(); ksft_set_plan(VDSO_TEST_PLAN); @@ -190,7 +190,7 @@ int main(int argc, char **argv) printf("[vDSO kselftest] VDSO_VERSION: %s\n", version);
- vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
- vdso_init_from_sysinfo_ehdr((uintptr_t)get_sysinfo_ehdr());
We could improve the existing code a little further by making sysinfo_ehdr a uintptr_t and using it here (I suspect that was the original intention).
Kevin
vdso_test_gettimeofday(); diff --git a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c index e411f287a426..d0c72b04f9dd 100644 --- a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c +++ b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c @@ -37,14 +37,12 @@ const char *name = "__vdso_gettimeofday"; int main(int argc, char **argv) {
- unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
- void* sysinfo_ehdr = get_sysinfo_ehdr(); if (!sysinfo_ehdr) { printf("AT_SYSINFO_EHDR is not present!\n"); return KSFT_SKIP; }
- vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
- vdso_init_from_sysinfo_ehdr((uintptr_t)get_sysinfo_ehdr()); /* Find gettimeofday. */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); gtod_t gtod = (gtod_t)vdso_sym(version, name);
Hi Kevin,
Thanks for the comments on this patch and others in the series. I will address them in v2. I just had a few things to clarify on the following comments.
$(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h index cdfed403ba13..90082a60a2b1 100644 --- a/tools/testing/selftests/vDSO/vdso_config.h +++ b/tools/testing/selftests/vDSO/vdso_config.h
I'm confused, vdso_test_gettimeofday.c doesn't seem to be including this file?
That's strange, especially given that the disassembly of the built test shows a call to getauxptr(). Nevertheless, I'll explicitly include vdso_config.h in the tests that use this get_sysinfo_ehdr()
+#define get_sysinfo_ehdr() (getauxptr(AT_SYSINFO_EHDR))
Using inline functions is generally preferred over macros, if possible, which seems to be the case here. Something like:
static inline uintptr_t get_sysinfo_ehdr(void)
Makes sense, however wouldn't we use just inline rather than static inline as this function will be used across different .c files?
Thanks once again, Aditya
On 25/10/2023 14:10, Aditya Deshpande wrote:
$(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c diff --git a/tools/testing/selftests/vDSO/vdso_config.h b/tools/testing/selftests/vDSO/vdso_config.h index cdfed403ba13..90082a60a2b1 100644 --- a/tools/testing/selftests/vDSO/vdso_config.h +++ b/tools/testing/selftests/vDSO/vdso_config.h
I'm confused, vdso_test_gettimeofday.c doesn't seem to be including this file?
That's strange, especially given that the disassembly of the built test shows a call to getauxptr(). Nevertheless, I'll explicitly include vdso_config.h in the tests that use this get_sysinfo_ehdr()
You might have missed a warning (I recommend building with -Werror). Calling an undeclared function is actually legal in C, though it's not a good idea for various reasons.
+#define get_sysinfo_ehdr() (getauxptr(AT_SYSINFO_EHDR))
Using inline functions is generally preferred over macros, if possible, which seems to be the case here. Something like:
static inline uintptr_t get_sysinfo_ehdr(void)
Makes sense, however wouldn't we use just inline rather than static inline as this function will be used across different .c files?
inline is a bit of a nightmare in C, as illustrated by the GCC manual [1]. The TL;DR is that you almost always want static inline, as it doesn't incur any linkage trouble.
Kevin
Replicate the existing arm64 vDSO and modify it so that it can be built for the purecap ABI. Therefore, under PCuABI, vDSOs will be built for both aarch64 (COMPAT64) and a purecap.
Note that this change only implements the build of the purecap vDSO. Mapping of this vDSO by the kernel into user address space will be done in a subsequent patch.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com --- arch/arm64/Makefile | 6 + arch/arm64/include/asm/vdso/gettimeofday.h | 92 ++++++++++++++ arch/arm64/kernel/vdso-purecap/.gitignore | 2 + arch/arm64/kernel/vdso-purecap/Makefile | 101 ++++++++++++++++ arch/arm64/kernel/vdso-purecap/note.S | 23 ++++ arch/arm64/kernel/vdso-purecap/sigreturn.S | 80 ++++++++++++ arch/arm64/kernel/vdso-purecap/vdso.lds.S | 114 ++++++++++++++++++ .../arm64/kernel/vdso-purecap/vgettimeofday.c | 25 ++++ include/linux/compiler.h | 9 ++ include/linux/user_ptr.h | 1 + 10 files changed, 453 insertions(+) create mode 100644 arch/arm64/kernel/vdso-purecap/.gitignore create mode 100644 arch/arm64/kernel/vdso-purecap/Makefile create mode 100644 arch/arm64/kernel/vdso-purecap/note.S create mode 100644 arch/arm64/kernel/vdso-purecap/sigreturn.S create mode 100644 arch/arm64/kernel/vdso-purecap/vdso.lds.S create mode 100644 arch/arm64/kernel/vdso-purecap/vgettimeofday.c
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index f5ac0cb0147e..c623b359bb44 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -179,6 +179,8 @@ vdso_install: $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso $@ $(if $(CONFIG_COMPAT_VDSO), \ $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 $@) + $(if $(CONFIG_CHERI_PURECAP_UABI), \ + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-purecap $@)
archprepare: $(Q)$(MAKE) $(build)=arch/arm64/tools kapi @@ -204,6 +206,10 @@ prepare: vdso_prepare vdso_prepare: prepare0 $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso \ include/generated/vdso-offsets.h arch/arm64/kernel/vdso/vdso.so +ifdef CONFIG_CHERI_PURECAP_UABI + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-purecap \ + include/generated/vdso-purecap-offsets.h arch/arm64/kernel/vdso-purecap/vdso.so +endif ifdef CONFIG_COMPAT_VDSO $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 \ include/generated/vdso32-offsets.h arch/arm64/kernel/vdso32/vdso.so diff --git a/arch/arm64/include/asm/vdso/gettimeofday.h b/arch/arm64/include/asm/vdso/gettimeofday.h index 764d13e2916c..fd7dfea6eb74 100644 --- a/arch/arm64/include/asm/vdso/gettimeofday.h +++ b/arch/arm64/include/asm/vdso/gettimeofday.h @@ -14,6 +14,66 @@
#define VDSO_HAS_CLOCK_GETRES 1
+// Purecap Assembly Fallbacks +#if defined(PURECAP_VDSO) +static __always_inline +int gettimeofday_fallback(struct __kernel_old_timeval *_tv, + struct timezone *_tz) +{ + register struct timezone *tz asm("c1") = _tz; + register struct __kernel_old_timeval *tv asm("c0") = _tv; + register long ret asm ("x0"); + register long nr asm("x8") = __NR_gettimeofday; + + asm volatile( + " svc #0\n" + : "=r" (ret) + : "C" (tv), "C" (tz), "r" (nr) + : "memory"); + + return ret; + //return 0; +} + +static __always_inline +long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) +{ + register struct __kernel_timespec *ts asm("c1") = _ts; + register clockid_t clkid asm("c0") = _clkid; + register long ret asm ("x0"); + register long nr asm("x8") = __NR_clock_gettime; + + asm volatile( + " svc #0\n" + : "=r" (ret) + : "C" (clkid), "C" (ts), "r" (nr) + : "memory"); + + return ret; + + //return 0; +} + +static __always_inline +int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) +{ + register struct __kernel_timespec *ts asm("c1") = _ts; + register clockid_t clkid asm("c0") = _clkid; + register long ret asm ("x0"); + register long nr asm("x8") = __NR_clock_getres; + + asm volatile( + " svc #0\n" + : "=r" (ret) + : "C" (clkid), "C" (ts), "r" (nr) + : "memory"); + + return ret; + //return 0; +} + +// Regular Aarch64 Assembly Fallbacks +#else static __always_inline int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz) @@ -66,6 +126,8 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) return ret; }
+#endif /* PURECAP_VDSO */ + static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_data *vd) { @@ -99,6 +161,34 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, return res; }
+#if defined(PURECAP_VDSO) +static __always_inline +const struct vdso_data *__arch_get_vdso_data(void) +{ + const struct vdso_data *vd; + asm(".weak _vdso_data\n\t" + ".hidden vdso_data\n\t" + "adrp %0, _vdso_data\n\t" + "add %0, %0, #:lo12:_vdso_data" + : "=C"(vd)); + return vd; +} + +#ifdef CONFIG_TIME_NS +static __always_inline +const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) +{ + const struct vdso_data *td; + asm(".weak _timens_data\n\t" + ".hidden _timens_data\n\t" + "adrp %0, _timens_data\n\t" + "add %0, %0, #:lo12:_timens_data" + : "=C"(td)); + return vd; +} +#endif +#else + static __always_inline const struct vdso_data *__arch_get_vdso_data(void) { @@ -113,6 +203,8 @@ const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) } #endif
+#endif + #endif /* !__ASSEMBLY__ */
#endif /* __ASM_VDSO_GETTIMEOFDAY_H */ diff --git a/arch/arm64/kernel/vdso-purecap/.gitignore b/arch/arm64/kernel/vdso-purecap/.gitignore new file mode 100644 index 000000000000..652e31d82582 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vdso.lds diff --git a/arch/arm64/kernel/vdso-purecap/Makefile b/arch/arm64/kernel/vdso-purecap/Makefile new file mode 100644 index 000000000000..99214eac9881 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/Makefile @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Building a vDSO image for Purecap Morello under PCuABI. +# +# Based on the standard AArch64 Makefile. +# + +# Include the generic Makefile to check the built vdso. +include $(srctree)/lib/vdso/Makefile + +obj-vdso := vgettimeofday.o note.o sigreturn.o + +# Build rules +targets := $(obj-vdso) vdso.so vdso.so.dbg +obj-vdso := $(addprefix $(obj)/, $(obj-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 -soname=linux-vdso.so.1 --hash-style=sysv \ + -Bsymbolic --build-id=sha1 -n $(btildflags-y) + +ifdef CONFIG_LD_ORPHAN_WARN + ldflags-y += --orphan-handling=$(CONFIG_LD_ORPHAN_WARN_LEVEL) +endif + +ldflags-y += -T + +# Flags set by the arm64 Makefile that should be removed as we are building +# for purecap here +REMOVE_FLAGS := --target=aarch64-linux-gnu -mgeneral-regs-only + +# Flags required for a purecap build. Will be passed to CC, AS, and LD. +PURECAP_FLAGS := --target=aarch64-linux-musl_purecap -mabi=purecap \ +--sysroot=$(MUSL_HOME) + +ccflags-remove-y := $(REMOVE_FLAGS) +asflags-remove-y := $(REMOVE_FLAGS) +asflags-y := $(PURECAP_FLAGS) +ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18 +ccflags-y += $(PURECAP_FLAGS) -DPURECAP_VDSO +ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO + +# -Wmissing-prototypes and -Wmissing-declarations are removed from +# the CFLAGS of vgettimeofday.c to make possible to build the +# kernel with CONFIG_WERROR enabled. +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ + $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \ + $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ + -Wmissing-prototypes -Wmissing-declarations +KASAN_SANITIZE := n +KCSAN_SANITIZE := n +UBSAN_SANITIZE := n +OBJECT_FILES_NON_STANDARD := y +KCOV_INSTRUMENT := n + +CFLAGS_vgettimeofday.o = -O2 -mcmodel=tiny -fasynchronous-unwind-tables + +ifneq ($(c-gettimeofday-y),) + CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y) +endif + +# Disable gcov profiling for VDSO code +GCOV_PROFILE := n + +targets += vdso.lds +CPPFLAGS_vdso.lds += -P -C -U$(ARCH) + +# Link rule for the .so file, .lds has to be first +$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE + $(call if_changed,vdsold_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 (borrowed from the 64-bit vDSO) +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-purecap-offsets.h: $(obj)/vdso.so.dbg FORCE + $(call if_changed,vdsosym) + +# Actual build commands +quiet_cmd_vdsold_and_vdso_check = LD $@ + cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check) + +# Install commands for the unstripped file +quiet_cmd_vdso_install = INSTALL $@ + cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ + +vdso.so: $(obj)/vdso.so.dbg + @mkdir -p $(MODLIB)/vdso + $(call cmd,vdso_install) + +vdso_install: vdso.so diff --git a/arch/arm64/kernel/vdso-purecap/note.S b/arch/arm64/kernel/vdso-purecap/note.S new file mode 100644 index 000000000000..4ea1fbbc6a39 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/note.S @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 ARM Limited + * + * Author: Will Deacon will.deacon@arm.com + * + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text. + * Here we can supply some information useful to userland. + */ + +#include <linux/uts.h> +#include <linux/version.h> +#include <linux/elfnote.h> +#include <linux/build-salt.h> +#include <asm/assembler.h> + +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END + +BUILD_SALT + +emit_aarch64_feature_1_and diff --git a/arch/arm64/kernel/vdso-purecap/sigreturn.S b/arch/arm64/kernel/vdso-purecap/sigreturn.S new file mode 100644 index 000000000000..04ce8a87fd75 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/sigreturn.S @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Sigreturn trampoline for returning from a signal when the SA_RESTORER + * flag is not set. It serves primarily as a hall of shame for crappy + * unwinders and features an exciting but mysterious NOP instruction. + * + * It's also fragile as hell, so please think twice before changing anything + * in here. + * + * Copyright (C) 2023 ARM Limited + * + * Author: Will Deacon will.deacon@arm.com + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/unistd.h> + + .text + +/* + * NOTE!!! You may notice that all of the .cfi directives in this file have + * been commented out. This is because they have been shown to trigger segfaults + * in libgcc when unwinding out of a SIGCANCEL handler to invoke pthread + * cleanup handlers during the thread cancellation dance. By omitting the + * directives, we trigger an arm64-specific fallback path in the unwinder which + * recognises the signal frame and restores many of the registers directly from + * the sigcontext. Re-enabling the cfi directives here therefore needs to be + * much more comprehensive to reduce the risk of further regressions. + */ + +/* Ensure that the mysterious NOP can be associated with a function. */ +// .cfi_startproc purecap + +/* + * .cfi_signal_frame causes the corresponding Frame Description Entry (FDE) in + * the .eh_frame section to be annotated as a signal frame. This allows DWARF + * unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo() and identify + * the next frame using the unmodified return address instead of subtracting 1, + * which may yield the wrong FDE. + */ +// .cfi_signal_frame + +/* + * Tell the unwinder where to locate the frame record linking back to the + * interrupted context. We don't provide unwind info for registers other than + * the frame pointer and the link register here; in practice, this is likely to + * be insufficient for unwinding in C/C++ based runtimes, especially without a + * means to restore the stack pointer. Thankfully, unwinders and debuggers + * already have baked-in strategies for attempting to unwind out of signals. + */ +// .cfi_def_cfa x29, 0 +// .cfi_offset x29, 0 * 8 +// .cfi_offset x30, 1 * 8 + +/* + * This mysterious NOP is required for some unwinders (e.g. libc++) that + * unconditionally subtract one from the result of _Unwind_GetIP() in order to + * identify the calling function. + * Hack borrowed from arch/powerpc/kernel/vdso64/sigtramp.S. + */ + nop // Mysterious NOP + +/* + * GDB, libgcc and libunwind rely on being able to identify the sigreturn + * instruction sequence to unwind from signal handlers. We cannot, therefore, + * use SYM_FUNC_START() here, as it will emit a BTI C instruction and break the + * unwinder. Thankfully, this function is only ever called from a RET and so + * omitting the landing pad is perfectly fine. + */ +SYM_CODE_START(__kernel_rt_sigreturn) +// PLEASE DO NOT MODIFY + mov x8, #__NR_rt_sigreturn +// PLEASE DO NOT MODIFY + svc #0 +// PLEASE DO NOT MODIFY +// .cfi_endproc +SYM_CODE_END(__kernel_rt_sigreturn) + +emit_aarch64_feature_1_and diff --git a/arch/arm64/kernel/vdso-purecap/vdso.lds.S b/arch/arm64/kernel/vdso-purecap/vdso.lds.S new file mode 100644 index 000000000000..5405c79e6170 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/vdso.lds.S @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * GNU linker script for the VDSO library. +* + * Copyright (C) 2023 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> +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64") +OUTPUT_ARCH(aarch64) + +SECTIONS +{ + 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) } + + /* + * Discard .note.gnu.property sections which are unused and have + * different alignment requirement from vDSO note sections. + */ + /DISCARD/ : { + *(.note.GNU-stack .note.gnu.property) + } + .note : { *(.note.*) } :text :note + + . = ALIGN(16); + + .text : { *(.text*) } :text =0xd503201f + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + . = ALIGN(4); + .altinstructions : { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + + .dynamic : { *(.dynamic) } :text :dynamic + + .rela.dyn : ALIGN(8) { *(.rela .rela*) } + + .rodata : { + *(.rodata*) + *(.got) + *(.got.plt) + *(.plt) + *(.plt.*) + *(.iplt) + *(.igot .igot.plt) + } :text + + _end = .; + PROVIDE(end = .); + + DWARF_DEBUG + ELF_DETAILS + + /DISCARD/ : { + *(.data .data.* .gnu.linkonce.d.* .sdata*) + *(.bss .sbss .dynbss .dynsbss) + *(.eh_frame .eh_frame_hdr) + } +} + +/* + * 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 */ +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_2.6.39 { + global: + __kernel_rt_sigreturn; + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + local: *; + }; +} + +/* + * Make the sigreturn code visible to the kernel. + */ +VDSO_sigtramp = __kernel_rt_sigreturn; diff --git a/arch/arm64/kernel/vdso-purecap/vgettimeofday.c b/arch/arm64/kernel/vdso-purecap/vgettimeofday.c new file mode 100644 index 000000000000..e9e462be74ee --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/vgettimeofday.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM64 userspace implementations of gettimeofday() and similar. + * + * Copyright (C) 2023 ARM Limited + * + */ + +int __kernel_clock_gettime(clockid_t clock, + struct __kernel_timespec *ts) +{ + return __cvdso_clock_gettime(clock, ts); +} + +int __kernel_gettimeofday(struct __kernel_old_timeval *tv, + struct timezone *tz) +{ + return __cvdso_gettimeofday(tv, tz); +} + +int __kernel_clock_getres(clockid_t clock_id, + struct __kernel_timespec *res) +{ + return __cvdso_clock_getres(clock_id, res); +} diff --git a/include/linux/compiler.h b/include/linux/compiler.h index d7779a18b24f..56e752138ef7 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -4,6 +4,11 @@
#include <linux/compiler_types.h>
+#if defined(PURECAP_VDSO) +#include <linux/user_ptr.h> +#include <linux/cheri.h> +#endif + #ifndef __ASSEMBLY__
#ifdef __KERNEL__ @@ -222,7 +227,11 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ static inline void *offset_to_ptr(const int *off) { +#if defined(PURECAP_VDSO) + return uaddr_to_user_ptr((unsigned long)off + *off); +#else return (void *)((unsigned long)off + *off); +#endif }
#endif /* __ASSEMBLY__ */ diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 2c2180f0f0c3..21bfe5075141 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -3,6 +3,7 @@ #define _LINUX_USER_PTR_H
#include <linux/typecheck.h> +#include <linux/types.h>
/** * as_user_ptr() - Convert an arbitrary integer value to a user pointer.
On 12/10/2023 12:36, Aditya Deshpande wrote:
Replicate the existing arm64 vDSO and modify it so that it can be built for the purecap ABI. Therefore, under PCuABI, vDSOs will be built for both aarch64 (COMPAT64) and a purecap.
Note that this change only implements the build of the purecap vDSO. Mapping of this vDSO by the kernel into user address space will be done in a subsequent patch.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com
arch/arm64/Makefile | 6 + arch/arm64/include/asm/vdso/gettimeofday.h | 92 ++++++++++++++ arch/arm64/kernel/vdso-purecap/.gitignore | 2 + arch/arm64/kernel/vdso-purecap/Makefile | 101 ++++++++++++++++ arch/arm64/kernel/vdso-purecap/note.S | 23 ++++ arch/arm64/kernel/vdso-purecap/sigreturn.S | 80 ++++++++++++ arch/arm64/kernel/vdso-purecap/vdso.lds.S | 114 ++++++++++++++++++ .../arm64/kernel/vdso-purecap/vgettimeofday.c | 25 ++++
AFAICT (diffing the directories), vdso-purecap/ is essentially the same as vdso/, with the Makefile tweaked. We should avoid creating so much duplication, especially since it makes it hard to keep the purecap vDSO in sync when rebasing. Since even the Makefile is mostly the same in both cases, it seems that the right thing to do is to make vdso/Makefile generate both the standard arm64 vDSO and the purecap vDSO (if CONFIG_CHERI_PURECAP_UABI is selected). That will probably require some refactoring of the Makefile, in order to build each file twice (with different flags and output directory).
include/linux/compiler.h | 9 ++ include/linux/user_ptr.h | 1 + 10 files changed, 453 insertions(+) create mode 100644 arch/arm64/kernel/vdso-purecap/.gitignore create mode 100644 arch/arm64/kernel/vdso-purecap/Makefile create mode 100644 arch/arm64/kernel/vdso-purecap/note.S create mode 100644 arch/arm64/kernel/vdso-purecap/sigreturn.S create mode 100644 arch/arm64/kernel/vdso-purecap/vdso.lds.S create mode 100644 arch/arm64/kernel/vdso-purecap/vgettimeofday.c
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index f5ac0cb0147e..c623b359bb44 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -179,6 +179,8 @@ vdso_install: $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso $@ $(if $(CONFIG_COMPAT_VDSO), \ $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 $@)
- $(if $(CONFIG_CHERI_PURECAP_UABI), \
$(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-purecap $@)
archprepare: $(Q)$(MAKE) $(build)=arch/arm64/tools kapi @@ -204,6 +206,10 @@ prepare: vdso_prepare vdso_prepare: prepare0 $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso \ include/generated/vdso-offsets.h arch/arm64/kernel/vdso/vdso.so +ifdef CONFIG_CHERI_PURECAP_UABI
- $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-purecap \
- include/generated/vdso-purecap-offsets.h arch/arm64/kernel/vdso-purecap/vdso.so
+endif ifdef CONFIG_COMPAT_VDSO $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 \ include/generated/vdso32-offsets.h arch/arm64/kernel/vdso32/vdso.so diff --git a/arch/arm64/include/asm/vdso/gettimeofday.h b/arch/arm64/include/asm/vdso/gettimeofday.h index 764d13e2916c..fd7dfea6eb74 100644 --- a/arch/arm64/include/asm/vdso/gettimeofday.h +++ b/arch/arm64/include/asm/vdso/gettimeofday.h @@ -14,6 +14,66 @@ #define VDSO_HAS_CLOCK_GETRES 1 +// Purecap Assembly Fallbacks +#if defined(PURECAP_VDSO) +static __always_inline +int gettimeofday_fallback(struct __kernel_old_timeval *_tv,
struct timezone *_tz)
+{
- register struct timezone *tz asm("c1") = _tz;
- register struct __kernel_old_timeval *tv asm("c0") = _tv;
- register long ret asm ("x0");
- register long nr asm("x8") = __NR_gettimeofday;
- asm volatile(
- " svc #0\n"
- : "=r" (ret)
- : "C" (tv), "C" (tz), "r" (nr)
- : "memory");
- return ret;
- //return 0;
+}
+static __always_inline +long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) +{
- register struct __kernel_timespec *ts asm("c1") = _ts;
- register clockid_t clkid asm("c0") = _clkid;
- register long ret asm ("x0");
- register long nr asm("x8") = __NR_clock_gettime;
- asm volatile(
- " svc #0\n"
- : "=r" (ret)
- : "C" (clkid), "C" (ts), "r" (nr)
- : "memory");
- return ret;
- //return 0;
+}
+static __always_inline +int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) +{
- register struct __kernel_timespec *ts asm("c1") = _ts;
- register clockid_t clkid asm("c0") = _clkid;
- register long ret asm ("x0");
- register long nr asm("x8") = __NR_clock_getres;
- asm volatile(
- " svc #0\n"
- : "=r" (ret)
- : "C" (clkid), "C" (ts), "r" (nr)
- : "memory");
- return ret;
- //return 0;
+}
We could avoid duplicating the entire helpers by defining macros that expand to "c0"/"x0", "c1"/"x1", "C"/"r" depending on __CHERI_PURE_CAPABILITY__ .
+// Regular Aarch64 Assembly Fallbacks +#else static __always_inline int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz) @@ -66,6 +126,8 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) return ret; } +#endif /* PURECAP_VDSO */
static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_data *vd) { @@ -99,6 +161,34 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, return res; } +#if defined(PURECAP_VDSO) +static __always_inline +const struct vdso_data *__arch_get_vdso_data(void) +{
- const struct vdso_data *vd;
- asm(".weak _vdso_data\n\t"
I suspect we don't need this directive, as the symbols are always defined (unlike __rela_dyn_{start,end} in the self-relocation case).
".hidden vdso_data\n\t"
"adrp %0, _vdso_data\n\t"
"add %0, %0, #:lo12:_vdso_data"
That would deserve a comment, as it's not entirely obvious why we need to go through these hoops in the purecap case.
: "=C"(vd));
- return vd;
+}
+#ifdef CONFIG_TIME_NS +static __always_inline +const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) +{
- const struct vdso_data *td;
- asm(".weak _timens_data\n\t"
".hidden _timens_data\n\t"
"adrp %0, _timens_data\n\t"
"add %0, %0, #:lo12:_timens_data"
: "=C"(td));
- return vd;
That was clearly meant to be td, not vd. That makes me wonder if that path is being exercised at all?
+} +#endif +#else
It's good practice to annotate #else and #endif with /* CONFIG_... */, especially when they are nested. Also regarding empty lines, it is acceptable to either add them or not, but we should do it consistently. In other words, either:
#if ... [code] #else [code] #endif
or:
#if ...
[code]
#else [code]
#endif
static __always_inline const struct vdso_data *__arch_get_vdso_data(void) { @@ -113,6 +203,8 @@ const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) } #endif +#endif
#endif /* !__ASSEMBLY__ */ #endif /* __ASM_VDSO_GETTIMEOFDAY_H */ diff --git a/arch/arm64/kernel/vdso-purecap/.gitignore b/arch/arm64/kernel/vdso-purecap/.gitignore new file mode 100644 index 000000000000..652e31d82582 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vdso.lds diff --git a/arch/arm64/kernel/vdso-purecap/Makefile b/arch/arm64/kernel/vdso-purecap/Makefile new file mode 100644 index 000000000000..99214eac9881 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/Makefile @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Building a vDSO image for Purecap Morello under PCuABI. +# +# Based on the standard AArch64 Makefile. +#
+# Include the generic Makefile to check the built vdso. +include $(srctree)/lib/vdso/Makefile
+obj-vdso := vgettimeofday.o note.o sigreturn.o
+# Build rules +targets := $(obj-vdso) vdso.so vdso.so.dbg +obj-vdso := $(addprefix $(obj)/, $(obj-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 -soname=linux-vdso.so.1 --hash-style=sysv \
-Bsymbolic --build-id=sha1 -n $(btildflags-y)
+ifdef CONFIG_LD_ORPHAN_WARN
- ldflags-y += --orphan-handling=$(CONFIG_LD_ORPHAN_WARN_LEVEL)
+endif
+ldflags-y += -T
+# Flags set by the arm64 Makefile that should be removed as we are building +# for purecap here +REMOVE_FLAGS := --target=aarch64-linux-gnu -mgeneral-regs-only
+# Flags required for a purecap build. Will be passed to CC, AS, and LD. +PURECAP_FLAGS := --target=aarch64-linux-musl_purecap -mabi=purecap \ +--sysroot=$(MUSL_HOME)
In principle, the vDSO should be entirely self-contained, without any dependency on other libraries. Is there an actual issue with inheriting the standard --target=aarch64-linux-gnu, without specifying a sysroot?
+ccflags-remove-y := $(REMOVE_FLAGS) +asflags-remove-y := $(REMOVE_FLAGS) +asflags-y := $(PURECAP_FLAGS) +ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18 +ccflags-y += $(PURECAP_FLAGS) -DPURECAP_VDSO +ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
+# -Wmissing-prototypes and -Wmissing-declarations are removed from +# the CFLAGS of vgettimeofday.c to make possible to build the +# kernel with CONFIG_WERROR enabled. +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \
$(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \
$(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \
-Wmissing-prototypes -Wmissing-declarations
+KASAN_SANITIZE := n +KCSAN_SANITIZE := n +UBSAN_SANITIZE := n +OBJECT_FILES_NON_STANDARD := y +KCOV_INSTRUMENT := n
+CFLAGS_vgettimeofday.o = -O2 -mcmodel=tiny -fasynchronous-unwind-tables
+ifneq ($(c-gettimeofday-y),)
- CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y)
+endif
+# Disable gcov profiling for VDSO code +GCOV_PROFILE := n
+targets += vdso.lds +CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
+# Link rule for the .so file, .lds has to be first +$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE
- $(call if_changed,vdsold_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 (borrowed from the 64-bit vDSO) +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-purecap-offsets.h: $(obj)/vdso.so.dbg FORCE
- $(call if_changed,vdsosym)
+# Actual build commands +quiet_cmd_vdsold_and_vdso_check = LD $@
cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check)
+# Install commands for the unstripped file +quiet_cmd_vdso_install = INSTALL $@
cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+vdso.so: $(obj)/vdso.so.dbg
- @mkdir -p $(MODLIB)/vdso
- $(call cmd,vdso_install)
+vdso_install: vdso.so
[...]
diff --git a/include/linux/compiler.h b/include/linux/compiler.h index d7779a18b24f..56e752138ef7 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -4,6 +4,11 @@ #include <linux/compiler_types.h> +#if defined(PURECAP_VDSO) +#include <linux/user_ptr.h> +#include <linux/cheri.h> +#endif
#ifndef __ASSEMBLY__ #ifdef __KERNEL__ @@ -222,7 +227,11 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ static inline void *offset_to_ptr(const int *off) { +#if defined(PURECAP_VDSO)
- return uaddr_to_user_ptr((unsigned long)off + *off);
+#else return (void *)((unsigned long)off + *off);
I doubt this function is ever used in the vDSO, but the nicest thing we can do is to make it purecap-friendly. Fortunately it's actually very simple: just replace unsigned long with uintptr_t. No need for any #ifdef'ing, and this way the additional #include's disappear too. (Note that no user pointer - in the sense of void __user * - is involved here, when built as part of the vDSO all pointers are capabilities so code in included headers needs to be compatible with that.)
Kevin
+#endif } #endif /* __ASSEMBLY__ */ diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 2c2180f0f0c3..21bfe5075141 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -3,6 +3,7 @@ #define _LINUX_USER_PTR_H #include <linux/typecheck.h> +#include <linux/types.h> /**
- as_user_ptr() - Convert an arbitrary integer value to a user pointer.
Provide purecap userspace binaries with a vDSO they can use. Redefine the aarch64 vDSO as the compat vDSO, therefore allowing both regular arm64 and purecap binaries to use a vDSO under PCuABI.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com --- arch/arm64/include/asm/elf.h | 16 ++++++-- arch/arm64/include/asm/vdso.h | 1 + arch/arm64/kernel/Makefile | 4 +- arch/arm64/kernel/vdso-purecap-wrap.S | 22 ++++++++++ arch/arm64/kernel/vdso.c | 58 ++++++++++++++++++++++++++- fs/compat_binfmt_elf.c | 16 ++++++++ include/linux/elf.h | 2 +- 7 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 arch/arm64/kernel/vdso-purecap-wrap.S
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index f01f1f99cf03..86805d6e5180 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -190,17 +190,20 @@ do { \
#ifdef CONFIG_CHERI_PURECAP_UABI /* - * TODO [PCuABI]: In Transitional PCuABI, AT_SYSINFO_EHDR is passed as NULL - * as there is no purecap vDSO yet. + * In Transitional PCuABI, we put a valid unsealed capability into the + * auxiliary vector. + * + * TODO [PCuABI]: Look into restricting the bounds of this capability to just + * the vDSO pages, as currently the bounds are of the root user capability. */ -#define ARCH_DLINFO SETUP_DLINFO(0) +#define ARCH_DLINFO SETUP_DLINFO(elf_uaddr_to_user_ptr((elf_addr_t)current->mm->context.vdso)) #else /* !CONFIG_CHERI_PURECAP_UABI */ #define ARCH_DLINFO SETUP_DLINFO((elf_addr_t)current->mm->context.vdso) #endif /* CONFIG_CHERI_PURECAP_UABI */
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES struct linux_binprm; -extern int arch_setup_additional_pages(struct linux_binprm *bprm, +extern int purecap_setup_additional_pages(struct linux_binprm *bprm, int uses_interp);
/* 1GB of VA */ @@ -244,6 +247,11 @@ typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG];
#define COMPAT_ARCH_DLINFO SETUP_DLINFO((elf_addr_t)current->mm->context.vdso)
+extern int aarch64_setup_additional_pages(struct linux_binprm *bprm, + int uses_interp); +#define compat_arch_setup_additional_pages \ + aarch64_setup_additional_pages + #else /* !CONFIG_COMPAT64 */
/* PIE load location for compat arm. Must match ARM ELF_ET_DYN_BASE. */ diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h index 83a50071a85e..709b5bbec255 100644 --- a/arch/arm64/include/asm/vdso.h +++ b/arch/arm64/include/asm/vdso.h @@ -28,6 +28,7 @@
extern char vdso_start[], vdso_end[]; extern char vdso32_start[], vdso32_end[]; +extern char vdso_purecap_start[], vdso_purecap_end[];
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 12eb4c55234e..934a677c8f69 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -72,12 +72,14 @@ obj-$(CONFIG_ARM64_MTE) += mte.o obj-$(CONFIG_ARM64_MORELLO) += morello.o obj-y += vdso-wrap.o obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o +obj-$(CONFIG_CHERI_PURECAP_UABI) += vdso-purecap-wrap.o obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.o CFLAGS_patch-scs.o += -mbranch-protection=none
# Force dependency (vdso*-wrap.S includes vdso.so through incbin) $(obj)/vdso-wrap.o: $(obj)/vdso/vdso.so $(obj)/vdso32-wrap.o: $(obj)/vdso32/vdso.so +$(obj)/vdso-purecap-wrap.o: $(obj)/vdso-purecap/vdso.so
obj-y += probes/ obj-y += head.o @@ -88,4 +90,4 @@ AFLAGS_head.o += -DVMLINUX_PATH=""$(realpath $(objtree)/vmlinux)"" endif
# for cleaning -subdir- += vdso vdso32 +subdir- += vdso vdso32 vdso-purecap diff --git a/arch/arm64/kernel/vdso-purecap-wrap.S b/arch/arm64/kernel/vdso-purecap-wrap.S new file mode 100644 index 000000000000..b0bac3af4925 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap-wrap.S @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 ARM Limited + */ + +#include <linux/init.h> +#include <linux/linkage.h> +#include <linux/const.h> +#include <asm/assembler.h> +#include <asm/page.h> + + .globl vdso_purecap_start, vdso_purecap_end + .section .rodata + .balign PAGE_SIZE +vdso_purecap_start: + .incbin "arch/arm64/kernel/vdso-purecap/vdso.so" + .balign PAGE_SIZE +vdso_purecap_end: + + .previous + +emit_aarch64_feature_1_and diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index c9d961249894..8f1305128799 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -32,6 +32,7 @@ enum vdso_abi { VDSO_ABI_AA64, VDSO_ABI_AA32, + VDSO_ABI_PURECAP, };
enum vvar_pages { @@ -64,6 +65,13 @@ static struct vdso_abi_info vdso_info[] __ro_after_init = { .vdso_code_end = vdso32_end, }, #endif /* CONFIG_COMPAT_VDSO */ +#ifdef CONFIG_CHERI_PURECAP_UABI + [VDSO_ABI_PURECAP] = { + .name = "vdso_purecap", + .vdso_code_start = vdso_purecap_start, + .vdso_code_end = vdso_purecap_end, + }, +#endif /* CONFIG_CHERI_PURECAP_UABI */ };
/* @@ -79,7 +87,6 @@ static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma) { current->mm->context.vdso = (void *)new_vma->vm_start; - return 0; }
@@ -143,6 +150,10 @@ int vdso_join_timens(struct task_struct *task, struct time_namespace *ns) #ifdef CONFIG_COMPAT_VDSO if (vma_is_special_mapping(vma, vdso_info[VDSO_ABI_AA32].dm)) zap_vma_pages(vma); +#endif +#ifdef CONFIG_CHERI_PURECAP_UABI + if (vma_is_special_mapping(vma, vdso_info[VDSO_ABI_PURECAP].dm)) + zap_vma_pages(vma); #endif }
@@ -194,6 +205,7 @@ static int __setup_additional_pages(enum vdso_abi abi, unsigned long gp_flags = 0; void *ret;
+ BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES);
vdso_text_len = vdso_info[abi].vdso_pages << PAGE_SHIFT; @@ -411,6 +423,48 @@ int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) } #endif /* CONFIG_COMPAT32 */
+#ifdef CONFIG_CHERI_PURECAP_UABI +enum purecap_map { + PURECAP_MAP_VVAR, + PURECAP_MAP_VDSO, +}; + +static struct vm_special_mapping purecap_vdso_maps[] __ro_after_init = { + [PURECAP_MAP_VVAR] = { + .name = "[vvar]", + .fault = vvar_fault, + }, + [PURECAP_MAP_VDSO] = { + .name = "[vdso]", + .mremap = vdso_mremap, + }, +}; +\ +static int __init purecap_vdso_init(void) { + vdso_info[VDSO_ABI_PURECAP].dm = &purecap_vdso_maps[PURECAP_MAP_VVAR]; + vdso_info[VDSO_ABI_PURECAP].cm = &purecap_vdso_maps[PURECAP_MAP_VDSO]; + + return __vdso_init(VDSO_ABI_PURECAP); +} +arch_initcall(purecap_vdso_init); + +int purecap_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +{ + struct mm_struct *mm = current->mm; + int ret; + + if (mmap_write_lock_killable(mm)) + return -EINTR; + + ret = __setup_additional_pages(VDSO_ABI_PURECAP, mm, bprm, uses_interp); + mmap_write_unlock(mm); + + return ret; +} + + +#endif /* CONFIG CHERI PURECAP UABI */ + enum aarch64_map { AA64_MAP_VVAR, AA64_MAP_VDSO, @@ -436,7 +490,7 @@ static int __init vdso_init(void) } arch_initcall(vdso_init);
-int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +int aarch64_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; int ret; diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index b6b453b35946..e22e866d587f 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -150,6 +150,22 @@
#endif /* CONFIG_COMPAT32 */
+#ifdef CONFIG_COMPAT64 + +#ifdef compat_arch_setup_additional_pages +#define COMPAT_ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ + compat_arch_setup_additional_pages(bprm, interpreter) +#endif + +#ifdef COMPAT_ARCH_SETUP_ADDITIONAL_PAGES +#undef ARCH_HAS_SETUP_ADDITIONAL_PAGES +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 +#undef ARCH_SETUP_ADDITIONAL_PAGES +#define ARCH_SETUP_ADDITIONAL_PAGES COMPAT_ARCH_SETUP_ADDITIONAL_PAGES +#endif + +#endif /* CONFIG_COMPAT64*/ + #undef elf_check_arch #define elf_check_arch compat_elf_check_arch
diff --git a/include/linux/elf.h b/include/linux/elf.h index 039ad1867045..2b66123c1f94 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -32,7 +32,7 @@
#if defined(ARCH_HAS_SETUP_ADDITIONAL_PAGES) && !defined(ARCH_SETUP_ADDITIONAL_PAGES) #define ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ - arch_setup_additional_pages(bprm, interpreter) + purecap_setup_additional_pages(bprm, interpreter) #endif
#define ELF32_GNU_PROPERTY_ALIGN 4
Nit: the commit title should ideally be less than 68 characters, which is sometimes difficult but in this case we can certainly "compress" the last 4 words.
On 12/10/2023 12:36, Aditya Deshpande wrote:
Provide purecap userspace binaries with a vDSO they can use. Redefine the aarch64 vDSO as the compat vDSO, therefore allowing both regular arm64 and purecap binaries to use a vDSO under PCuABI.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com
arch/arm64/include/asm/elf.h | 16 ++++++-- arch/arm64/include/asm/vdso.h | 1 + arch/arm64/kernel/Makefile | 4 +- arch/arm64/kernel/vdso-purecap-wrap.S | 22 ++++++++++ arch/arm64/kernel/vdso.c | 58 ++++++++++++++++++++++++++- fs/compat_binfmt_elf.c | 16 ++++++++ include/linux/elf.h | 2 +- 7 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 arch/arm64/kernel/vdso-purecap-wrap.S
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index f01f1f99cf03..86805d6e5180 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -190,17 +190,20 @@ do { \ #ifdef CONFIG_CHERI_PURECAP_UABI /*
- TODO [PCuABI]: In Transitional PCuABI, AT_SYSINFO_EHDR is passed as NULL
- as there is no purecap vDSO yet.
- In Transitional PCuABI, we put a valid unsealed capability into the
- auxiliary vector.
I think we can skip this, this is not about the transitional ABI as it precisely did not include a vDSO.
- TODO [PCuABI]: Look into restricting the bounds of this capability to just
*/
- the vDSO pages, as currently the bounds are of the root user capability.
-#define ARCH_DLINFO SETUP_DLINFO(0) +#define ARCH_DLINFO SETUP_DLINFO(elf_uaddr_to_user_ptr((elf_addr_t)current->mm->context.vdso))
elf_uaddr_to_user_ptr() is only useful for code shared between native and compat. Since this is only used in native, we can use uaddr_to_user_ptr_safe() directly (while waiting for the bounds to be narrowed).
[...]
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index c9d961249894..8f1305128799 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -32,6 +32,7 @@ enum vdso_abi { VDSO_ABI_AA64, VDSO_ABI_AA32,
- VDSO_ABI_PURECAP,
}; enum vvar_pages { @@ -64,6 +65,13 @@ static struct vdso_abi_info vdso_info[] __ro_after_init = { .vdso_code_end = vdso32_end, }, #endif /* CONFIG_COMPAT_VDSO */ +#ifdef CONFIG_CHERI_PURECAP_UABI
- [VDSO_ABI_PURECAP] = {
.name = "vdso_purecap",
.vdso_code_start = vdso_purecap_start,
.vdso_code_end = vdso_purecap_end,
- },
+#endif /* CONFIG_CHERI_PURECAP_UABI */ }; /* @@ -79,7 +87,6 @@ static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma) { current->mm->context.vdso = (void *)new_vma->vm_start;
Spurious change, also in __setup_additional_pages().
return 0; } @@ -143,6 +150,10 @@ int vdso_join_timens(struct task_struct *task, struct time_namespace *ns) #ifdef CONFIG_COMPAT_VDSO if (vma_is_special_mapping(vma, vdso_info[VDSO_ABI_AA32].dm)) zap_vma_pages(vma); +#endif +#ifdef CONFIG_CHERI_PURECAP_UABI
if (vma_is_special_mapping(vma, vdso_info[VDSO_ABI_PURECAP].dm))
zap_vma_pages(vma);
#endif } @@ -194,6 +205,7 @@ static int __setup_additional_pages(enum vdso_abi abi, unsigned long gp_flags = 0; void *ret;
- BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES);
vdso_text_len = vdso_info[abi].vdso_pages << PAGE_SHIFT; @@ -411,6 +423,48 @@ int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) } #endif /* CONFIG_COMPAT32 */ +#ifdef CONFIG_CHERI_PURECAP_UABI +enum purecap_map {
- PURECAP_MAP_VVAR,
- PURECAP_MAP_VDSO,
+};
+static struct vm_special_mapping purecap_vdso_maps[] __ro_after_init = {
- [PURECAP_MAP_VVAR] = {
.name = "[vvar]",
.fault = vvar_fault,
- },
- [PURECAP_MAP_VDSO] = {
.name = "[vdso]",
.mremap = vdso_mremap,
- },
+}; +\
That \ probably wasn't meant to be there :D
+static int __init purecap_vdso_init(void) {
- vdso_info[VDSO_ABI_PURECAP].dm = &purecap_vdso_maps[PURECAP_MAP_VVAR];
- vdso_info[VDSO_ABI_PURECAP].cm = &purecap_vdso_maps[PURECAP_MAP_VDSO];
- return __vdso_init(VDSO_ABI_PURECAP);
+} +arch_initcall(purecap_vdso_init);
+int purecap_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +{
- struct mm_struct *mm = current->mm;
- int ret;
- if (mmap_write_lock_killable(mm))
return -EINTR;
- ret = __setup_additional_pages(VDSO_ABI_PURECAP, mm, bprm, uses_interp);
- mmap_write_unlock(mm);
- return ret;
+}
+#endif /* CONFIG CHERI PURECAP UABI */
enum aarch64_map { AA64_MAP_VVAR, AA64_MAP_VDSO, @@ -436,7 +490,7 @@ static int __init vdso_init(void) } arch_initcall(vdso_init); -int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +int aarch64_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; int ret; diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index b6b453b35946..e22e866d587f 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -150,6 +150,22 @@ #endif /* CONFIG_COMPAT32 */ +#ifdef CONFIG_COMPAT64
+#ifdef compat_arch_setup_additional_pages +#define COMPAT_ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \
- compat_arch_setup_additional_pages(bprm, interpreter)
+#endif
+#ifdef COMPAT_ARCH_SETUP_ADDITIONAL_PAGES +#undef ARCH_HAS_SETUP_ADDITIONAL_PAGES +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 +#undef ARCH_SETUP_ADDITIONAL_PAGES +#define ARCH_SETUP_ADDITIONAL_PAGES COMPAT_ARCH_SETUP_ADDITIONAL_PAGES +#endif
These definitions were #ifdef'ed out in compat64 by [1]. Now that we want them in compat64 too, we should move them to the common part of the file (the one that is not #ifdef'ed), instead of duplicating them for compat64. The appropriate place, to preserve the original order, would be at the end, just before #include "binfmt_elf.c".
[1] https://git.morello-project.org/morello/kernel/linux/-/commit/84080b0
+#endif /* CONFIG_COMPAT64*/
#undef elf_check_arch #define elf_check_arch compat_elf_check_arch diff --git a/include/linux/elf.h b/include/linux/elf.h index 039ad1867045..2b66123c1f94 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -32,7 +32,7 @@ #if defined(ARCH_HAS_SETUP_ADDITIONAL_PAGES) && !defined(ARCH_SETUP_ADDITIONAL_PAGES) #define ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \
- arch_setup_additional_pages(bprm, interpreter)
- purecap_setup_additional_pages(bprm, interpreter)
Such a generic hook should not be changed, as it will break all other architectures (and even arm64 in !PCuABI) right away. Using a similar approach as for compat would be appropriate: make arch_setup_additional_pages a macro and define it as either the standard arm64 one or the purecap one depending on CONFIG_CHERI_PURECAP_UABI.
Kevin
#endif #define ELF32_GNU_PROPERTY_ALIGN 4
On 12/10/2023 12:36, Aditya Deshpande wrote:
Hello,
This patch series implements a pure-capability vDSO which purecap userspace applications can directly use. It also fixes the vDSO selftests so that they can be built as purecap binaries, therefore allowing the new purecap vDSO to be tested. The existing aarch64 vDSO implementation is unchanged - it has simply been redefined as the compat vDSO. Therefore, processes using both the standard and purecap ABIs will have a vDSO to use.
I have tested both vDSOs using the patched versions of the selftests. I have tested the selftests by by feeding it broken vDSOs and seeing ensuring they fail appropriately.
The first patch fixes the tests, the second patch builds the purecap vDSO, and the third patch sets up the mapping for it and makes it available for use. If you think the patches should be ordered differently, and/or if we should possibly combine the building and mapping patches into one vDSO implementation patch, please let me know.
Thanks, Aditya
GitLab Issue:
GitLab Review Branch:
Aditya Deshpande (3): selftests/vDSO: Add support for purecap vDSO testing arm64: vdso: Build a pure-capability vDSO arm64: vDSO: Map the purecap vDSO into userspace application address space
Well done, this is a very good start! The patches look correct, and logically split; once the duplication is reduced (see comments inline on each patch) they should be in a good shape for merging.
Kevin
arch/arm64/Makefile | 6 + arch/arm64/include/asm/elf.h | 16 ++- arch/arm64/include/asm/vdso.h | 1 + arch/arm64/include/asm/vdso/gettimeofday.h | 92 ++++++++++++++ arch/arm64/kernel/Makefile | 4 +- arch/arm64/kernel/vdso-purecap-wrap.S | 22 ++++ arch/arm64/kernel/vdso-purecap/.gitignore | 2 + arch/arm64/kernel/vdso-purecap/Makefile | 101 ++++++++++++++++ arch/arm64/kernel/vdso-purecap/note.S | 23 ++++ arch/arm64/kernel/vdso-purecap/sigreturn.S | 80 ++++++++++++ arch/arm64/kernel/vdso-purecap/vdso.lds.S | 114 ++++++++++++++++++ .../arm64/kernel/vdso-purecap/vgettimeofday.c | 25 ++++ arch/arm64/kernel/vdso.c | 58 ++++++++- fs/compat_binfmt_elf.c | 16 +++ include/linux/compiler.h | 9 ++ include/linux/elf.h | 2 +- include/linux/user_ptr.h | 1 + tools/testing/selftests/vDSO/Makefile | 16 +-- tools/testing/selftests/vDSO/vdso_config.h | 11 ++ tools/testing/selftests/vDSO/vdso_test_abi.c | 4 +- .../selftests/vDSO/vdso_test_gettimeofday.c | 6 +- 21 files changed, 587 insertions(+), 22 deletions(-) create mode 100644 arch/arm64/kernel/vdso-purecap-wrap.S create mode 100644 arch/arm64/kernel/vdso-purecap/.gitignore create mode 100644 arch/arm64/kernel/vdso-purecap/Makefile create mode 100644 arch/arm64/kernel/vdso-purecap/note.S create mode 100644 arch/arm64/kernel/vdso-purecap/sigreturn.S create mode 100644 arch/arm64/kernel/vdso-purecap/vdso.lds.S create mode 100644 arch/arm64/kernel/vdso-purecap/vgettimeofday.c
linux-morello@op-lists.linaro.org