diff mbox series

Add Selftest to test fork() syscall

Message ID 20241022204215.61256-1-cvam0000@gmail.com
State New
Headers show
Series Add Selftest to test fork() syscall | expand

Commit Message

Shivam Chaudhary Oct. 22, 2024, 8:42 p.m. UTC
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

Comments

Shuah Khan Oct. 23, 2024, 8:45 p.m. UTC | #1
On 10/22/24 14:42, Shivam Chaudhary wrote:
> 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.

Short log should incude the subsystem:

e.g: selftests: add test for fork() syscall

In anycase, I don't see any value to adding this test.
fork() is a heavily used system call.
  
thanks,
-- Shuah
Shivam Chaudhary Oct. 23, 2024, 9:11 p.m. UTC | #2
On 24/10/24 2:15 AM, Shuah Khan wrote:
> On 10/22/24 14:42, Shivam Chaudhary wrote:
>> 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.
>
> Short log should incude the subsystem:
>
> e.g: selftests: add test for fork() syscall
>
> In anycase, I don't see any value to adding this test.
> fork() is a heavily used system call.
>

Thanks for responding Shuah,

Yes, you are correct that fork() is a heavily used syscall, that is why 
my concern is that

it could fail millions of other program that depends on fork() if any 
error or regression

occurs in the future. In my opinion, that is why we should test it every 
way possible.


thanks
Shivam
Shuah Khan Oct. 23, 2024, 10:06 p.m. UTC | #3
On 10/23/24 15:11, Shivam Chaudhary wrote:
> 
> On 24/10/24 2:15 AM, Shuah Khan wrote:
>> On 10/22/24 14:42, Shivam Chaudhary wrote:
>>> 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.
>>
>> Short log should incude the subsystem:
>>
>> e.g: selftests: add test for fork() syscall
>>
>> In anycase, I don't see any value to adding this test.
>> fork() is a heavily used system call.
>>
> 
> Thanks for responding Shuah,
> 
> Yes, you are correct that fork() is a heavily used syscall, that is why my concern is that
> 
> it could fail millions of other program that depends on fork() if any error or regression
> 
> occurs in the future. In my opinion, that is why we should test it every way possible.
> 
> 

Sorry. I don't see any value in adding this test.

thanks,
-- Shuah
diff mbox series

Patch

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;
+}
+