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