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