]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
kselftest/arm64: Add a test for vfork() with GCS
authorMark Brown <broonie@kernel.org>
Thu, 3 Jul 2025 16:00:17 +0000 (17:00 +0100)
committerThomas Weißschuh <linux@weissschuh.net>
Fri, 4 Jul 2025 11:26:14 +0000 (13:26 +0200)
Ensure that we've got at least some coverage of the special cases around
vfork() by adding a test case in basic-gcs doing the same thing as the
plain fork() one - vfork(), do a few checks and then return to the parent.

Signed-off-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20250703-arm64-gcs-vfork-exit-v3-3-1e9a9d2ddbbe@kernel.org
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
tools/testing/selftests/arm64/gcs/basic-gcs.c

index 3fb9742342a349f732712fb92270134b586f3c3e..54f9c888249d74451e1432280a395156ce72e56c 100644 (file)
@@ -298,6 +298,68 @@ out:
        return pass;
 }
 
+/* A vfork()ed process can run and exit */
+static bool test_vfork(void)
+{
+       unsigned long child_mode;
+       int ret, status;
+       pid_t pid;
+       bool pass = true;
+
+       pid = vfork();
+       if (pid == -1) {
+               ksft_print_msg("vfork() failed: %d\n", errno);
+               pass = false;
+               goto out;
+       }
+       if (pid == 0) {
+               /*
+                * In child, make sure we can call a function, read
+                * the GCS pointer and status and then exit.
+                */
+               valid_gcs_function();
+               get_gcspr();
+
+               ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
+                                 &child_mode, 0, 0, 0);
+               if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
+                       ksft_print_msg("GCS not enabled in child\n");
+                       ret = EXIT_FAILURE;
+               }
+
+               _exit(ret);
+       }
+
+       /*
+        * In parent, check we can still do function calls then check
+        * on the child.
+        */
+       valid_gcs_function();
+
+       ksft_print_msg("Waiting for child %d\n", pid);
+
+       ret = waitpid(pid, &status, 0);
+       if (ret == -1) {
+               ksft_print_msg("Failed to wait for child: %d\n",
+                              errno);
+               return false;
+       }
+
+       if (!WIFEXITED(status)) {
+               ksft_print_msg("Child exited due to signal %d\n",
+                              WTERMSIG(status));
+               pass = false;
+       } else if (WEXITSTATUS(status)) {
+               ksft_print_msg("Child exited with status %d\n",
+                              WEXITSTATUS(status));
+               pass = false;
+       }
+
+out:
+
+       return pass;
+}
+
 typedef bool (*gcs_test)(void);
 
 static struct {
@@ -314,6 +376,7 @@ static struct {
        { "enable_invalid", enable_invalid, true },
        { "map_guarded_stack", map_guarded_stack },
        { "fork", test_fork },
+       { "vfork", test_vfork },
 };
 
 int main(void)