]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
um: Discover host_task_size from envp
authorBenjamin Berg <benjamin.berg@intel.com>
Thu, 19 Sep 2024 12:45:09 +0000 (14:45 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 10 Oct 2024 11:37:22 +0000 (13:37 +0200)
When loading the UML binary, the host kernel will place the stack at the
highest possible address. It will then map the program name and
environment variables onto the start of the stack.

As such, an easy way to figure out the host_task_size is to use the
highest pointer to an environment variable as a reference.

Ensure that this works by disabling address layout randomization and
re-executing UML in case it was enabled.

This increases the available TASK_SIZE for 64 bit UML considerably.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Link: https://patch.msgid.link/20240919124511.282088-9-benjamin@sipsolutions.net
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
arch/um/include/shared/as-layout.h
arch/um/include/shared/os.h
arch/um/kernel/um_arch.c
arch/um/os-Linux/main.c
arch/x86/um/os-Linux/Makefile
arch/x86/um/os-Linux/task_size.c [deleted file]

index 283226c34ca47b74949bb7c41c047988b41afc46..d9679c911e54229e4fc514f20fc938a2dad0904b 100644 (file)
@@ -49,7 +49,7 @@ extern unsigned long brk_start;
 extern unsigned long host_task_size;
 extern unsigned long stub_start;
 
-extern int linux_main(int argc, char **argv);
+extern int linux_main(int argc, char **argv, char **envp);
 extern void uml_finishsetup(void);
 
 struct siginfo;
index b511637e1f76dbe9639a40f9bfcbee31f14db234..bf539fee78316d6ce9a386dab40c4433e438c2c0 100644 (file)
@@ -325,9 +325,6 @@ extern int __ignore_sigio_fd(int fd);
 /* tty.c */
 extern int get_pty(void);
 
-/* sys-$ARCH/task_size.c */
-extern unsigned long os_get_top_address(void);
-
 long syscall(long number, ...);
 
 /* irqflags tracing */
index 4452df4f2c4b48cafb371b911c917dd4f785a376..38cbb41a64bc3a1893b87b24fe8c99dbc79a7cb0 100644 (file)
@@ -302,7 +302,24 @@ static void parse_cache_line(char *line)
        }
 }
 
-int __init linux_main(int argc, char **argv)
+static unsigned long get_top_address(char **envp)
+{
+       unsigned long top_addr = (unsigned long) &top_addr;
+       int i;
+
+       /* The earliest variable should be after the program name in ELF */
+       for (i = 0; envp[i]; i++) {
+               if ((unsigned long) envp[i] > top_addr)
+                       top_addr = (unsigned long) envp[i];
+       }
+
+       top_addr &= ~(UM_KERN_PAGE_SIZE - 1);
+       top_addr += UM_KERN_PAGE_SIZE;
+
+       return top_addr;
+}
+
+int __init linux_main(int argc, char **argv, char **envp)
 {
        unsigned long avail, diff;
        unsigned long virtmem_size, max_physmem;
@@ -324,7 +341,7 @@ int __init linux_main(int argc, char **argv)
        if (have_console == 0)
                add_arg(DEFAULT_COMMAND_LINE_CONSOLE);
 
-       host_task_size = os_get_top_address();
+       host_task_size = get_top_address(envp);
        /* reserve a few pages for the stubs */
        stub_start = host_task_size - STUB_DATA_PAGES * PAGE_SIZE;
        /* another page for the code portion */
index cf1179ed1aecfdc3edaa6049907fd726f6efdcb7..8a52c49c53615352e75fb55efb7fc6bb5e616567 100644 (file)
@@ -11,6 +11,7 @@
 #include <signal.h>
 #include <string.h>
 #include <sys/resource.h>
+#include <sys/personality.h>
 #include <as-layout.h>
 #include <init.h>
 #include <kern_util.h>
@@ -108,6 +109,12 @@ int __init main(int argc, char **argv, char **envp)
        char **new_argv;
        int ret, i, err;
 
+       /* Disable randomization and re-exec if it was changed successfully */
+       ret = personality(PER_LINUX | ADDR_NO_RANDOMIZE);
+       if (ret >= 0 && (ret & (PER_LINUX | ADDR_NO_RANDOMIZE)) !=
+                        (PER_LINUX | ADDR_NO_RANDOMIZE))
+               execve("/proc/self/exe", argv, envp);
+
        set_stklim();
 
        setup_env_path();
@@ -140,7 +147,7 @@ int __init main(int argc, char **argv, char **envp)
 #endif
 
        change_sig(SIGPIPE, 0);
-       ret = linux_main(argc, argv);
+       ret = linux_main(argc, argv, envp);
 
        /*
         * Disable SIGPROF - I have no idea why libc doesn't do this or turn
index 5249bbc30dcdb107ed45d7f0d76328f650308f28..77a308aaa5ec9afdc7e5e463220abf8c8a8e018d 100644 (file)
@@ -3,7 +3,7 @@
 # Licensed under the GPL
 #
 
-obj-y = registers.o task_size.o mcontext.o
+obj-y = registers.o mcontext.o
 
 obj-$(CONFIG_X86_32) += tls.o
 
diff --git a/arch/x86/um/os-Linux/task_size.c b/arch/x86/um/os-Linux/task_size.c
deleted file mode 100644 (file)
index 1dc9adc..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <longjmp.h>
-
-#ifdef __i386__
-
-static jmp_buf buf;
-
-static void segfault(int sig)
-{
-       longjmp(buf, 1);
-}
-
-static int page_ok(unsigned long page)
-{
-       unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
-       unsigned long n = ~0UL;
-       void *mapped = NULL;
-       int ok = 0;
-
-       /*
-        * First see if the page is readable.  If it is, it may still
-        * be a VDSO, so we go on to see if it's writable.  If not
-        * then try mapping memory there.  If that fails, then we're
-        * still in the kernel area.  As a sanity check, we'll fail if
-        * the mmap succeeds, but gives us an address different from
-        * what we wanted.
-        */
-       if (setjmp(buf) == 0)
-               n = *address;
-       else {
-               mapped = mmap(address, UM_KERN_PAGE_SIZE,
-                             PROT_READ | PROT_WRITE,
-                             MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-               if (mapped == MAP_FAILED)
-                       return 0;
-               if (mapped != address)
-                       goto out;
-       }
-
-       /*
-        * Now, is it writeable?  If so, then we're in user address
-        * space.  If not, then try mprotecting it and try the write
-        * again.
-        */
-       if (setjmp(buf) == 0) {
-               *address = n;
-               ok = 1;
-               goto out;
-       } else if (mprotect(address, UM_KERN_PAGE_SIZE,
-                           PROT_READ | PROT_WRITE) != 0)
-               goto out;
-
-       if (setjmp(buf) == 0) {
-               *address = n;
-               ok = 1;
-       }
-
- out:
-       if (mapped != NULL)
-               munmap(mapped, UM_KERN_PAGE_SIZE);
-       return ok;
-}
-
-unsigned long os_get_top_address(void)
-{
-       struct sigaction sa, old;
-       unsigned long bottom = 0;
-       /*
-        * A 32-bit UML on a 64-bit host gets confused about the VDSO at
-        * 0xffffe000.  It is mapped, is readable, can be reprotected writeable
-        * and written.  However, exec discovers later that it can't be
-        * unmapped.  So, just set the highest address to be checked to just
-        * below it.  This might waste some address space on 4G/4G 32-bit
-        * hosts, but shouldn't hurt otherwise.
-        */
-       unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
-       unsigned long test, original;
-
-       printf("Locating the bottom of the address space ... ");
-       fflush(stdout);
-
-       /*
-        * We're going to be longjmping out of the signal handler, so
-        * SA_DEFER needs to be set.
-        */
-       sa.sa_handler = segfault;
-       sigemptyset(&sa.sa_mask);
-       sa.sa_flags = SA_NODEFER;
-       if (sigaction(SIGSEGV, &sa, &old)) {
-               perror("os_get_top_address");
-               exit(1);
-       }
-
-       /* Manually scan the address space, bottom-up, until we find
-        * the first valid page (or run out of them).
-        */
-       for (bottom = 0; bottom < top; bottom++) {
-               if (page_ok(bottom))
-                       break;
-       }
-
-       /* If we've got this far, we ran out of pages. */
-       if (bottom == top) {
-               fprintf(stderr, "Unable to determine bottom of address "
-                       "space.\n");
-               exit(1);
-       }
-
-       printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT);
-       printf("Locating the top of the address space ... ");
-       fflush(stdout);
-
-       original = bottom;
-
-       /* This could happen with a 4G/4G split */
-       if (page_ok(top))
-               goto out;
-
-       do {
-               test = bottom + (top - bottom) / 2;
-               if (page_ok(test))
-                       bottom = test;
-               else
-                       top = test;
-       } while (top - bottom > 1);
-
-out:
-       /* Restore the old SIGSEGV handling */
-       if (sigaction(SIGSEGV, &old, NULL)) {
-               perror("os_get_top_address");
-               exit(1);
-       }
-       top <<= UM_KERN_PAGE_SHIFT;
-       printf("0x%lx\n", top);
-
-       return top;
-}
-
-#else
-
-unsigned long os_get_top_address(void)
-{
-       /* The old value of CONFIG_TOP_ADDR */
-       return 0x7fc0002000;
-}
-
-#endif