From 50dd299371c0e9927e72af50ed5b36fd809a7cde Mon Sep 17 00:00:00 2001 From: Sergiu Moga Date: Wed, 1 Nov 2023 13:02:47 +0200 Subject: [PATCH] plat/common/x86: Switch to auxiliary stack on syscall entry 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 --- plat/common/x86/syscall.S | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/plat/common/x86/syscall.S b/plat/common/x86/syscall.S index af4edc987..5604dd36d 100644 --- a/plat/common/x86/syscall.S +++ b/plat/common/x86/syscall.S @@ -33,12 +33,65 @@ #include #include +#include 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 /* -- 2.39.5