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