1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2011-2017 Red Hat, Inc.
4 */
5
6 /* Description:
7 *
8 * This is a reproducer of CVE-2011-0999, which fixed by mainline commit
9 * a7d6e4ecdb7648478ddec76d30d87d03d6e22b31:
10 *
11 * "Transparent hugepages can only be created if rmap is fully
12 * functional. So we must prevent hugepages to be created while
13 * is_vma_temporary_stack() is true."
14 *
15 * It will cause a panic something like this, if the patch didn't get
16 * applied:
17 *
18 * kernel BUG at mm/huge_memory.c:1260!
19 * invalid opcode: 0000 [#1] SMP
20 * last sysfs file: /sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_map
21 * ....
22 *
23 * Due to commit da029c11e6b1 which reduced the stack size considerably, we
24 * now perform a binary search to find the largest possible argument we can
25 * use. Only the first iteration of the test performs the search; subsequent
26 * iterations use the result of the search which is stored in some shared
27 * memory.
28 */
29
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/resource.h>
33 #include <sys/wait.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include "tst_test.h"
39 #include "mem.h"
40 #include "tst_minmax.h"
41
42 #define ARGS_SZ (256 * 32)
43
44 static struct bisection {
45 long left;
46 long right;
47 long mid;
48 } *bst;
49
50 static char *args[ARGS_SZ];
51 static char *arg;
52
thp_test(void)53 static void thp_test(void)
54 {
55 long prev_left;
56 int pid;
57
58 while (bst->right - bst->left > 1) {
59 pid_t pid = SAFE_FORK();
60
61 if (!pid) {
62 /* We set mid to left assuming exec will succeed. If
63 * exec fails with E2BIG (and thus returns) then we
64 * restore left and set right to mid instead.
65 */
66 prev_left = bst->left;
67 bst->mid = (bst->left + bst->right) / 2;
68 bst->left = bst->mid;
69 args[bst->mid] = NULL;
70
71 TEST(execvp("true", args));
72 if (TST_ERR != E2BIG)
73 tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
74 bst->left = prev_left;
75 bst->right = bst->mid;
76 exit(0);
77 }
78
79 tst_reap_children();
80 tst_res(TINFO, "left: %ld, right: %ld, mid: %ld",
81 bst->left, bst->right, bst->mid);
82 }
83
84 /* We end with mid == right or mid == left where right - left =
85 * 1. Regardless we must use left because right is only set to values
86 * which are too large.
87 */
88 pid = SAFE_FORK();
89 if (pid == 0) {
90 args[bst->left] = NULL;
91 TEST(execvp("true", args));
92 if (TST_ERR != E2BIG)
93 tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
94 exit(0);
95 }
96 tst_reap_children();
97
98 tst_res(TPASS, "system didn't crash.");
99 }
100
setup(void)101 static void setup(void)
102 {
103 struct rlimit rl = {
104 .rlim_cur = RLIM_INFINITY,
105 .rlim_max = RLIM_INFINITY,
106 };
107 int i;
108 long arg_len, arg_count;
109
110 bst = SAFE_MMAP(NULL, sizeof(*bst),
111 PROT_READ | PROT_WRITE,
112 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
113 bst->left = 0;
114 bst->right = ARGS_SZ;
115
116 arg_len = sysconf(_SC_PAGESIZE);
117 arg = SAFE_MALLOC(arg_len);
118 memset(arg, 'c', arg_len - 1);
119 arg[arg_len - 1] = '\0';
120
121 args[0] = "true";
122 arg_count = ARGS_SZ;
123 tst_res(TINFO, "Using %ld args of size %ld", arg_count, arg_len);
124 for (i = 1; i < arg_count; i++)
125 args[i] = arg;
126
127 SAFE_SETRLIMIT(RLIMIT_STACK, &rl);
128 }
129
cleanup(void)130 static void cleanup(void)
131 {
132 free(arg);
133 }
134
135 static struct tst_test test = {
136 .needs_root = 1,
137 .forks_child = 1,
138 .setup = setup,
139 .cleanup = cleanup,
140 .test_all = thp_test,
141 };
142