162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define _GNU_SOURCE 762306a36Sopenharmony_ci#include <unistd.h> 862306a36Sopenharmony_ci#include <errno.h> 962306a36Sopenharmony_ci#include <string.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <stdlib.h> 1262306a36Sopenharmony_ci#include <stdbool.h> 1362306a36Sopenharmony_ci#include <fcntl.h> 1462306a36Sopenharmony_ci#include <time.h> 1562306a36Sopenharmony_ci#include <sys/wait.h> 1662306a36Sopenharmony_ci#include <sys/mount.h> 1762306a36Sopenharmony_ci#include <sys/stat.h> 1862306a36Sopenharmony_ci#include <sys/types.h> 1962306a36Sopenharmony_ci#include <sys/io.h> 2062306a36Sopenharmony_ci#include <sys/ioctl.h> 2162306a36Sopenharmony_ci#include <sys/reboot.h> 2262306a36Sopenharmony_ci#include <sys/utsname.h> 2362306a36Sopenharmony_ci#include <sys/sendfile.h> 2462306a36Sopenharmony_ci#include <sys/sysmacros.h> 2562306a36Sopenharmony_ci#include <sys/random.h> 2662306a36Sopenharmony_ci#include <linux/random.h> 2762306a36Sopenharmony_ci#include <linux/version.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci__attribute__((noreturn)) static void poweroff(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci fflush(stdout); 3262306a36Sopenharmony_ci fflush(stderr); 3362306a36Sopenharmony_ci reboot(RB_AUTOBOOT); 3462306a36Sopenharmony_ci sleep(30); 3562306a36Sopenharmony_ci fprintf(stderr, "\x1b[37m\x1b[41m\x1b[1mFailed to power off!!!\x1b[0m\n"); 3662306a36Sopenharmony_ci exit(1); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void panic(const char *what) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci 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)); 4262306a36Sopenharmony_ci poweroff(); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define pretty_message(msg) puts("\x1b[32m\x1b[1m" msg "\x1b[0m") 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void print_banner(void) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct utsname utsname; 5062306a36Sopenharmony_ci int len; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (uname(&utsname) < 0) 5362306a36Sopenharmony_ci panic("uname"); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci len = strlen(" WireGuard Test Suite on ") + strlen(utsname.sysname) + strlen(utsname.release) + strlen(utsname.machine); 5662306a36Sopenharmony_ci 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, ""); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void seed_rng(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int bits = 256, fd; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!getrandom(NULL, 0, GRND_NONBLOCK)) 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci pretty_message("[+] Fake seeding RNG..."); 6662306a36Sopenharmony_ci fd = open("/dev/random", O_WRONLY); 6762306a36Sopenharmony_ci if (fd < 0) 6862306a36Sopenharmony_ci panic("open(random)"); 6962306a36Sopenharmony_ci if (ioctl(fd, RNDADDTOENTCNT, &bits) < 0) 7062306a36Sopenharmony_ci panic("ioctl(RNDADDTOENTCNT)"); 7162306a36Sopenharmony_ci close(fd); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void set_time(void) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci if (time(NULL)) 7762306a36Sopenharmony_ci return; 7862306a36Sopenharmony_ci pretty_message("[+] Setting fake time..."); 7962306a36Sopenharmony_ci if (stime(&(time_t){1433512680}) < 0) 8062306a36Sopenharmony_ci panic("settimeofday()"); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void mount_filesystems(void) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci pretty_message("[+] Mounting filesystems..."); 8662306a36Sopenharmony_ci mkdir("/dev", 0755); 8762306a36Sopenharmony_ci mkdir("/proc", 0755); 8862306a36Sopenharmony_ci mkdir("/sys", 0755); 8962306a36Sopenharmony_ci mkdir("/tmp", 0755); 9062306a36Sopenharmony_ci mkdir("/run", 0755); 9162306a36Sopenharmony_ci mkdir("/var", 0755); 9262306a36Sopenharmony_ci if (mount("none", "/dev", "devtmpfs", 0, NULL)) 9362306a36Sopenharmony_ci panic("devtmpfs mount"); 9462306a36Sopenharmony_ci if (mount("none", "/proc", "proc", 0, NULL)) 9562306a36Sopenharmony_ci panic("procfs mount"); 9662306a36Sopenharmony_ci if (mount("none", "/sys", "sysfs", 0, NULL)) 9762306a36Sopenharmony_ci panic("sysfs mount"); 9862306a36Sopenharmony_ci if (mount("none", "/tmp", "tmpfs", 0, NULL)) 9962306a36Sopenharmony_ci panic("tmpfs mount"); 10062306a36Sopenharmony_ci if (mount("none", "/run", "tmpfs", 0, NULL)) 10162306a36Sopenharmony_ci panic("tmpfs mount"); 10262306a36Sopenharmony_ci if (mount("none", "/sys/kernel/debug", "debugfs", 0, NULL)) 10362306a36Sopenharmony_ci ; /* Not a problem if it fails.*/ 10462306a36Sopenharmony_ci if (symlink("/run", "/var/run")) 10562306a36Sopenharmony_ci panic("run symlink"); 10662306a36Sopenharmony_ci if (symlink("/proc/self/fd", "/dev/fd")) 10762306a36Sopenharmony_ci panic("fd symlink"); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void enable_logging(void) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci int fd; 11362306a36Sopenharmony_ci pretty_message("[+] Enabling logging..."); 11462306a36Sopenharmony_ci fd = open("/proc/sys/kernel/printk", O_WRONLY); 11562306a36Sopenharmony_ci if (fd >= 0) { 11662306a36Sopenharmony_ci if (write(fd, "9\n", 2) != 2) 11762306a36Sopenharmony_ci panic("write(printk)"); 11862306a36Sopenharmony_ci close(fd); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci fd = open("/proc/sys/debug/exception-trace", O_WRONLY); 12162306a36Sopenharmony_ci if (fd >= 0) { 12262306a36Sopenharmony_ci if (write(fd, "1\n", 2) != 2) 12362306a36Sopenharmony_ci panic("write(exception-trace)"); 12462306a36Sopenharmony_ci close(fd); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void kmod_selftests(void) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci FILE *file; 13162306a36Sopenharmony_ci char line[2048], *start, *pass; 13262306a36Sopenharmony_ci bool success = true; 13362306a36Sopenharmony_ci pretty_message("[+] Module self-tests:"); 13462306a36Sopenharmony_ci file = fopen("/proc/kmsg", "r"); 13562306a36Sopenharmony_ci if (!file) 13662306a36Sopenharmony_ci panic("fopen(kmsg)"); 13762306a36Sopenharmony_ci if (fcntl(fileno(file), F_SETFL, O_NONBLOCK) < 0) 13862306a36Sopenharmony_ci panic("fcntl(kmsg, nonblock)"); 13962306a36Sopenharmony_ci while (fgets(line, sizeof(line), file)) { 14062306a36Sopenharmony_ci start = strstr(line, "wireguard: "); 14162306a36Sopenharmony_ci if (!start) 14262306a36Sopenharmony_ci continue; 14362306a36Sopenharmony_ci start += 11; 14462306a36Sopenharmony_ci *strchrnul(start, '\n') = '\0'; 14562306a36Sopenharmony_ci if (strstr(start, "www.wireguard.com")) 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci pass = strstr(start, ": pass"); 14862306a36Sopenharmony_ci if (!pass || pass[6] != '\0') { 14962306a36Sopenharmony_ci success = false; 15062306a36Sopenharmony_ci printf(" \x1b[31m* %s\x1b[0m\n", start); 15162306a36Sopenharmony_ci } else 15262306a36Sopenharmony_ci printf(" \x1b[32m* %s\x1b[0m\n", start); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci fclose(file); 15562306a36Sopenharmony_ci if (!success) { 15662306a36Sopenharmony_ci puts("\x1b[31m\x1b[1m[-] Tests failed! \u2639\x1b[0m"); 15762306a36Sopenharmony_ci poweroff(); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void launch_tests(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci char cmdline[4096], *success_dev; 16462306a36Sopenharmony_ci int status, fd; 16562306a36Sopenharmony_ci pid_t pid; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci pretty_message("[+] Launching tests..."); 16862306a36Sopenharmony_ci pid = fork(); 16962306a36Sopenharmony_ci if (pid == -1) 17062306a36Sopenharmony_ci panic("fork"); 17162306a36Sopenharmony_ci else if (pid == 0) { 17262306a36Sopenharmony_ci execl("/init.sh", "init", NULL); 17362306a36Sopenharmony_ci panic("exec"); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci if (waitpid(pid, &status, 0) < 0) 17662306a36Sopenharmony_ci panic("waitpid"); 17762306a36Sopenharmony_ci if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 17862306a36Sopenharmony_ci pretty_message("[+] Tests successful! :-)"); 17962306a36Sopenharmony_ci fd = open("/proc/cmdline", O_RDONLY); 18062306a36Sopenharmony_ci if (fd < 0) 18162306a36Sopenharmony_ci panic("open(/proc/cmdline)"); 18262306a36Sopenharmony_ci if (read(fd, cmdline, sizeof(cmdline) - 1) <= 0) 18362306a36Sopenharmony_ci panic("read(/proc/cmdline)"); 18462306a36Sopenharmony_ci cmdline[sizeof(cmdline) - 1] = '\0'; 18562306a36Sopenharmony_ci for (success_dev = strtok(cmdline, " \n"); success_dev; success_dev = strtok(NULL, " \n")) { 18662306a36Sopenharmony_ci if (strncmp(success_dev, "wg.success=", 11)) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci memcpy(success_dev + 11 - 5, "/dev/", 5); 18962306a36Sopenharmony_ci success_dev += 11 - 5; 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (!success_dev || !strlen(success_dev)) 19362306a36Sopenharmony_ci panic("Unable to find success device"); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci fd = open(success_dev, O_WRONLY); 19662306a36Sopenharmony_ci if (fd < 0) 19762306a36Sopenharmony_ci panic("open(success_dev)"); 19862306a36Sopenharmony_ci if (write(fd, "success\n", 8) != 8) 19962306a36Sopenharmony_ci panic("write(success_dev)"); 20062306a36Sopenharmony_ci close(fd); 20162306a36Sopenharmony_ci } else { 20262306a36Sopenharmony_ci const char *why = "unknown cause"; 20362306a36Sopenharmony_ci int what = -1; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (WIFEXITED(status)) { 20662306a36Sopenharmony_ci why = "exit code"; 20762306a36Sopenharmony_ci what = WEXITSTATUS(status); 20862306a36Sopenharmony_ci } else if (WIFSIGNALED(status)) { 20962306a36Sopenharmony_ci why = "signal"; 21062306a36Sopenharmony_ci what = WTERMSIG(status); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci printf("\x1b[31m\x1b[1m[-] Tests failed with %s %d! \u2639\x1b[0m\n", why, what); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void ensure_console(void) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci for (unsigned int i = 0; i < 1000; ++i) { 21962306a36Sopenharmony_ci int fd = open("/dev/console", O_RDWR); 22062306a36Sopenharmony_ci if (fd < 0) { 22162306a36Sopenharmony_ci usleep(50000); 22262306a36Sopenharmony_ci continue; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci dup2(fd, 0); 22562306a36Sopenharmony_ci dup2(fd, 1); 22662306a36Sopenharmony_ci dup2(fd, 2); 22762306a36Sopenharmony_ci close(fd); 22862306a36Sopenharmony_ci if (write(1, "\0\0\0\0\n", 5) == 5) 22962306a36Sopenharmony_ci return; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci panic("Unable to open console device"); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void clear_leaks(void) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci int fd; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci fd = open("/sys/kernel/debug/kmemleak", O_WRONLY); 23962306a36Sopenharmony_ci if (fd < 0) 24062306a36Sopenharmony_ci return; 24162306a36Sopenharmony_ci pretty_message("[+] Starting memory leak detection..."); 24262306a36Sopenharmony_ci write(fd, "clear\n", 5); 24362306a36Sopenharmony_ci close(fd); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void check_leaks(void) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int fd; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci fd = open("/sys/kernel/debug/kmemleak", O_WRONLY); 25162306a36Sopenharmony_ci if (fd < 0) 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci pretty_message("[+] Scanning for memory leaks..."); 25462306a36Sopenharmony_ci sleep(2); /* Wait for any grace periods. */ 25562306a36Sopenharmony_ci write(fd, "scan\n", 5); 25662306a36Sopenharmony_ci close(fd); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci fd = open("/sys/kernel/debug/kmemleak", O_RDONLY); 25962306a36Sopenharmony_ci if (fd < 0) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci if (sendfile(1, fd, NULL, 0x7ffff000) > 0) 26262306a36Sopenharmony_ci panic("Memory leaks encountered"); 26362306a36Sopenharmony_ci close(fd); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciint main(int argc, char *argv[]) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci ensure_console(); 26962306a36Sopenharmony_ci print_banner(); 27062306a36Sopenharmony_ci mount_filesystems(); 27162306a36Sopenharmony_ci seed_rng(); 27262306a36Sopenharmony_ci set_time(); 27362306a36Sopenharmony_ci kmod_selftests(); 27462306a36Sopenharmony_ci enable_logging(); 27562306a36Sopenharmony_ci clear_leaks(); 27662306a36Sopenharmony_ci launch_tests(); 27762306a36Sopenharmony_ci check_leaks(); 27862306a36Sopenharmony_ci poweroff(); 27962306a36Sopenharmony_ci return 1; 28062306a36Sopenharmony_ci} 281