]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/posix-process: Define `libc` wrappers for `vfork`
authorSergiu Moga <sergiu@unikraft.io>
Mon, 24 Mar 2025 17:00:35 +0000 (19:00 +0200)
committerUnikraft Bot <monkey@unikraft.io>
Thu, 17 Apr 2025 12:33:46 +0000 (12:33 +0000)
It is impossible to write the libc wrapper of the `vfork` system call
in C since the child might end up reusing and overwriting the parent's
return address when it pops it and then calls `execve`, leaving the
parent to return to an invalid state - typically would overwrite it
with the return address from execve, making it look as if the parent
returned from execve.

Userspace libc's solve this by writing the libc wrapper as something
among the lines of:
popq %<register that the syscall guarantees to preserve>
movq $SYS_vfork, %rax
syscall
pushq %<register we popped return address into 3 lines above>
ret

On ARM64 as well as others, clone() with the flags
`SIGCHLD | CLONE_VM | CLONE_VFORK` is called instead but let's just
still use our `vfork` implementation since it essentially also does
the same exact thing.

However, as a Unikernel with function calls for system calls we do
not need to care about any of that, as our execenv prologue can store
and restore everything without touching the stack. Thus, we can write
our native libc wrappers as a basic jump/branch to said prologue.

Signed-off-by: Sergiu Moga <sergiu@unikraft.io>
Approved-by: Michalis Pappas <michalis@unikraft.io>
Reviewed-by: Michalis Pappas <michalis@unikraft.io>
Reviewed-by: Andrei Tatar <andrei@unikraft.io>
GitHub-Closes: #1618

lib/posix-process/Makefile.uk
lib/posix-process/arch/arm64/vfork.S [new file with mode: 0644]
lib/posix-process/arch/x86_64/vfork.S [new file with mode: 0644]

index 75c690dcf50540a40e6d67422284df0db6c80f80..86ca80b5f60772ce9cf95182dc813dd4dd1b37bd 100644 (file)
@@ -13,6 +13,7 @@ LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_EXECVE) += $(LIBPOSIX_PROCESS_BA
 LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_EXECVE) += $(LIBPOSIX_PROCESS_BASE)/arch/$(CONFIG_UK_ARCH)/execve.c|arch
 
 LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_VFORK) += $(LIBPOSIX_PROCESS_BASE)/vfork.c
+LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_VFORK) += $(LIBPOSIX_PROCESS_BASE)/arch/$(CONFIG_UK_ARCH)/vfork.S|arch
 
 LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/deprecated.c
 LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/process.c
diff --git a/lib/posix-process/arch/arm64/vfork.S b/lib/posix-process/arch/arm64/vfork.S
new file mode 100644 (file)
index 0000000..6009f4c
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2025, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include <uk/syscall.h>
+
+/*
+ * NOTE: We need to replicate this assembly libc wrapper in our
+ * supported/ported external libc libraries (e.g. musl) because their
+ * wrappers do not typically tailor to our use case of having system calls
+ * regular function calls.
+ *
+ * Whenever porting a new libc library, make sure to replicate this code there
+ * as well, unless the library in question does somehow indeed have a more
+ * special wrapper that still allows our vfork functionality to still work
+ * correctly.
+ */
+#if UK_LIBC_SYSCALLS
+.global vfork
+vfork:
+       b       uk_syscall_e_vfork
+#endif /* UK_LIBC_SYSCALLS */
diff --git a/lib/posix-process/arch/x86_64/vfork.S b/lib/posix-process/arch/x86_64/vfork.S
new file mode 100644 (file)
index 0000000..638f404
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2025, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include <uk/syscall.h>
+
+/*
+ * NOTE: We need to replicate this assembly libc wrapper in our
+ * supported/ported external libc libraries (e.g. musl) because their
+ * wrappers do not typically tailor to our use case of having system calls
+ * regular function calls.
+ *
+ * Whenever porting a new libc library, make sure to replicate this code there
+ * as well, unless the library in question does somehow indeed have a more
+ * special wrapper that still allows our vfork functionality to still work
+ * correctly.
+ */
+#if UK_LIBC_SYSCALLS
+.global vfork
+vfork:
+       jmp     uk_syscall_e_vfork
+#endif /* UK_LIBC_SYSCALLS */