There was a race window in a bunch of syscalls between calling
Thread::from_tid() and checking if the found thread was in the same
process as the calling thread.
If the found thread object was destroyed at that point, there was a
use-after-free that could be exploited by filling the kernel heap with
something that looked like a thread object.
While I was bringing up multitasking, I put the current PID in the SS2
(ring 2 stack segment) slot of the TSS. This was so I could see which
PID was currently running when just inspecting the CPU state.
Memory validation is used to verify that user syscalls are allowed to
access a given memory range. Ring 0 threads never make syscalls, and
so will never end up in validation anyway.
The reason we were allowing kmalloc memory accesses is because kernel
thread stacks used to be allocated in kmalloc memory. Since that's no
longer the case, we can stop making exceptions for kmalloc in the
validation code.
Move timeout management to the ReadBlocker and WriteBlocker classes.
Also get rid of the specialized ReceiveBlocker since it no longer does
anything that ReadBlocker can't do.
We now keep DNS answers around in a cache for TTL seconds after getting
them the first time. The cache is capped at 256 responses for now.
Suggested by @zecke in #10.
To protect against DNS spoof attacks, we now check that the questions
in incoming responses match the questions in the request we sent out.
Suggested by @zecke in #10.
- Break out request building into a DNSRequest class.
- Break out response parsing into a DNSResponse class.
A DNSRequest contains one or more DNSQuestion objects.
A DNSResponse contains all the DNSQuestions asked, and a DNSAnswer
object for each answer.
If a DNS server responds with multiple answers for a question, we will
get a newline-separated sequence of answers from LookupServer.
However, we don't handle this properly yet in LibC, so just split the
response by line and only care about the first answer for now.
Vector::ensure_capacity() makes sure the underlying vector buffer can
contain all the data, but it doesn't update the Vector::size().
As a result, writev() would simply collect all the buffers to write,
and then do nothing.
It was possible to read uninitialized kernel memory via getsockname().
Of course, kmalloc() is a good boy and scrubs new allocations with 0xBB
so all you got was a bunch of 0xBB.
This implementation uses the new helper method of Bitmap called
find_longest_range_of_unset_bits. This method looks for the biggest
range of contiguous bits unset in the bitmap and returns the start of
the range back to the caller.