Re: [PATCH 3/3] mm: Care about shadow stack guard gap when getting an unmapped area
From: Liam R. Howlett
Date: Tue Sep 03 2024 - 15:43:31 EST
* Mark Brown <broonie@xxxxxxxxxx> [240902 15:09]:
> As covered in the commit log for c44357c2e76b ("x86/mm: care about shadow
> stack guard gap during placement") our current mmap() implementation does
> not take care to ensure that a new mapping isn't placed with existing
> mappings inside it's own guard gaps. This is particularly important for
> shadow stacks since if two shadow stacks end up getting placed adjacent to
> each other then they can overflow into each other which weakens the
> protection offered by the feature.
>
> On x86 there is a custom arch_get_unmapped_area() which was updated by the
> above commit to cover this case by specifying a start_gap for allocations
> with VM_SHADOW_STACK. Both arm64 and RISC-V have equivalent features and
> use the generic implementation of arch_get_unmapped_area() so let's make
> the equivalent change there so they also don't get shadow stack pages
> placed without guard pages.
>
> Architectures which do not have this feature will define VM_SHADOW_STACK
> to VM_NONE and hence be unaffected.
>
> Suggested-by: Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx>
> Signed-off-by: Mark Brown <broonie@xxxxxxxxxx>
> ---
> mm/mmap.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/mm/mmap.c b/mm/mmap.c
> index b06ba847c96e..902c482b6084 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -1753,6 +1753,14 @@ static unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info)
> return gap;
> }
>
> +static inline unsigned long stack_guard_placement(vm_flags_t vm_flags)
> +{
> + if (vm_flags & VM_SHADOW_STACK)
> + return PAGE_SIZE;
Is PAGE_SIZE is enough?
> +
> + return 0;
> +}
> +
> /*
> * Search for an unmapped address range.
> *
> @@ -1814,6 +1822,7 @@ generic_get_unmapped_area(struct file *filp, unsigned long addr,
> info.length = len;
> info.low_limit = mm->mmap_base;
> info.high_limit = mmap_end;
> + info.start_gap = stack_guard_placement(vm_flags);
> return vm_unmapped_area(&info);
> }
>
> @@ -1863,6 +1872,7 @@ generic_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
> info.length = len;
> info.low_limit = PAGE_SIZE;
> info.high_limit = arch_get_mmap_base(addr, mm->mmap_base);
> + info.start_gap = stack_guard_placement(vm_flags);
> addr = vm_unmapped_area(&info);
>
> /*
>
> --
> 2.39.2
>