1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. 4 */ 5 6 #define _GNU_SOURCE 7 #include <unistd.h> 8 #include <errno.h> 9 #include <string.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <stdbool.h> 13 #include <fcntl.h> 14 #include <sys/wait.h> 15 #include <sys/mount.h> 16 #include <sys/stat.h> 17 #include <sys/types.h> 18 #include <sys/io.h> 19 #include <sys/ioctl.h> 20 #include <sys/reboot.h> 21 #include <sys/utsname.h> 22 #include <sys/sendfile.h> 23 #include <sys/sysmacros.h> 24 #include <sys/random.h> 25 #include <linux/random.h> 26 #include <linux/version.h> 27 28 __attribute__((noreturn)) static void poweroff(void) 29 { 30 fflush(stdout); 31 fflush(stderr); 32 reboot(RB_AUTOBOOT); 33 sleep(30); 34 fprintf(stderr, "\x1b[37m\x1b[41m\x1b[1mFailed to power off!!!\x1b[0m\n"); 35 exit(1); 36 } 37 38 static void panic(const char *what) 39 { 40 fprintf(stderr, "\n\n\x1b[37m\x1b[41m\x1b[1mSOMETHING WENT HORRIBLY WRONG\x1b[0m\n\n \x1b[31m\x1b[1m%s: %s\x1b[0m\n\n\x1b[37m\x1b[44m\x1b[1mPower off...\x1b[0m\n\n", what, strerror(errno)); 41 poweroff(); 42 } 43 44 #define pretty_message(msg) puts("\x1b[32m\x1b[1m" msg "\x1b[0m") 45 46 static void print_banner(void) 47 { 48 struct utsname utsname; 49 int len; 50 51 if (uname(&utsname) < 0) 52 panic("uname"); 53 54 len = strlen(" WireGuard Test Suite on ") + strlen(utsname.sysname) + strlen(utsname.release) + strlen(utsname.machine); 55 printf("\x1b[45m\x1b[33m\x1b[1m%*.s\x1b[0m\n\x1b[45m\x1b[33m\x1b[1m WireGuard Test Suite on %s %s %s \x1b[0m\n\x1b[45m\x1b[33m\x1b[1m%*.s\x1b[0m\n\n", len, "", utsname.sysname, utsname.release, utsname.machine, len, ""); 56 } 57 58 static void seed_rng(void) 59 { 60 int bits = 256, fd; 61 62 if (!getrandom(NULL, 0, GRND_NONBLOCK)) 63 return; 64 pretty_message("[+] Fake seeding RNG..."); 65 fd = open("/dev/random", O_WRONLY); 66 if (fd < 0) 67 panic("open(random)"); 68 if (ioctl(fd, RNDADDTOENTCNT, &bits) < 0) 69 panic("ioctl(RNDADDTOENTCNT)"); 70 close(fd); 71 } 72 73 static void mount_filesystems(void) 74 { 75 pretty_message("[+] Mounting filesystems..."); 76 mkdir("/dev", 0755); 77 mkdir("/proc", 0755); 78 mkdir("/sys", 0755); 79 mkdir("/tmp", 0755); 80 mkdir("/run", 0755); 81 mkdir("/var", 0755); 82 if (mount("none", "/dev", "devtmpfs", 0, NULL)) 83 panic("devtmpfs mount"); 84 if (mount("none", "/proc", "proc", 0, NULL)) 85 panic("procfs mount"); 86 if (mount("none", "/sys", "sysfs", 0, NULL)) 87 panic("sysfs mount"); 88 if (mount("none", "/tmp", "tmpfs", 0, NULL)) 89 panic("tmpfs mount"); 90 if (mount("none", "/run", "tmpfs", 0, NULL)) 91 panic("tmpfs mount"); 92 if (mount("none", "/sys/kernel/debug", "debugfs", 0, NULL)) 93 ; /* Not a problem if it fails.*/ 94 if (symlink("/run", "/var/run")) 95 panic("run symlink"); 96 if (symlink("/proc/self/fd", "/dev/fd")) 97 panic("fd symlink"); 98 } 99 100 static void enable_logging(void) 101 { 102 int fd; 103 pretty_message("[+] Enabling logging..."); 104 fd = open("/proc/sys/kernel/printk", O_WRONLY); 105 if (fd >= 0) { 106 if (write(fd, "9\n", 2) != 2) 107 panic("write(printk)"); 108 close(fd); 109 } 110 fd = open("/proc/sys/debug/exception-trace", O_WRONLY); 111 if (fd >= 0) { 112 if (write(fd, "1\n", 2) != 2) 113 panic("write(exception-trace)"); 114 close(fd); 115 } 116 } 117 118 static void kmod_selftests(void) 119 { 120 FILE *file; 121 char line[2048], *start, *pass; 122 bool success = true; 123 pretty_message("[+] Module self-tests:"); 124 file = fopen("/proc/kmsg", "r"); 125 if (!file) 126 panic("fopen(kmsg)"); 127 if (fcntl(fileno(file), F_SETFL, O_NONBLOCK) < 0) 128 panic("fcntl(kmsg, nonblock)"); 129 while (fgets(line, sizeof(line), file)) { 130 start = strstr(line, "wireguard: "); 131 if (!start) 132 continue; 133 start += 11; 134 *strchrnul(start, '\n') = '\0'; 135 if (strstr(start, "www.wireguard.com")) 136 break; 137 pass = strstr(start, ": pass"); 138 if (!pass || pass[6] != '\0') { 139 success = false; 140 printf(" \x1b[31m* %s\x1b[0m\n", start); 141 } else 142 printf(" \x1b[32m* %s\x1b[0m\n", start); 143 } 144 fclose(file); 145 if (!success) { 146 puts("\x1b[31m\x1b[1m[-] Tests failed! \u2639\x1b[0m"); 147 poweroff(); 148 } 149 } 150 151 static void launch_tests(void) 152 { 153 char cmdline[4096], *success_dev; 154 int status, fd; 155 pid_t pid; 156 157 pretty_message("[+] Launching tests..."); 158 pid = fork(); 159 if (pid == -1) 160 panic("fork"); 161 else if (pid == 0) { 162 execl("/init.sh", "init", NULL); 163 panic("exec"); 164 } 165 if (waitpid(pid, &status, 0) < 0) 166 panic("waitpid"); 167 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 168 pretty_message("[+] Tests successful! :-)"); 169 fd = open("/proc/cmdline", O_RDONLY); 170 if (fd < 0) 171 panic("open(/proc/cmdline)"); 172 if (read(fd, cmdline, sizeof(cmdline) - 1) <= 0) 173 panic("read(/proc/cmdline)"); 174 cmdline[sizeof(cmdline) - 1] = '\0'; 175 for (success_dev = strtok(cmdline, " \n"); success_dev; success_dev = strtok(NULL, " \n")) { 176 if (strncmp(success_dev, "wg.success=", 11)) 177 continue; 178 memcpy(success_dev + 11 - 5, "/dev/", 5); 179 success_dev += 11 - 5; 180 break; 181 } 182 if (!success_dev || !strlen(success_dev)) 183 panic("Unable to find success device"); 184 185 fd = open(success_dev, O_WRONLY); 186 if (fd < 0) 187 panic("open(success_dev)"); 188 if (write(fd, "success\n", 8) != 8) 189 panic("write(success_dev)"); 190 close(fd); 191 } else { 192 const char *why = "unknown cause"; 193 int what = -1; 194 195 if (WIFEXITED(status)) { 196 why = "exit code"; 197 what = WEXITSTATUS(status); 198 } else if (WIFSIGNALED(status)) { 199 why = "signal"; 200 what = WTERMSIG(status); 201 } 202 printf("\x1b[31m\x1b[1m[-] Tests failed with %s %d! \u2639\x1b[0m\n", why, what); 203 } 204 } 205 206 static void ensure_console(void) 207 { 208 for (unsigned int i = 0; i < 1000; ++i) { 209 int fd = open("/dev/console", O_RDWR); 210 if (fd < 0) { 211 usleep(50000); 212 continue; 213 } 214 dup2(fd, 0); 215 dup2(fd, 1); 216 dup2(fd, 2); 217 close(fd); 218 if (write(1, "\0\0\0\0\n", 5) == 5) 219 return; 220 } 221 panic("Unable to open console device"); 222 } 223 224 static void clear_leaks(void) 225 { 226 int fd; 227 228 fd = open("/sys/kernel/debug/kmemleak", O_WRONLY); 229 if (fd < 0) 230 return; 231 pretty_message("[+] Starting memory leak detection..."); 232 write(fd, "clear\n", 5); 233 close(fd); 234 } 235 236 static void check_leaks(void) 237 { 238 int fd; 239 240 fd = open("/sys/kernel/debug/kmemleak", O_WRONLY); 241 if (fd < 0) 242 return; 243 pretty_message("[+] Scanning for memory leaks..."); 244 sleep(2); /* Wait for any grace periods. */ 245 write(fd, "scan\n", 5); 246 close(fd); 247 248 fd = open("/sys/kernel/debug/kmemleak", O_RDONLY); 249 if (fd < 0) 250 return; 251 if (sendfile(1, fd, NULL, 0x7ffff000) > 0) 252 panic("Memory leaks encountered"); 253 close(fd); 254 } 255 256 int main(int argc, char *argv[]) 257 { 258 ensure_console(); 259 print_banner(); 260 mount_filesystems(); 261 seed_rng(); 262 kmod_selftests(); 263 enable_logging(); 264 clear_leaks(); 265 launch_tests(); 266 check_leaks(); 267 poweroff(); 268 return 1; 269 } 270