Age | Commit message (Collapse) | Author |
|
|
|
When entering the kernel from a syscall, we now insert a small bit of
stack padding after the RegisterDump. This makes kernel stacks less
deterministic across syscalls and may make some bugs harder to exploit.
Inspired by Elena Reshetova's talk on kernel stack exploitation.
|
|
These were doing mostly the same things, so let's just share the code.
|
|
It's still possible to read the TSC via the read_tsc() syscall, but we
will now clear some of the bottom bits for unprivileged users.
|
|
The LinkDemo program calls dlopen/dlsym/dlclose to try and load
a dyanmic library from /usr/lib. It read a global variable and
calls a global function (extern "C" of course :) ).
There a few hacks left in the LinkLib dynamic library, however.
In order to get the linker to stop complaining, we have to use
-nostartfiles -ffreestanding otherwise it will link crt0.o to our
shared object, which is definitely not right as the _init function
for a main program (that calls main) is not suitable for our lib
|
|
All threads were running with iomapbase=0 in their TSS, which the CPU
interprets as "there's an I/O permission bitmap starting at offset 0
into my TSS".
Because of that, any bits that were 1 inside the TSS would allow the
thread to execute I/O instructions on the port with that bit index.
Fix this by always setting the iomapbase to sizeof(TSS32), and also
setting the TSS descriptor's limit to sizeof(TSS32), effectively making
the I/O permissions bitmap zero-length.
This should make it no longer possible to do I/O from userspace. :^)
|
|
x86 descriptor limits are 20 bytes, not 24 bytes. This was already
a 4-bit wide bitfield, so no damage done, but let's be correct.
|
|
Lazy FPU restore is well known to be vulnerable to timing attacks,
and eager restore is a lot simpler anyway, so let's just do it eagerly.
|
|
This prevents code running outside of kernel mode from using the
following instructions:
* SGDT - Store Global Descriptor Table
* SIDT - Store Interrupt Descriptor Table
* SLDT - Store Local Descriptor Table
* SMSW - Store Machine Status Word
* STR - Store Task Register
There's no need for userspace to be able to use these instructions so
let's just disable them to prevent information leakage.
|
|
We now refuse to boot on machines that don't support PAE since all
of our paging code depends on it.
Also let's only enable SSE and PGE support if the CPU advertises it.
|
|
This prevents the kernel from jumping to code in userspace memory.
|
|
These should just fail with EPERM if you're not the superuser.
|
|
|
|
Let's reject address ranges that wrap around the 2^32 mark.
|
|
Thanks to yyyyyyy for finding the bug! :^)
|
|
|
|
At the moment, addresses below 8MB and above 3GB are never accessible
to userspace, so just reject them without even looking at the current
process's memory regions.
|
|
/bin/ping is now setuid-root, and will drop privileges immediately
after opening a raw socket.
|
|
This patch hardens /proc a bit by making many things only accessible
to UID 0, and also disallowing access to /proc/PID/ for anyone other
than the UID of that process (and superuser, obviously.)
|
|
There's a lot more of this and we need to stop printing kernel pointers
anywhere but the debug console.
|
|
This one is less obviously exploitable than the previous one, but still
a bug nonetheless.
|
|
We were happily allowing syscalls with pointers into kernel-only
regions (virtual address >= 0xc0000000).
This patch fixes that by only considering user regions in the current
process, and also double-checking the Region::is_user_accessible() flag
before approving an access.
Thanks to Fire30 for finding the bug! :^)
|
|
Let's also have set_process_boost() for giving all threads in a process
the same boost.
|
|
This patch introduces a syscall:
int set_thread_boost(int tid, int amount)
You can use this to add a permanent boost value to the effective thread
priority of any thread with your UID (or any thread in the system if
you are the superuser.)
This is quite crude, but opens up some interesting opportunities. :^)
|
|
Threads now have numeric priorities with a base priority in the 1-99
range.
Whenever a runnable thread is *not* scheduled, its effective priority
is incremented by 1. This is tracked in Thread::m_extra_priority.
The effective priority of a thread is m_priority + m_extra_priority.
When a runnable thread *is* scheduled, its m_extra_priority is reset to
zero and the effective priority returns to base.
This means that lower-priority threads will always eventually get
scheduled to run, once its effective priority becomes high enough to
exceed the base priority of threads "above" it.
The previous values for ThreadPriority (Low, Normal and High) are now
replaced as follows:
Low -> 10
Normal -> 30
High -> 50
In other words, it will take 20 ticks for a "Low" priority thread to
get to "Normal" effective priority, and another 20 to reach "High".
This is not perfect, and I've used some quite naive data structures,
but I think the mechanism will allow us to build various new and
interesting optimizations, and we can figure out better data structures
later on. :^)
|
|
If an mmap fails to allocate a region, but the addr passed in was
non-zero, non-fixed mmaps should attempt to allocate at any available
virtual address.
|
|
|
|
|
|
|
|
This is memory that's loaded from an inode (file) but not modified in
memory, so still identical to what's on disk. This kind of memory can
be freed and reloaded transparently from disk if needed.
|
|
Dirty private memory is all memory in non-inode-backed mappings that's
process-private, meaning it's not shared with any other process.
This patch exposes that number via SystemMonitor, giving us an idea of
how much memory each process is responsible for all on its own.
|
|
|
|
|
|
|
|
|
|
|
|
PR #591 defines the rationale for kernel-level timers. They're most
immediately useful for TCP retransmission, but will most likely see use
in many other areas as well.
|
|
This patch introduces three separate thread queues, one for each thread
priority available to userspace (Low, Normal and High.)
Each queue operates in a round-robin fashion, but we now always prefer
to schedule the highest priority thread that currently wants to run.
There are tons of tweaks and improvements that we can and should make
to this mechanism, but I think this is a step in the right direction.
This makes WindowServer significantly more responsive while one of its
clients is burning CPU. :^)
|
|
|
|
I don't know why I put this in the scheduler to begin with.. the caller
can just block until the beeping is finished.
|
|
We don't care about dead processes that were once members of a specific
process group.
This was causing us to try and send SIGINT to already-dead processes
when pressing Ctrl+C in a terminal whose pgrp they were once in.
Fixes #922.
|
|
Instead of panicking right away when we run out of physical pages,
we now try to find a PurgeableVMObject with some volatile pages in it.
If we find one, we purge that entire object and steal one of its pages.
This makes it possible for the kernel to keep going instead of dying.
Very cool. :^)
|
|
We were listing the total number of user/super pages as the number of
"available" pages in the system. This was then misinterpreted in the
SystemMonitor program and displayed wrong in the GUI.
|
|
|
|
Previously we assumed all hosts would have support for IA32_EFER.NXE.
This is mostly true for newer hardware, but older hardware will crash
and burn if you try to use this feature.
Now we check for support via CPUID.80000001[20].
|
|
This patch implements a simple version of the futex (fast userspace
mutex) API in the kernel and uses it to make the pthread_cond_t API's
block instead of busily sched_yield().
An arbitrary userspace address is passed to the kernel as a "token"
that identifies the futex and you can then FUTEX_WAIT and FUTEX_WAKE
that specific userspace address.
FUTEX_WAIT corresponds to pthread_cond_wait() and FUTEX_WAKE is used
for pthread_cond_signal() and pthread_cond_broadcast().
I'm pretty sure I'm missing something in this implementation, but it's
hopefully okay for a start. :^)
|
|
From now on, you'll have to request executable memory specifically
if you want some.
|
|
This removes the ability to jump into kmalloc memory, etc.
Only the kernel image itself is allowed to exec, located between 1-2MB.
|
|
Also share some validation logic between mmap() and mprotect().
|
|
This should give us access to the largest set of CPU features available
on the host machine.
|