The stack needs special handling in PCuABI to ensure that its underlying reservation is appropriately sized. A new sysctl "cheri.max_stack_size" is introduced to that effect, as per the PCuABI specification, specifying the reservation size (before alignment) unless the hard limit of RLIMIT_STACK is lower (unlimited by default).
The default value for cheri.max_stack_size (128 MB) should be plenty for any application. The minimum value (256 KB) was chosen to leave a safe amount of space over the initial stack mapping expansion (128 KB, see setup_arg_pages()). The maximum value is fairly arbitrary, but has the advantage of fitting in an int.
acct_stack_growth() is also modified to prevent a mapping with VM_GROWSDOWN from growing beyond its reservation (for this reason the reservation details are set before calling expand_stack_locked()). This is only used for the main stack, as PROT_GROWSDOWN is not supported in PCuABI.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- fs/exec.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mm/mmap.c | 3 +++ 2 files changed, 62 insertions(+)
diff --git a/fs/exec.c b/fs/exec.c index 48b29402f838..cabb0560877e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -66,6 +66,9 @@ #include <linux/coredump.h> #include <linux/time_namespace.h> #include <linux/user_events.h> +#include <linux/mm_reserv.h> +#include <linux/cheri.h> +#include <linux/mman.h>
#include <linux/uaccess.h> #include <asm/mmu_context.h> @@ -113,6 +116,56 @@ bool path_noexec(const struct path *path) (path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC); }
+#ifdef CONFIG_CHERI_PURECAP_UABI +static int cheri_max_stack_size = 128 * 1024 * 1024; +static int __cheri_max_stack_size_min = 256 * 1024; +static int __cheri_max_stack_size_max = 1024 * 1024 * 1024; + +static struct ctl_table pcuabi_sysctls[] = { + { + .procname = "max_stack_size", + .mode = 0644, + .data = &cheri_max_stack_size, + .maxlen = sizeof(int), + .proc_handler = proc_dointvec_minmax, + .extra1 = &__cheri_max_stack_size_min, + .extra2 = &__cheri_max_stack_size_max, + }, + { } +}; + +static void register_pcuabi_sysctls(void) +{ + register_sysctl_init("cheri", pcuabi_sysctls); +} + +static int set_stack_reserv(struct vm_area_struct *vma, + struct linux_binprm *bprm) +{ + size_t rlim_stack_max = bprm->rlim_stack.rlim_max; + ptraddr_t start; + size_t len; + + len = min(rlim_stack_max, (size_t)cheri_max_stack_size); + + start = (vma->vm_end - len) & cheri_representable_alignment_mask(len); + /* + * vma->vm_end must remain unchanged, so after aligning down the start + * address, we need to recalculate the length. + */ + len = (vma->vm_end - start); + return reserv_vma_set_reserv(vma, start, len, PROT_READ | PROT_WRITE); +} +#else +static inline void register_pcuabi_sysctls(void) {} + +static inline int set_stack_reserv(struct vm_area_struct *vma, + struct linux_binprm *bprm) +{ + return 0; +} +#endif + #ifdef CONFIG_USELIB /* * Note that a shared library must be both readable and executable due to @@ -862,6 +915,11 @@ int setup_arg_pages(struct linux_binprm *bprm, stack_base = vma->vm_end - stack_expand; #endif current->mm->start_stack = bprm->p; + + ret = set_stack_reserv(vma, bprm); + if (ret) + goto out_unlock; + ret = expand_stack_locked(vma, stack_base); if (ret) ret = -EFAULT; @@ -2195,6 +2253,7 @@ static struct ctl_table fs_exec_sysctls[] = { static int __init init_fs_exec_sysctls(void) { register_sysctl_init("fs", fs_exec_sysctls); + register_pcuabi_sysctls(); return 0; }
diff --git a/mm/mmap.c b/mm/mmap.c index 40d64fa163a2..886a729fb13c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1973,6 +1973,9 @@ static int acct_stack_growth(struct vm_area_struct *vma, if (size > rlimit(RLIMIT_STACK)) return -ENOMEM;
+ if (reserv_is_supported(mm) && size > reserv_vma_reserv_len(vma)) + return -ERESERVATION; + /* mlock limit tests */ if (!mlock_future_ok(mm, vma->vm_flags, grow << PAGE_SHIFT)) return -ENOMEM;