浏览代码

Kernel: Prevent executing I/O instructions in userspace

All threads were running with iomapbase=0 in their TSS, which the CPU
interprets as "there's an I/O permission bitmap starting at offset 0
into my TSS".

Because of that, any bits that were 1 inside the TSS would allow the
thread to execute I/O instructions on the port with that bit index.

Fix this by always setting the iomapbase to sizeof(TSS32), and also
setting the TSS descriptor's limit to sizeof(TSS32), effectively making
the I/O permissions bitmap zero-length.

This should make it no longer possible to do I/O from userspace. :^)
Andreas Kling 5 年之前
父节点
当前提交
f598bbbb1d
共有 5 个文件被更改,包括 21 次插入6 次删除
  1. 1 0
      Base/usr/share/man/man1/crash.md
  2. 2 0
      Kernel/Process.cpp
  3. 4 4
      Kernel/Scheduler.cpp
  4. 1 0
      Kernel/Thread.cpp
  5. 13 2
      Userland/crash.cpp

+ 1 - 0
Base/usr/share/man/man1/crash.md

@@ -33,6 +33,7 @@ kinds of crashes.
 * `-y`: Write to recently freed memory. (Tests an opportunistic malloc guard.)
 * `-X`: Attempt to execute non-executable memory. (Not mapped with PROT\_EXEC.)
 * `-U`: Attempt to trigger an x86 User Mode Instruction Prevention fault.
+* `-I`: Use an x86 I/O instruction in userspace.
 
 ## Examples
 

+ 2 - 0
Kernel/Process.cpp

@@ -705,6 +705,8 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir
     new_main_thread->make_thread_specific_region({});
 
     memset(&tss, 0, sizeof(TSS32));
+    tss.iomapbase = sizeof(TSS32);
+
     tss.eflags = 0x0202;
     tss.eip = entry_eip;
     tss.cs = 0x1b;

+ 4 - 4
Kernel/Scheduler.cpp

@@ -476,10 +476,10 @@ bool Scheduler::context_switch(Thread& thread)
         thread.set_selector(gdt_alloc_entry());
         auto& descriptor = get_gdt_entry(thread.selector());
         descriptor.set_base(&thread.tss());
-        descriptor.set_limit(0xffff);
+        descriptor.set_limit(sizeof(TSS32));
         descriptor.dpl = 0;
         descriptor.segment_present = 1;
-        descriptor.granularity = 1;
+        descriptor.granularity = 0;
         descriptor.zero = 0;
         descriptor.operation_size = 1;
         descriptor.descriptor_type = 0;
@@ -501,10 +501,10 @@ static void initialize_redirection()
 {
     auto& descriptor = get_gdt_entry(s_redirection.selector);
     descriptor.set_base(&s_redirection.tss);
-    descriptor.set_limit(0xffff);
+    descriptor.set_limit(sizeof(TSS32));
     descriptor.dpl = 0;
     descriptor.segment_present = 1;
-    descriptor.granularity = 1;
+    descriptor.granularity = 0;
     descriptor.zero = 0;
     descriptor.operation_size = 1;
     descriptor.descriptor_type = 0;

+ 1 - 0
Kernel/Thread.cpp

@@ -59,6 +59,7 @@ Thread::Thread(Process& process)
     m_fpu_state = (FPUState*)kmalloc_aligned(sizeof(FPUState), 16);
     memcpy(m_fpu_state, &s_clean_fpu_state, sizeof(FPUState));
     memset(&m_tss, 0, sizeof(m_tss));
+    m_tss.iomapbase = sizeof(TSS32);
 
     // Only IF is set when a process boots.
     m_tss.eflags = 0x0202;

+ 13 - 2
Userland/crash.cpp

@@ -1,5 +1,6 @@
 #include <AK/Function.h>
 #include <AK/String.h>
+#include <Kernel/IO.h>
 #include <Kernel/Syscall.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -10,7 +11,7 @@
 
 static void print_usage_and_exit()
 {
-    printf("usage: crash -[AsdiamfMFTtSxyXU]\n");
+    printf("usage: crash -[AsdiamfMFTtSxyXUI]\n");
     exit(0);
 }
 
@@ -99,6 +100,7 @@ int main(int argc, char** argv)
         ReadFromFreedMemoryStillCachedByMalloc,
         ExecuteNonExecutableMemory,
         TriggerUserModeInstructionPrevention,
+        UseIOInstruction,
     };
     Mode mode = SegmentationViolation;
 
@@ -139,6 +141,8 @@ int main(int argc, char** argv)
         mode = ExecuteNonExecutableMemory;
     else if (String(argv[1]) == "-U")
         mode = TriggerUserModeInstructionPrevention;
+    else if (String(argv[1]) == "-I")
+        mode = UseIOInstruction;
     else
         print_usage_and_exit();
 
@@ -330,6 +334,13 @@ int main(int argc, char** argv)
         }).run(run_type);
     }
 
+    if (mode == UseIOInstruction || mode == TestAllCrashTypes) {
+        Crash("Attempt to use an I/O instruction", [] {
+            u8 keyboard_status = IO::in8(0x64);
+            printf("Keyboard status: %#02x\n", keyboard_status);
+            return Crash::Failure::DidNotCrash;
+        }).run(run_type);
+    }
+
     return 0;
 }
-