xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fork/fork14.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2014  Red Hat, Inc.
4  * Copyright (C) 2024 SUSE LLC Andrea Cervesato <[email protected]>
5  */
6 
7 /*\
8  * [Description]
9  *
10  * This test is a reproducer for kernel 3.5:
11  * 7edc8b0ac16c ("mm/fork: fix overflow in vma length when copying mmap on clone")
12  *
13  * Since VMA length in dup_mmap() is calculated and stored in a unsigned
14  * int, it will overflow when length of mmaped memory > 16 TB. When
15  * overflow occurs, fork will incorrectly succeed. The patch above fixed it.
16  */
17 
18 #include "tst_test.h"
19 #include <stdlib.h>
20 #include <sys/wait.h>
21 
22 #define LARGE		(16 * 1024)
23 #define EXTENT		(16 * 1024 + 10)
24 
25 static char **memvec;
26 
run(void)27 static void run(void)
28 {
29 	int i, j, ret;
30 	pid_t pid;
31 	void *mem;
32 	int prev_failed = 0;
33 	int passed = 1;
34 	int failures = 0;
35 
36 	memset(memvec, 0, EXTENT);
37 
38 	for (i = 0; i < EXTENT; i++) {
39 		mem = mmap(NULL, 1 * TST_GB,
40 			PROT_READ | PROT_WRITE,
41 			MAP_PRIVATE | MAP_ANONYMOUS,
42 			0, 0);
43 
44 		if (mem == MAP_FAILED) {
45 			failures++;
46 
47 			tst_res(TINFO, "mmap() failed");
48 
49 			if (failures > 10) {
50 				tst_brk(TCONF, "mmap() fails too many "
51 					"times, so it's almost impossible to "
52 					"get a vm_area_struct sized 16TB");
53 			}
54 
55 			continue;
56 		}
57 
58 		memvec[i] = mem;
59 
60 		pid = fork();
61 
62 		if (pid == -1) {
63 			/* keep track of the failed fork() and verify that next one
64 			 * is failing as well.
65 			 */
66 			prev_failed = 1;
67 			continue;
68 		}
69 
70 		if (!pid)
71 			exit(0);
72 
73 		ret = waitpid(pid, NULL, 0);
74 		if (ret == -1 && errno != ECHILD)
75 			tst_brk(TBROK | TERRNO, "waitpid() error");
76 
77 		if (prev_failed && i >= LARGE) {
78 			passed = 0;
79 			break;
80 		}
81 
82 		prev_failed = 0;
83 
84 		tst_res(TDEBUG, "fork() passed at %d attempt", i);
85 	}
86 
87 	for (j = 0; j < i; j++) {
88 		if (memvec[j])
89 			SAFE_MUNMAP(memvec[j], 1 * TST_GB);
90 	}
91 
92 	if (passed)
93 		tst_res(TPASS, "fork() failed as expected");
94 	else
95 		tst_res(TFAIL, "fork() succeeded incorrectly");
96 }
97 
setup(void)98 static void setup(void)
99 {
100 	memvec = SAFE_MALLOC(EXTENT * sizeof(char *));
101 }
102 
cleanup(void)103 static void cleanup(void)
104 {
105 	for (long i = 0; i < EXTENT; i++) {
106 		if (memvec && memvec[i])
107 			SAFE_MUNMAP(memvec[i], 1 * TST_GB);
108 	}
109 
110 	if (memvec)
111 		free(memvec);
112 }
113 
114 static struct tst_test test = {
115 	.test_all = run,
116 	.setup = setup,
117 	.cleanup = cleanup,
118 	.forks_child = 1,
119 	.needs_abi_bits = 64,
120 	.tags = (const struct tst_tag[]) {
121 		{"linux-git", "7edc8b0ac16c"},
122 		{}
123 	}
124 };
125