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