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