This test verifies the correct behavior of the fork() system call, which creates a child process by duplicating the parent process.
The test checks the following: - The child PID returned by fork() is present in /proc. - The child PID is different from the parent PID. - The memory allocated to a variable in the child process is independent of the parent process.
Test logs :
- Run without root TAP version 13 1..1 ok 1 # SKIP This test needs root to run!
- Run with root TAP version 13 1..1 # Inside the parent process. # Child PID got from fork() return : 56038 # Parent PID from getpid(): 56037 # Inside the child process. 1..2 ok 1 Child Pid from /proc and fork() matching ok 2 Child Pid != Parent pid 1..3 ok 3 After modification in child No effect on the value of 'var' in parent # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0
Signed-off-by: Shivam Chaudhary cvam0000@gmail.com ---
Here is my proposal for a new directory, /syscalls, to add syscall selftests, as there is currently no dedicated space for these tests. I encountered this issue while writing the test case for the delete_module syscall and was unsure where to place it. As a heads-up, the delete_module test is currently under review, and I would like to add it to this directory.
tools/testing/selftests/Makefile | 1 + tools/testing/selftests/syscalls/.gitignore | 1 + .../syscalls/fork_syscall/.gitignore | 1 + .../selftests/syscalls/fork_syscall/Makefile | 5 + .../syscalls/fork_syscall/fork_syscall.c | 151 ++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 tools/testing/selftests/syscalls/.gitignore create mode 100644 tools/testing/selftests/syscalls/fork_syscall/.gitignore create mode 100644 tools/testing/selftests/syscalls/fork_syscall/Makefile create mode 100644 tools/testing/selftests/syscalls/fork_syscall/fork_syscall.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 363d031a16f7..9265c17c5de3 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -97,6 +97,7 @@ TARGETS += sparc64 TARGETS += splice TARGETS += static_keys TARGETS += sync +TARGETS += syscalls/fork_syscall TARGETS += syscall_user_dispatch TARGETS += sysctl TARGETS += tc-testing diff --git a/tools/testing/selftests/syscalls/.gitignore b/tools/testing/selftests/syscalls/.gitignore new file mode 100644 index 000000000000..c7ae138d3f0c --- /dev/null +++ b/tools/testing/selftests/syscalls/.gitignore @@ -0,0 +1 @@ +// SPDX-License-Identifier: GPL-2.0 \ No newline at end of file diff --git a/tools/testing/selftests/syscalls/fork_syscall/.gitignore b/tools/testing/selftests/syscalls/fork_syscall/.gitignore new file mode 100644 index 000000000000..788cc1ff70bd --- /dev/null +++ b/tools/testing/selftests/syscalls/fork_syscall/.gitignore @@ -0,0 +1 @@ +# SPDX-License-Identifier: GPL-2.0-only \ No newline at end of file diff --git a/tools/testing/selftests/syscalls/fork_syscall/Makefile b/tools/testing/selftests/syscalls/fork_syscall/Makefile new file mode 100644 index 000000000000..56033a3d5a87 --- /dev/null +++ b/tools/testing/selftests/syscalls/fork_syscall/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS := fork_syscall +CFLAGS += -Wall + +include ../lib.mk \ No newline at end of file diff --git a/tools/testing/selftests/syscalls/fork_syscall/fork_syscall.c b/tools/testing/selftests/syscalls/fork_syscall/fork_syscall.c new file mode 100644 index 000000000000..eab22831f7e1 --- /dev/null +++ b/tools/testing/selftests/syscalls/fork_syscall/fork_syscall.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* kselftest for fork() system call + * + * Summery : fork() system call is used to create a new process + * by duplicating an existing one. The new process, known as the + * child process, is a copy of the parent process. + * + * Child process is dublicate process but has different PID and + * memory allocation. + * + * About the test : With this test we are testing the following: + * - Child PID which fork() returns to Parent is present in /proc + * - Child PID is not same as Parent PID. + * - Memory allocation to a variable in child and parent process + * is different. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <dirent.h> +#include <ctype.h> + +#include "../../kselftest.h" + +// Function to check if a string is numeric (PID check) +int is_numeric(const char *str) { + while (*str) { + if (!isdigit(*str)) return 0; + str++; + } + return 1; +} + +// Function to find the child PID in /proc +pid_t find_child_pid(pid_t parent_pid) { + DIR *proc_dir = opendir("/proc"); + struct dirent *entry; + + if (proc_dir == NULL) { + perror("Failed to open /proc directory"); + ksft_exit_fail(); + return 1; + } + + // Iterate through the /proc directory to find PIDs + while ((entry = readdir(proc_dir)) != NULL) { + // Check if the entry is a PID + if (is_numeric(entry->d_name)) { + pid_t pid = atoi(entry->d_name); + + // Construct the path to /proc/<pid>/ + //stat to check the parent PID + + char path[40], buffer[100]; + snprintf(path, 40, "/proc/%d/stat", pid); + + FILE *stat_file = fopen(path, "r"); + if (stat_file != NULL) { + fgets(buffer, 100, stat_file); + fclose(stat_file); + + // The fourth field in /proc/<pid>/stat is the parent PID + pid_t ppid; + sscanf(buffer, "%*d %*s %*c %d", &ppid); + + if (ppid == parent_pid) { + closedir(proc_dir); + // Return the child PID if the parent PID matches + return pid; + } + } + } + } + + closedir(proc_dir); + + // Return -1 if no child PID was found + return -1; +} + +int main(void) { + + // Setting up kselftest framework + ksft_print_header(); + ksft_set_plan(1); + + // Check if test is run a root + if (geteuid()) { + ksft_test_result_skip("This test needs root to run!\n"); + return 1; + } + + // forking + pid_t pid = fork(); + + // Declare a variable in both parent and child processes + int var = 17; + + if (pid == -1) { + ksft_test_result_error("%s.\n", strerror(errno)); + ksft_finished(); + return 1; + + } else if (pid == 0) { + // This is the child process + ksft_print_msg("Inside the child process.\n"); + var = 1998; + + } else { + // This is the parent process + pid_t ppid=getpid(); + ksft_print_msg("Inside the parent process.\n"); + ksft_print_msg("Child PID got from fork() return : %d\n", pid); + ksft_print_msg("Parent PID from getpid(): %d\n",ppid); + + // Find the child PID in /proc + pid_t child_pid = find_child_pid(getpid()); + if (child_pid != -1) { + ksft_set_plan(2); + if(child_pid == pid && pid != ppid && var != 1998) { + ksft_test_result_pass("Child Pid from /proc and fork() matching\n"); + ksft_test_result_pass("Child Pid != Parent pid\n"); + ksft_set_plan(3); + ksft_test_result_pass( + "After modification in child No effect on the value of 'var' in parent\n"); + ksft_exit_pass(); + return 0; + } + else { + ksft_exit_fail(); + return 1; + } + } + else { + ksft_test_result_fail("Child Pid from /proc and fork() does not match"); + ksft_exit_fail(); + return 1; + } + + // Wait for the child process to finish + wait(NULL); + } + + return 0; +} +