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
37static 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
45static 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
57static 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
82static 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
109static 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
133static 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
166static 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
221static 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
239static 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
251static 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
271int 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