The C interface (posix interface?) for regexes has no "initialize"
function, only a free function. The comment in regcomp in
LibRegex/C/Regex.cpp notes that calling regcomp without a regfree is an
error, and will leak memory. Every single time regcomp is called on a
regex_t*, it will allocate new memory.
Make sure that all the regcomp calls are paired with a regfree in the
tests program
We can't unref an object to destruction while there's still a live
RefPtr to the object, otherwise the RefPtr destructor will try to
destroy it again, accessing the refcount of a destroyed object (before
realizing that oops! the object is already dead)
Unfortunately adopt_ref requires a reference, which obviously does not
work well with when attempting to harden against allocation failure.
The adopt_ref_if_nonnull() variant will allow you to avoid using bare
pointers, while still allowing you to handle allocation failure.
This patch adds some rudimentary tests for InodeWatcher. It tests the
basic functionality, but maybe there are corner cases I haven't caught.
Additionally, this is our first LibCore test. :^)
This allows the construction of `Variant<int, int, int>`.
While this might not seem useful, it is very useful for making variants
that contain a series of member function pointers, which I plan to use
in LibGL for glGenLists() and co.
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 adds an implementation of memmem, using the Bitap text
search algorithm for needles smaller than 32 bytes, and a naive loop
search for longer needles.
Add a test case that the timeout argument to pthread_cond_timedwait
works in LibPthread. This change also validates the new support for
timeouts to the futex syscall, as that's how condition variables are
implemented.
This adds a test for the race condition in clock_nanosleep.
The crux is that clock_nanosleep verifies that the output buffer
is writable *before* sleeping, and writes to it *after* sleeping.
In the meantime, a concurrent thread can make the output buffer
unwritable, e.g. by deallocating it.
This testcase is needlessly complex because pthread_kill is
not implemented yet. I tried to keep it as simple as possible.
Here is the relevant part of dmesg:
[nanosleep-race-outbuf-munmap(22:22)]: Unblock nanosleep-race-outbuf-munmap(20:20) due to signal
nanosleep-race-outbuf-munmap(20:20) Unrecoverable page fault, write to address 0x02130016
CRASH: Page Fault. Process: nanosleep-race-outbuf-munmap(20)
[nanosleep-race-outbuf-munmap(20:20)]: 0xc01160ff memcpy +44
[nanosleep-race-outbuf-munmap(20:20)]: 0xc014de64 Kernel::Process::crash(int, unsigned int) +782
[nanosleep-race-outbuf-munmap(20:20)]: 0xc01191b5 illegal_instruction_handler +0
[nanosleep-race-outbuf-munmap(20:20)]: 0xc011965b page_fault_handler +649
[nanosleep-race-outbuf-munmap(20:20)]: 0xc0117233 page_fault_asm_entry +22
[nanosleep-race-outbuf-munmap(20:20)]: 0xc011616b copy_to_user +102
[nanosleep-race-outbuf-munmap(20:20)]: 0xc015911f Kernel::Process::sys(Kernel::Syscall::SC_clock_nanosleep_params const*) +457
[nanosleep-race-outbuf-munmap(20:20)]: 0xc015daad syscall_handler +1130
[nanosleep-race-outbuf-munmap(20:20)]: 0xc015d597 syscall_asm_entry +29
[nanosleep-race-outbuf-munmap(20:20)]: 0x08048437 main +146
[nanosleep-race-outbuf-munmap(20:20)]: 0x08048573 _start +94
Most importantly, note that it crashes *inside*
Kernel::Process::sys.
Instead, the correct behavior is to return -EFAULT.
We should only execute the filename verbatim if it contains a slash (/)
character somewhere. Otherwise, we need to look through the entries in
the PATH environment variable.
This fixes an issue where you could easily "override" system programs
by placing them in a directory you control, and then waiting for
someone to come there and run e.g "ls" :^)
Test: LibC/exec-should-not-search-current-directory.cpp
Previously this API would return an InodeIdentifier, which meant that
there was a race in path resolution where an inode could be unlinked
in between finding the InodeIdentifier for a path component, and
actually resolving that to an Inode object.
Attaching a test that would quickly trip an assertion before.
Test: Kernel/path-resolution-race.cpp
Previously it was not possible for this function to fail. You could
exploit this by triggering the creation of a VMObject whose physical
memory range would wrap around the 32-bit limit.
It was quite easy to map kernel memory into userspace and read/write
whatever you wanted in it.
Test: Kernel/bxvga-mmap-kernel-into-userspace.cpp
Right now, permission flags passed to VFS::open() are effectively ignored, but
that is going to change.
* O_RDONLY is 0, but it's still nicer to pass it explicitly
* POSIX says that binding a Unix socket to a symlink shall fail with EADDRINUSE
It's now an error to sys$mmap() a file as writable if it's currently
mapped executable by anyone else.
It's also an error to sys$execve() a file that's currently mapped
writable by anyone else.
This fixes a race condition vulnerability where one program could make
modifications to an executable while another process was in the kernel,
in the middle of exec'ing the same executable.
Test: Kernel/elf-execve-mmap-race.cpp
It was possible to craft a custom ELF executable that when symbolicated
would cause the kernel to read from user-controlled addresses anywhere
in memory. You could then fetch this memory via /proc/PID/stack
We fix this by making ELFImage hand out StringView rather than raw
const char* for symbol names. In case a symbol offset is outside the
ELF image, you get a null StringView. :^)
Test: Kernel/elf-symbolication-kernel-read-exploit.cpp
The join_thread() syscall is not supposed to be interruptible by
signals, but it was. And since the process death mechanism piggybacked
on signal interrupts, it was possible to interrupt a pthread_join() by
killing the process that was doing it, leading to confusing due to some
assumptions being made by Thread::finalize() for threads that have a
pending joiner.
This patch fixes the issue by making "interrupted by death" a distinct
block result separate from "interrupted by signal". Then we handle that
state in join_thread() and tidy things up so that thread finalization
doesn't get confused by the pending joiner being gone.
Test: Tests/Kernel/null-deref-crash-during-pthread_join.cpp
This fixes a null RefPtr deref (which asserts) in the scheduler if a
file descriptor being select()'ed is closed by a second thread while
blocked in select().
Test: Kernel/null-deref-close-during-select.cpp
This patch fixes some issues with the mmap() and mprotect() syscalls,
neither of whom were checking the permission bits of the underlying
files when mapping an inode MAP_SHARED.
This made it possible to subvert execution of any running program
by simply memory-mapping its executable and replacing some of the code.
Test: Kernel/mmap-write-into-running-programs-executable-file.cpp
This encourages callers to strongly reference file descriptions while
working with them.
This fixes a use-after-free issue where one thread would close() an
open fd while another thread was blocked on it becoming readable.
Test: Kernel/uaf-close-while-blocked-in-read.cpp