]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
plat/common/x86: Switch to auxiliary stack on syscall entry
authorSergiu Moga <sergiu@unikraft.io>
Wed, 1 Nov 2023 11:02:47 +0000 (13:02 +0200)
committerSergiu Moga <sergiu@unikraft.io>
Sun, 26 Nov 2023 15:45:56 +0000 (17:45 +0200)
We would usually use the application's stack to execute the issued
system call. However this present a couple of problems such as
if the application's stacks are too small (like it is the case
for Go's goroutines) this will end up generating an unhandled
pagefault or, even worse, corrupting other memory areas beyond
the respective stack. Another problem would be that we will
end up overwriting stuff on the Red Zone for those applications
that employ it.

To fix this, switch to the per-thread auxiliary stack on system call
entry and use that instead. The switch shall be done with the help of
the `swapgs` instruction to help us swap the `gs_base` registers, as
our own `KERNEL_GS_BASE` contains a pointer to the current LCPU's
`struct lcpu` that has an up to date pointer to the current thread's
auxiliary stack.

Signed-off-by: Sergiu Moga <sergiu@unikraft.io>
plat/common/x86/syscall.S

index af4edc987ae953fb355f9d43c884f78d9046ca16..5604dd36d3158215e71c5cdc7e97e529dd38fa18 100644 (file)
 
 #include <uk/arch/lcpu.h>
 #include <uk/asm/cfi.h>
+#include <uk/plat/common/lcpu.h>
 
 ENTRY(_ukplat_syscall)
        .cfi_startproc simple
        .cfi_def_cfa rsp, 0
        .cfi_register rip, rcx
        cli
+
+       /* Switch to Unikraft's gs_base, which contains pointer to the current
+        * LCPU's `struct lcpu`.
+        */
+       swapgs
+
+       /* We can now use the scratch register %r11 (SYSv ABI) to temporarily
+        * store the current stack pointer and switch to the auxiliary stack
+        * of the current thread, which is also stored in `struct lcpu`'s
+        * `auxsp` field.
+        * We thus achieve a complete switch to another stack while preserving
+        * the context of the application.
+        */
+       /* Temporarily store current stack pointer in scratch register */
+       movq    %rsp, %r11
+
+       /* Switch to the auxiliary stack so that we do not contaminate the
+        * application's stack, as this could either be too small and result
+        * in corrupted memory or we could modify unwanted variables stored
+        * in the Red Zone.
+        */
+       movq    %gs:LCPU_AUXSP_OFFSET, %rsp
+
+       /* Store application's stack pointer at the top of current thread's
+        * auxiliary stack. We have to do this because we obviously can't
+        * rely on the scratch register being maintained between thread switches
+        */
+       pushq_reg_cfi r11
+       .cfi_rel_offset rsp, 0
+
+       /* We are now in a state where the stack looks like this:
+        *      --------------- <-- auxsp (i.e. lcpu_get_current()->auxsp OR
+        *      | app's saved |            uk_thread_current()->auxsp)
+        *      |     %rsp    |
+        *      --------------- <-- (auxsp - 8) OR (**current %rsp**)
+        *      |             |
+        *      |             |
+        *            ...
+        *      |             |
+        *      --------------- <-- (auxsp - CONFIG_UKPLAT_AUXSP_SIZE)
+        *        END OF AUXSP
+        */
+
+       /* Make a final alignment so that we preserve syscall semantics where
+        * register pushes on the stack are happening on an initially 16-byte
+        * aligned stack.
+        * Therefore, all in-syscall context operations, buffer and function
+        * frames must fit into (CONFIG_UKPLAT_AUXSP_SIZE - 16) bytes.
+        */
+       subq    $8, %rsp
+       .cfi_adjust_cfa_offset 8
+
        /*
         * Push arguments in the order of 'struct __regs' to the stack.
         * We are going to handover a refernce to this stack area as
@@ -117,6 +170,20 @@ ENTRY(_ukplat_syscall)
        /* orig_rax and exception frame */
        addq $(6 * 8), %rsp
        .cfi_adjust_cfa_offset -(6 * 8)
+
+       /* Undo alignment done after storing application %rsp */
+       addq    $8, %rsp
+       .cfi_adjust_cfa_offset -8
+
+       /* We are now back in the state where the stack was looking like the
+        * diagram above. Restore application %rsp!
+        */
+       movq    8(%rsp), %rsp
+       .cfi_adjust_cfa_offset -__REGS_PAD_SIZE
+
+       /* Restore application's gs_base register */
+       swapgs
+
        sti
 
        /*