ソースを参照

Kernel: Enable SMAP protection during the execve() syscall

The userspace execve() wrapper now measures all the strings and puts
them in a neat and tidy structure on the stack.

This way we know exactly how much to copy in the kernel, and we don't
have to use the SMAP-violating validate_read_str(). :^)
Andreas Kling 5 年 前
コミット
952bb95baa
4 ファイル変更76 行追加36 行削除
  1. 33 34
      Kernel/Process.cpp
  2. 1 1
      Kernel/Process.h
  3. 16 0
      Kernel/Syscall.h
  4. 26 1
      Libraries/LibC/unistd.cpp

+ 33 - 34
Kernel/Process.cpp

@@ -909,50 +909,49 @@ int Process::exec(String path, Vector<String> arguments, Vector<String> environm
     return 0;
 }
 
-int Process::sys$execve(const char* filename, const char** argv, const char** envp)
+int Process::sys$execve(const Syscall::SC_execve_params* user_params)
 {
-    SmapDisabler disabler;
     // NOTE: Be extremely careful with allocating any kernel memory in exec().
     //       On success, the kernel stack will be lost.
-    if (!validate_read_str(filename))
+    Syscall::SC_execve_params params;
+    if (!validate_read_typed(user_params))
+        return -EFAULT;
+    copy_from_user(&params, user_params, sizeof(params));
+
+    if (params.arguments.length > ARG_MAX || params.environment.length > ARG_MAX)
+        return -E2BIG;
+
+    if (!validate_read(params.path.characters, params.path.length))
         return -EFAULT;
-    if (!*filename)
+
+    if (params.path.length == 0)
         return -ENOENT;
-    if (argv) {
-        if (!validate_read_typed(argv))
-            return -EFAULT;
-        for (size_t i = 0; argv[i]; ++i) {
-            if (!validate_read_str(argv[i]))
-                return -EFAULT;
-        }
-    }
-    if (envp) {
-        if (!validate_read_typed(envp))
-            return -EFAULT;
-        for (size_t i = 0; envp[i]; ++i) {
-            if (!validate_read_str(envp[i]))
-                return -EFAULT;
+
+    auto copy_user_strings = [&](const auto& list, auto& output) {
+        if (!list.length)
+            return true;
+        if (!validate_read_typed(list.strings, list.length))
+            return false;
+        Vector<Syscall::SyscallString, 32> strings;
+        strings.resize(list.length);
+        copy_from_user(strings.data(), list.strings, list.length * sizeof(Syscall::SyscallString));
+        for (size_t i = 0; i < list.length; ++i) {
+            if (!validate_read(strings[i].characters, strings[i].length))
+                return false;
+            output.append(copy_string_from_user(strings[i].characters, strings[i].length));
         }
-    }
+        return true;
+    };
 
-    String path(filename);
     Vector<String> arguments;
+    if (!copy_user_strings(params.arguments, arguments))
+        return -EFAULT;
+
     Vector<String> environment;
-    {
-        auto parts = path.split('/');
-        if (argv) {
-            for (size_t i = 0; argv[i]; ++i) {
-                arguments.append(argv[i]);
-            }
-        } else {
-            arguments.append(parts.last());
-        }
+    if (!copy_user_strings(params.environment, environment))
+        return -EFAULT;
 
-        if (envp) {
-            for (size_t i = 0; envp[i]; ++i)
-                environment.append(envp[i]);
-        }
-    }
+    auto path = copy_string_from_user(params.path.characters, params.path.length);
 
     int rc = exec(move(path), move(arguments), move(environment));
     ASSERT(rc < 0); // We should never continue after a successful exec!

+ 1 - 1
Kernel/Process.h

@@ -155,7 +155,7 @@ public:
     int sys$ttyname_r(int fd, char*, ssize_t);
     int sys$ptsname_r(int fd, char*, ssize_t);
     pid_t sys$fork(RegisterDump&);
-    int sys$execve(const char* filename, const char** argv, const char** envp);
+    int sys$execve(const Syscall::SC_execve_params*);
     int sys$getdtablesize();
     int sys$dup(int oldfd);
     int sys$dup2(int oldfd, int newfd);

+ 16 - 0
Kernel/Syscall.h

@@ -300,6 +300,22 @@ struct SC_set_mmap_name_params {
     size_t name_length;
 };
 
+struct SyscallString {
+    const char* characters { nullptr };
+    size_t length { 0 };
+};
+
+struct SyscallStringList {
+    SyscallString* strings { nullptr };
+    size_t length { 0 };
+};
+
+struct SC_execve_params {
+    SyscallString path;
+    SyscallStringList arguments;
+    SyscallStringList environment;
+};
+
 void initialize();
 int sync();
 

+ 26 - 1
Libraries/LibC/unistd.cpp

@@ -2,6 +2,7 @@
 #include <AK/String.h>
 #include <AK/Vector.h>
 #include <Kernel/Syscall.h>
+#include <alloca.h>
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -49,7 +50,31 @@ int execv(const char* path, char* const argv[])
 
 int execve(const char* filename, char* const argv[], char* const envp[])
 {
-    int rc = syscall(SC_execve, filename, argv, envp);
+    size_t arg_count = 0;
+    for (size_t i = 0; argv[i]; ++i)
+        ++arg_count;
+
+    size_t env_count = 0;
+    for (size_t i = 0; envp[i]; ++i)
+        ++env_count;
+
+    auto copy_strings = [&](auto& vec, size_t count, auto& output) {
+        output.length = count;
+        for (size_t i = 0; vec[i]; ++i) {
+            output.strings[i].characters = vec[i];
+            output.strings[i].length = strlen(vec[i]);
+        }
+    };
+
+    Syscall::SC_execve_params params;
+    params.arguments.strings = (Syscall::SyscallString*)alloca(arg_count * sizeof(Syscall::SyscallString));
+    params.environment.strings = (Syscall::SyscallString*)alloca(env_count * sizeof(Syscall::SyscallString));
+
+    params.path = { filename, strlen(filename) };
+    copy_strings(argv, arg_count, params.arguments);
+    copy_strings(envp, env_count, params.environment);
+
+    int rc = syscall(SC_execve, &params);
     __RETURN_WITH_ERRNO(rc, rc, -1);
 }