Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/timens: add a test for vfork+exit

* check that a child process is in parent's time namespace after vfork.
* check that a child process is in the target namespace after exec.

Output on success:
1..4
ok 1 parent before vfork
ok 2 child after exec
ok 3 wait for child
ok 4 parent after vfork
# Totals: pass:4 fail:0 xfail:0 xpass:0 skip:0 error:0

Signed-off-by: Andrei Vagin <avagin@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20221013173154.291597-1-avagin@google.com

authored by

Andrei Vagin and committed by
Kees Cook
af4fddff 2b5f9dad

+141 -1
+1
tools/testing/selftests/timens/.gitignore
··· 8 8 timens 9 9 timer 10 10 timerfd 11 + vfork_exec
+1 -1
tools/testing/selftests/timens/Makefile
··· 1 - TEST_GEN_PROGS := timens timerfd timer clock_nanosleep procfs exec futex 1 + TEST_GEN_PROGS := timens timerfd timer clock_nanosleep procfs exec futex vfork_exec 2 2 TEST_GEN_PROGS_EXTENDED := gettime_perf 3 3 4 4 CFLAGS := -Wall -Werror -pthread
+139
tools/testing/selftests/timens/vfork_exec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + #include <errno.h> 4 + #include <fcntl.h> 5 + #include <sched.h> 6 + #include <stdio.h> 7 + #include <stdbool.h> 8 + #include <sys/stat.h> 9 + #include <sys/syscall.h> 10 + #include <sys/types.h> 11 + #include <sys/wait.h> 12 + #include <time.h> 13 + #include <unistd.h> 14 + #include <string.h> 15 + #include <pthread.h> 16 + 17 + #include "log.h" 18 + #include "timens.h" 19 + 20 + #define OFFSET (36000) 21 + 22 + struct thread_args { 23 + char *tst_name; 24 + struct timespec *now; 25 + }; 26 + 27 + static void *tcheck(void *_args) 28 + { 29 + struct thread_args *args = _args; 30 + struct timespec *now = args->now, tst; 31 + int i; 32 + 33 + for (i = 0; i < 2; i++) { 34 + _gettime(CLOCK_MONOTONIC, &tst, i); 35 + if (abs(tst.tv_sec - now->tv_sec) > 5) { 36 + pr_fail("%s: in-thread: unexpected value: %ld (%ld)\n", 37 + args->tst_name, tst.tv_sec, now->tv_sec); 38 + return (void *)1UL; 39 + } 40 + } 41 + return NULL; 42 + } 43 + 44 + static int check_in_thread(char *tst_name, struct timespec *now) 45 + { 46 + struct thread_args args = { 47 + .tst_name = tst_name, 48 + .now = now, 49 + }; 50 + pthread_t th; 51 + void *retval; 52 + 53 + if (pthread_create(&th, NULL, tcheck, &args)) 54 + return pr_perror("thread"); 55 + if (pthread_join(th, &retval)) 56 + return pr_perror("pthread_join"); 57 + return !(retval == NULL); 58 + } 59 + 60 + static int check(char *tst_name, struct timespec *now) 61 + { 62 + struct timespec tst; 63 + int i; 64 + 65 + for (i = 0; i < 2; i++) { 66 + _gettime(CLOCK_MONOTONIC, &tst, i); 67 + if (abs(tst.tv_sec - now->tv_sec) > 5) 68 + return pr_fail("%s: unexpected value: %ld (%ld)\n", 69 + tst_name, tst.tv_sec, now->tv_sec); 70 + } 71 + if (check_in_thread(tst_name, now)) 72 + return 1; 73 + ksft_test_result_pass("%s\n", tst_name); 74 + return 0; 75 + } 76 + 77 + int main(int argc, char *argv[]) 78 + { 79 + struct timespec now; 80 + int status; 81 + pid_t pid; 82 + 83 + if (argc > 1) { 84 + char *endptr; 85 + 86 + ksft_cnt.ksft_pass = 1; 87 + now.tv_sec = strtoul(argv[1], &endptr, 0); 88 + if (*endptr != 0) 89 + return pr_perror("strtoul"); 90 + 91 + return check("child after exec", &now); 92 + } 93 + 94 + nscheck(); 95 + 96 + ksft_set_plan(4); 97 + 98 + clock_gettime(CLOCK_MONOTONIC, &now); 99 + 100 + if (unshare_timens()) 101 + return 1; 102 + 103 + if (_settime(CLOCK_MONOTONIC, OFFSET)) 104 + return 1; 105 + 106 + if (check("parent before vfork", &now)) 107 + return 1; 108 + 109 + pid = vfork(); 110 + if (pid < 0) 111 + return pr_perror("fork"); 112 + 113 + if (pid == 0) { 114 + char now_str[64]; 115 + char *cargv[] = {"exec", now_str, NULL}; 116 + char *cenv[] = {NULL}; 117 + 118 + /* Check for proper vvar offsets after execve. */ 119 + snprintf(now_str, sizeof(now_str), "%ld", now.tv_sec + OFFSET); 120 + execve("/proc/self/exe", cargv, cenv); 121 + pr_perror("execve"); 122 + _exit(1); 123 + } 124 + 125 + if (waitpid(pid, &status, 0) != pid) 126 + return pr_perror("waitpid"); 127 + 128 + if (status) 129 + ksft_exit_fail(); 130 + ksft_inc_pass_cnt(); 131 + ksft_test_result_pass("wait for child\n"); 132 + 133 + /* Check that we are still in the source timens. */ 134 + if (check("parent after vfork", &now)) 135 + return 1; 136 + 137 + ksft_exit_pass(); 138 + return 0; 139 + }