Skip to content

Instantly share code, notes, and snippets.

@thehajime
Created August 22, 2026 23:56
Show Gist options
  • Select an option

  • Save thehajime/fd11fbcea45db9fff6d0358a9fdf9e85 to your computer and use it in GitHub Desktop.

Select an option

Save thehajime/fd11fbcea45db9fff6d0358a9fdf9e85 to your computer and use it in GitHub Desktop.
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile
index b087b4278173..9aac5101af7e 100644
--- a/arch/um/kernel/Makefile
+++ b/arch/um/kernel/Makefile
@@ -31,6 +31,8 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_SMP) += smp.o
+CFLAGS_process.o += -fomit-frame-pointer
+
USER_OBJS := config.o
include $(srctree)/arch/um/scripts/Makefile.rules
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index e117a1e27c7e..d03b82e13bb4 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -160,6 +160,110 @@ static void fork_handler(void)
userspace(&current->thread.regs.regs);
}
+static void *uml_nommu_user_stack_alloc(unsigned long size)
+{
+ return (void *)alloc_stack(0, __uml_cant_sleep());
+}
+
+static void uml_nommu_user_stack_free(void *address,
+ unsigned long size)
+{
+ free_stack((unsigned long)address, 0);
+}
+
+static int uml_nommu_get_user_stack_range(struct mm_struct *mm,
+ unsigned long sp,
+ unsigned long *low,
+ unsigned long *high)
+{
+ unsigned long stack_low;
+ unsigned long stack_high;
+
+ if (!mm || !low || !high)
+ return -EINVAL;
+
+ stack_low = mm->start_brk;
+ stack_high = mm->start_stack;
+
+ if (!stack_low || stack_high <= stack_low)
+ return -EINVAL;
+
+ /*
+ * The stack grows downward, so SP normally lies below
+ * start_stack and above start_brk.
+ */
+ if (sp < stack_low || sp > stack_high)
+ return -EFAULT;
+
+ *low = stack_low;
+ *high = stack_high;
+
+ return 0;
+}
+
+static unsigned long
+relocate_stack_pointer(unsigned long pointer,
+ unsigned long old_low,
+ unsigned long old_high,
+ unsigned long new_low)
+{
+ if (pointer < old_low || pointer >= old_high)
+ return pointer;
+
+ return new_low + (pointer - old_low);
+}
+
+static int uml_nommu_copy_user_stack(struct task_struct *child)
+{
+ unsigned long parent_sp;
+ unsigned long stack_low;
+ unsigned long stack_high;
+ unsigned long stack_size;
+ unsigned long child_sp;
+ void *child_stack;
+ int ret;
+
+ parent_sp = current->thread.regs.regs.gp[HOST_SP];
+
+ ret = uml_nommu_get_user_stack_range(current->mm,
+ parent_sp,
+ &stack_low,
+ &stack_high);
+ if (ret)
+ return ret;
+
+ stack_size = stack_high - stack_low;
+
+ child_stack = uml_nommu_user_stack_alloc(stack_size);
+ if (!child_stack)
+ return -ENOMEM;
+
+ memcpy(child_stack,
+ (void *)stack_low,
+ stack_size);
+
+ child_sp = (unsigned long)child_stack +
+ (parent_sp - stack_low);
+
+ child->thread.regs.regs.gp[HOST_SP] = child_sp;
+
+ child->thread.arch.nommu_user_stack = child_stack;
+ child->thread.arch.nommu_user_stack_size = stack_size;
+
+ unsigned long parent_bp;
+ unsigned long child_stack_base = (unsigned long)child_stack;
+
+ parent_bp = child->thread.regs.regs.gp[HOST_BP];
+
+ child->thread.regs.regs.gp[HOST_BP] =
+ relocate_stack_pointer(parent_bp,
+ stack_low,
+ stack_high,
+ child_stack_base);
+
+ return 0;
+}
+
int copy_thread(struct task_struct * p, const struct kernel_clone_args *args)
{
u64 clone_flags = args->flags;
@@ -189,6 +293,14 @@ int copy_thread(struct task_struct * p, const struct kernel_clone_args *args)
handler = fork_handler;
arch_copy_thread(&current->thread.arch, &p->thread.arch);
+
+#ifdef CONFIG_NOMMU_SWMMU
+ if (!(args->flags & CLONE_VM)) {
+ ret = uml_nommu_copy_user_stack(p);
+ if (ret)
+ return ret;
+ }
+#endif
} else {
get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
p->thread.request.thread.proc = args->fn;
diff --git a/arch/x86/um/asm/processor_64.h b/arch/x86/um/asm/processor_64.h
index f90159508936..6a478ee92d18 100644
--- a/arch/x86/um/asm/processor_64.h
+++ b/arch/x86/um/asm/processor_64.h
@@ -11,6 +11,10 @@ struct arch_thread {
unsigned long debugregs[8];
int debugregs_seq;
struct faultinfo faultinfo;
+#ifdef CONFIG_NOMMU_SWMMU
+ void *nommu_user_stack;
+ unsigned long nommu_user_stack_size;
+#endif
};
#define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \
diff --git a/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c b/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c
index 0f8726cc56b4..e0655fafac42 100644
--- a/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c
+++ b/tools/testing/selftests/mm/nommu/nommu_swmmu_test.c
@@ -155,6 +155,77 @@ struct child_result {
uint64_t value;
};
+static int
+test_eager_copy_fork(void)
+{
+ struct swmmu_test_object *object;
+ pid_t pid;
+ int status;
+ uint64_t parent_value;
+
+ object = nommu_swmmu_alloc(sizeof(*object));
+ if (!object) {
+ ksft_test_result_skip("SWMMU allocation is unavailable\n");
+ return KSFT_SKIP;
+ }
+
+ nommu_swmmu_store_u64(&object->value,
+ sizeof(object->value),
+ 41);
+
+ pid = fork();
+ if (pid < 0) {
+ ksft_test_result_fail("fork failed: %s\n",
+ strerror(errno));
+ nommu_swmmu_free(object);
+ return KSFT_FAIL;
+ }
+
+ if (pid == 0) {
+ _exit(0);
+ }
+
+ int wait_ret;
+
+ status = -1;
+ wait_ret = waitpid(pid, &status, 0);
+
+ if (wait_ret != pid) {
+ ksft_test_result_fail(
+ "waitpid returned %d, expected %d: %s\n",
+ wait_ret, pid, strerror(errno));
+ nommu_swmmu_free(object);
+ return KSFT_FAIL;
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ ksft_test_result_fail(
+ "child exited abnormally: status=%#x\n",
+ status);
+ nommu_swmmu_free(object);
+ return KSFT_FAIL;
+ }
+
+ parent_value = nommu_swmmu_load_u64(&object->value,
+ sizeof(object->value));
+
+ if (parent_value != 41) {
+ ksft_test_result_fail(
+ "parent value changed to %llu\n",
+ (unsigned long long)parent_value);
+ nommu_swmmu_free(object);
+ return KSFT_FAIL;
+ }
+
+ nommu_swmmu_free(object);
+
+ ksft_test_result_pass(
+ "fork child exited and parent backing remained unchanged\n");
+
+ return KSFT_PASS;
+}
+
+#if 0
static int
test_eager_copy_fork(void)
{
@@ -274,6 +345,7 @@ test_eager_copy_fork(void)
return KSFT_PASS;
}
+#endif
int main(void)
{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment