[PATCH 08/16] x86: mm: Support MAP_BELOW_HINT

Charlie Jenkins
2024-08-28T08:00:43Z

Add support for MAP_BELOW_HINT to mmap by restricting high_limit to
addr when the flag is enabled.

Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
---
arch/x86/kernel/sys_x86_64.c | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
index 01d7cd85ef97..fa16b38f3702 100644
--- a/arch/x86/kernel/sys_x86_64.c
+++ b/arch/x86/kernel/sys_x86_64.c
@@ -86,7 +86,7 @@ SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}

-static void find_start_end(unsigned long addr, unsigned long flags,
+static void find_start_end(unsigned long addr, unsigned long len, unsigned long flags,
unsigned long *begin, unsigned long *end)
{
if (!in_32bit_syscall() && (flags & MAP_32BIT)) {
@@ -106,10 +106,14 @@ static void find_start_end(unsigned long addr, unsigned long flags,
}

*begin = get_mmap_base(1);
+
if (in_32bit_syscall())
*end = task_size_32bit();
else
*end = task_size_64bit(addr > DEFAULT_MAP_WINDOW);
+
+ if (flags & MAP_BELOW_HINT)
+ *end = MIN(*end, addr + len);
}

static inline unsigned long stack_guard_placement(vm_flags_t vm_flags)
@@ -132,7 +136,7 @@ arch_get_unmapped_area_vmflags(struct file *filp, unsigned long addr, unsigned l
if (flags & MAP_FIXED)
return addr;

- find_start_end(addr, flags, &begin, &end);
+ find_start_end(addr, len, flags, &begin, &end);

if (len > end)
return -ENOMEM;
@@ -166,6 +170,7 @@ arch_get_unmapped_area_topdown_vmflags(struct file *filp, unsigned long addr0,
struct mm_struct *mm = current->mm;
unsigned long addr = addr0;
struct vm_unmapped_area_info info = {};
+ unsigned long task_size, mmap_base;

/* requested length too big for entire address space */
if (len > TASK_SIZE)
@@ -198,7 +203,8 @@ arch_get_unmapped_area_topdown_vmflags(struct file *filp, unsigned long addr0,
else
info.low_limit = PAGE_SIZE;

- info.high_limit = get_mmap_base(0);
+ mmap_base = get_mmap_base(0);
+ info.high_limit = mmap_base;
info.start_gap = stack_guard_placement(vm_flags);

/*
@@ -210,6 +216,19 @@ arch_get_unmapped_area_topdown_vmflags(struct file *filp, unsigned long addr0,
*/
if (addr > DEFAULT_MAP_WINDOW && !in_32bit_syscall())
info.high_limit += TASK_SIZE_MAX - DEFAULT_MAP_WINDOW;
+ if (flags & MAP_BELOW_HINT) {
+#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
+ task_size = task_size_32bit();
+#else
+ task_size = task_size_64bit(0);
+#endif
+ /*
+ * mmap_base is defined by PAGE_ALIGN(task_size - gap - rnd) so
+ * subtract out the task_size to isolate the gap + rnd.
+ */
+ info.high_limit = MIN(info.high_limit,
+ (addr + len) - (task_size - mmap_base));
+ }

info.align_offset = pgoff << PAGE_SHIFT;
if (filp) {

--
2.45.0