The bprm struct now contains the full initial user pointers (PCC, CSP, argv/envp/auxv) as calculated by binfmt_elf, so we can use them to set pcc, csp and c1-c3 in start_thread() in PCuABI. This also ensures that the capability metadata of argv/envp is the same in the auxiliary vector and c1-c2.
To centralise the PCuABI handling in morello.c, x0 and c1-c3 are now set in morello_thread_start() instead of the separate init_gp_regs().
start_thread() had to be moved to process.c as it is not possible to include linux/binfmts.h from asm/processor.h (circular reference), in order to obtain the definition of struct linux_binprm.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/asm/elf.h | 2 +- arch/arm64/include/asm/morello.h | 4 ++- arch/arm64/include/asm/processor.h | 47 +++--------------------------- arch/arm64/kernel/morello.c | 27 ++++++++++++++--- arch/arm64/kernel/process.c | 17 +++++++++++ 5 files changed, 48 insertions(+), 49 deletions(-)
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 63f6f50599e6..7d253fc3961c 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -135,7 +135,7 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG]; typedef struct user_fpsimd_state elf_fpregset_t;
#define START_THREAD(elf_ex, regs, elf_entry, bprm) \ - start_thread(regs, elf_entry, bprm->p, bprm->argc, bprm->envc) + start_thread(regs, elf_entry, bprm)
#define SET_PERSONALITY_AARCH64() \ ({ \ diff --git a/arch/arm64/include/asm/morello.h b/arch/arm64/include/asm/morello.h index da198622b35e..df85e838ffdf 100644 --- a/arch/arm64/include/asm/morello.h +++ b/arch/arm64/include/asm/morello.h @@ -10,6 +10,7 @@ struct pt_regs; struct task_struct; struct user_cap; +struct linux_binprm;
#ifdef CONFIG_ARM64_MORELLO
@@ -54,7 +55,8 @@ void morello_thread_set_csp(struct pt_regs *regs, user_uintptr_t sp); * Any invalid usage will result in an error at link time. */
-void morello_thread_start(struct pt_regs *regs, unsigned long pc); +int morello_thread_start(struct pt_regs *regs, unsigned long pc, + struct linux_binprm *bprm); void morello_thread_init_user(void); void morello_thread_save_user_state(struct task_struct *tsk); void morello_thread_restore_user_state(struct task_struct *tsk); diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index ab02589247f4..c8b91f1a6b88 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -267,35 +267,6 @@ void tls_preserve_current_state(void); .fpsimd_cpu = NR_CPUS, \ }
-static inline int init_gp_regs(struct pt_regs *regs, unsigned long sp, - int argc, int envc) -{ - int retval = 0; -#ifdef CONFIG_CHERI_PURECAP_UABI - unsigned long argv, envp, auxv; - /* - * TODO [PCuABI]: - * - When argv/envp/auxv is moved off the stack, update the registers x1-x3 - * with the new pointer values, and ensure c1-c3 contain appropriate - * capabilities (currently set to csp). - * - Restrict bounds/perms of c1-c3. - * - * In ret_to_user c regs are first loaded then merged with x regs if their values - * are different. Hence we load capabilities in c regs and the value in x regs. - */ - retval = argc; /* Placed in x0 */ - argv = sp + 1 * sizeof(user_uintptr_t); /* Increment past argc on stack */ - envp = argv + (argc + 1) * sizeof(user_uintptr_t); /* Go past arg vals + NULL */ - auxv = envp + (envc + 1) * sizeof(user_uintptr_t); /* Go past env vals + NULL */ - regs->cregs[1] = regs->cregs[2] = regs->cregs[3] = regs->csp; - regs->regs[1] = argv; - regs->regs[2] = envp; - regs->regs[3] = auxv; -#endif /* CONFIG_CHERI_PURECAP_UABI */ - - return retval; -} - static inline void start_thread_common(struct pt_regs *regs, unsigned long pc) { s32 previous_syscall = regs->syscallno; @@ -307,19 +278,8 @@ static inline void start_thread_common(struct pt_regs *regs, unsigned long pc) regs->pmr_save = GIC_PRIO_IRQON; }
-static inline int start_thread(struct pt_regs *regs, unsigned long pc, - unsigned long sp, int argc, int envc) -{ - start_thread_common(regs, pc); - regs->pstate = PSR_MODE_EL0t; - spectre_v4_enable_task_mitigation(current); - regs->sp = sp; - - if (system_supports_morello()) - morello_thread_start(regs, pc); - - return init_gp_regs(regs, sp, argc, envc); -} +int start_thread(struct pt_regs *regs, unsigned long pc, + struct linux_binprm *bprm);
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT64 @@ -333,7 +293,8 @@ static inline void compat_start_thread(struct pt_regs *regs, unsigned long pc, regs->sp = sp;
if (system_supports_morello()) - morello_thread_start(regs, pc); + /* The third argument is only used in PCuABI */ + morello_thread_start(regs, pc, NULL); }
#else /* CONFIG_COMPAT64 */ diff --git a/arch/arm64/kernel/morello.c b/arch/arm64/kernel/morello.c index 7e14ff42db68..ae5977ca6420 100644 --- a/arch/arm64/kernel/morello.c +++ b/arch/arm64/kernel/morello.c @@ -5,6 +5,7 @@
#define pr_fmt(fmt) "morello: " fmt
+#include <linux/binfmts.h> #include <linux/cache.h> #include <linux/capability.h> #include <linux/cheri.h> @@ -65,6 +66,14 @@ static void update_regs_c64(struct pt_regs *regs, unsigned long pc) } }
+#ifdef CONFIG_CHERI_PURECAP_UABI +static void set_creg_user_ptr(struct pt_regs *regs, int r, void __user *val) +{ + regs->regs[r] = user_ptr_addr(val); + regs->cregs[r] = (uintcap_t)val; +} +#endif + void morello_cap_get_val_tag(uintcap_t cap, __uint128_t *val, u8 *tag) { *((uintcap_t *)val) = cheri_tag_clear(cap); @@ -86,8 +95,11 @@ uintcap_t morello_build_any_user_cap(const __uint128_t *val, u8 tag) return cap; }
-void morello_thread_start(struct pt_regs *regs, unsigned long pc) +int morello_thread_start(struct pt_regs *regs, unsigned long pc, + struct linux_binprm *bprm) { + int ret = 0; + update_regs_c64(regs, pc);
/* @@ -96,14 +108,21 @@ void morello_thread_start(struct pt_regs *regs, unsigned long pc) * register merging automatically happens during ret_to_user. */ if (is_pure_task()) { - /* TODO [PCuABI] - Adjust the bounds/permissions properly */ - regs->pcc = cheri_user_root_cap; +#ifdef CONFIG_CHERI_PURECAP_UABI + regs->pcc = (uintcap_t)bprm->pcuabi.pcc; + regs->csp = (uintcap_t)bprm->pcuabi.csp;
- regs->csp = cheri_user_root_cap; + ret = bprm->argc; /* Set x0 */ + set_creg_user_ptr(regs, 1, bprm->pcuabi.argv); + set_creg_user_ptr(regs, 2, bprm->pcuabi.envp); + set_creg_user_ptr(regs, 3, bprm->pcuabi.auxv); +#endif } else /* Hybrid */ { regs->pcc = cheri_user_root_allperms_cap; /* CSP is null-derived in hybrid */ } + + return ret; }
void morello_thread_init_user(void) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 03b48b6bc9a3..f560f92c4c3b 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -41,6 +41,7 @@ #include <linux/thread_info.h> #include <linux/prctl.h> #include <linux/stacktrace.h> +#include <linux/binfmts.h>
#include <asm/alternative.h> #include <asm/compat.h> @@ -456,6 +457,22 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) return 0; }
+int start_thread(struct pt_regs *regs, unsigned long pc, + struct linux_binprm *bprm) +{ + int ret = 0; + + start_thread_common(regs, pc); + regs->pstate = PSR_MODE_EL0t; + spectre_v4_enable_task_mitigation(current); + regs->sp = bprm->p; + + if (system_supports_morello()) + ret = morello_thread_start(regs, pc, bprm); + + return ret; +} + void tls_preserve_current_state(void) { task_save_user_tls(current);