[PATCH v1 4/8] sbm: x86: allocate and map an exception stack

From: Petr Tesarik
Date: Wed Feb 14 2024 - 06:37:15 EST


From: Petr Tesarik <petr.tesarik1@xxxxxxxxxxxxxxxxxxx>

Sandbox mode should run with CPL 3. It is treated as user mode by the CPU,
so non-IST interrupts will load RSP from TSS. This is the trampoline stack
and that one is fine. However, the interrupt entry code then moves to the
thread stack, assuming that it cannot be currently in use, since the task
was executing in user mode. This assumption is not valid for sandbox mode,
because the code was originally called from kernel mode, and the thread
stack contains precious data of the sandbox mode callers.

Allocate a separate exception stack for sandbox and use it instead of the
thread stack in interrupt handlers while sandbox mode is active. To find
the sandbox exception stack from interrupt entry, store a pointer to the
state page in struct thread_info. This pointer is non-NULL if the current
task is running in sandbox mode. It is also non-NULL during the transition
from/to sandbox mode. The sandbox exception stack is valid in either case.

Signed-off-by: Petr Tesarik <petr.tesarik1@xxxxxxxxxxxxxxxxxxx>
---
arch/x86/include/asm/sbm.h | 24 ++++++++++++++++++++++++
arch/x86/include/asm/thread_info.h | 3 +++
arch/x86/kernel/sbm/call_64.S | 1 +
arch/x86/kernel/sbm/core.c | 21 ++++++++++++++++++++-
arch/x86/kernel/traps.c | 3 ++-
5 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/sbm.h b/arch/x86/include/asm/sbm.h
index ed214c17af06..ca4741b449e8 100644
--- a/arch/x86/include/asm/sbm.h
+++ b/arch/x86/include/asm/sbm.h
@@ -9,6 +9,8 @@
#ifndef __ASM_SBM_H
#define __ASM_SBM_H

+#include <asm/processor.h>
+
#if defined(CONFIG_HAVE_ARCH_SBM) && defined(CONFIG_SANDBOX_MODE)

#include <asm/pgtable_types.h>
@@ -17,6 +19,7 @@
* struct x86_sbm_state - Run-time state of the environment.
* @pgd: Sandbox mode page global directory.
* @stack: Sandbox mode stack.
+ * @exc_stack: Exception and IRQ stack.
*
* One instance of this union is allocated for each sandbox and stored as SBM
* instance private data.
@@ -24,8 +27,29 @@
struct x86_sbm_state {
pgd_t *pgd;
unsigned long stack;
+ unsigned long exc_stack;
};

+/**
+ * top_of_intr_stack() - Get address interrupt stack.
+ *
+ */
+static inline unsigned long top_of_intr_stack(void)
+{
+ struct x86_sbm_state *sbm = current_thread_info()->sbm_state;
+
+ if (sbm)
+ return sbm->exc_stack + EXCEPTION_STKSZ;
+ return current_top_of_stack();
+}
+
+#else /* defined(CONFIG_HAVE_ARCH_SBM) && defined(CONFIG_SANDBOX_MODE) */
+
+static inline unsigned long top_of_intr_stack(void)
+{
+ return current_top_of_stack();
+}
+
#endif /* defined(CONFIG_HAVE_ARCH_SBM) && defined(CONFIG_SANDBOX_MODE) */

#endif /* __ASM_SBM_H */
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index d63b02940747..95b1acffb78a 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -60,6 +60,9 @@ struct thread_info {
#ifdef CONFIG_SMP
u32 cpu; /* current CPU */
#endif
+#ifdef CONFIG_SANDBOX_MODE
+ struct x86_sbm_state *sbm_state; /* SandBox mode state page */
+#endif
};

#define INIT_THREAD_INFO(tsk) \
diff --git a/arch/x86/kernel/sbm/call_64.S b/arch/x86/kernel/sbm/call_64.S
index 245d0dddce73..1b232c8d15b7 100644
--- a/arch/x86/kernel/sbm/call_64.S
+++ b/arch/x86/kernel/sbm/call_64.S
@@ -9,6 +9,7 @@

#include <linux/linkage.h>
#include <asm/nospec-branch.h>
+#include <asm/percpu.h>

.code64
.section .entry.text, "ax"
diff --git a/arch/x86/kernel/sbm/core.c b/arch/x86/kernel/sbm/core.c
index f3a123d64afc..81f1b0093537 100644
--- a/arch/x86/kernel/sbm/core.c
+++ b/arch/x86/kernel/sbm/core.c
@@ -264,6 +264,14 @@ int arch_sbm_init(struct sbm *sbm)
if (err)
return err;

+ state->exc_stack = __get_free_pages(GFP_KERNEL, EXCEPTION_STACK_ORDER);
+ if (err)
+ return err;
+ err = map_range(state, state->exc_stack,
+ state->exc_stack + EXCEPTION_STKSZ, PAGE_KERNEL);
+ if (err)
+ return err;
+
err = map_cpu_data(state);
if (err)
return err;
@@ -324,6 +332,7 @@ void arch_sbm_destroy(struct sbm *sbm)
free_pgd(state->pgd);
free_pages((unsigned long)state->pgd, PGD_ORDER);
}
+ free_pages(state->exc_stack, EXCEPTION_STACK_ORDER);
free_pages(state->stack, THREAD_SIZE_ORDER);
free_page((unsigned long)state);
sbm->private = NULL;
@@ -332,6 +341,16 @@ void arch_sbm_destroy(struct sbm *sbm)
int arch_sbm_exec(struct sbm *sbm, sbm_func func, void *args)
{
struct x86_sbm_state *state = sbm->private;
+ int err;
+
+ /* let interrupt handlers use the sandbox state page */
+ barrier();
+ WRITE_ONCE(current_thread_info()->sbm_state, state);
+
+ err = x86_sbm_exec(state, func, args, state->stack + THREAD_SIZE);
+
+ /* NULLify the state page pointer before it becomes stale */
+ WRITE_ONCE(current_thread_info()->sbm_state, NULL);

- return x86_sbm_exec(state, func, args, state->stack + THREAD_SIZE);
+ return err;
}
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index c3b2f863acf0..b9c9c74314e7 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -66,6 +66,7 @@
#include <asm/vdso.h>
#include <asm/tdx.h>
#include <asm/cfi.h>
+#include <asm/sbm.h>

#ifdef CONFIG_X86_64
#include <asm/x86_init.h>
@@ -773,7 +774,7 @@ DEFINE_IDTENTRY_RAW(exc_int3)
*/
asmlinkage __visible noinstr struct pt_regs *sync_regs(struct pt_regs *eregs)
{
- struct pt_regs *regs = (struct pt_regs *)this_cpu_read(pcpu_hot.top_of_stack) - 1;
+ struct pt_regs *regs = (struct pt_regs *)top_of_intr_stack() - 1;
if (regs != eregs)
*regs = *eregs;
return regs;
--
2.34.1