Open copumpkin opened 9 years ago
I agree with your observations, and I think the answer is 3 - they do make sense, should be easy (or at least, not exceedingly difficult), but nobody's done it yet.
ASLR (address-space layout randomization) does make sense even in OSv: One might argue that OSv's single-application philosophy means that if someone breaks into the application he cannot do any sort of "privilege escalation" and break into other applications, because there are none on this VM. But nevertheless, it is always better, if there is an exploit which can break into an application, that the result of this break will be an application crash (denial of service) rather than execution of the attacker's code. This is why things like ASLR do make sense even in OSv.
OSv is alreay very-much "prepared" for ASLR, most importantly all executables are position-independent so can be moved around, but more work is needed for a full ASLR implementation:
See https://en.wikipedia.org/wiki/Address_space_layout_randomization#Linux for some more notes about how Linux did ASLR.
About W^X, I agree it's also useful to have. Our ELF object loading code already supports non-writable text pages, and even pages which are temporarily writable during the object's load and after doing relocations, made unwritable - this is the so-called "relro" feature, which OSv supports and even supports the "full relro" variant described in http://tk-blog.blogspot.co.il/2009/02/relro-not-so-well-known-memory.html. But I guess we could also offer a more strict "W^X" feature - where every page marked writable is automatically marked non-executable (the NX bit on x86_64), including (for example) stacks. I don't know if all applications can run this way, but it wouldn't hurt to allow it for applications which can run this way. By the way, OSv itself modifies its own code to enable tracepoints which might break W^X in the kernel, but since this is the only place it does this (beyond the usual relro feature that is only written on load time), I think we could start with simply not supporting tracepoints with W^X.
About W^X, I agree it's also useful to have. Our ELF object loading code already supports non-writable text pages, and even pages which are temporarily writable during the object's load and after doing relocations, made unwritable - this is the so-called "relro" feature, which OSv supports and even supports the "full relro" variant described in http://tk-blog.blogspot.co.il/2009/02/relro-not-so-well-known-memory.html. But I guess we could also offer a more strict "W^X" feature - where every page marked writable is automatically marked non-executable (the NX bit on x86_64), including (for example) stacks.
Is protecting the ELF loading process alone sufficient? I don't know the OSv code nearly well enough, but is the kernel itself loaded via the ELF loader? If so, that's probably fine, as long as newly allocated memory also enforces the separation. If the kernel is loaded in a special manner, I'd be concerned about the safely W^X-loaded program file somehow modifying (or convincing the kernel to modify) kernel memory for nefarious purposes during an exploit.
Re: the tracepoints, making the options mutually exclusive seems fine.
One small note about the state of non-executable stacks in OSv:
OSv allocates stack in two ways:
Out of curiosity, how many are the internal threads?
On Fri, Nov 13, 2015 at 1:02 AM, n03l notifications@github.com wrote:
Out of curiosity, how many are the internal threads?
OSv uses threads liberally in the kernel, because they have very little overhead besides the memory use of the stack. In particular, sleeping threads do not slow down scheduling at all.
To list the running threads, you can use gdb's "osv info threads" or scripts/top.py.
As an example I ran "make image=rogue; scripts/run.py -c1" and got 116 threads - only one of them is the application thread. Many of these threads, perhaps too many (see issue #247) - belong to ZFS. But the vast majority are idle and their only overhead are is the memory their stacks take.
Interesting discussion on ASLR and W^X support (and non-support) on OSv and other unikernels: https://x41-dsec.de/news/missing-or-weak-mitigations-in-various-unikernels/. The table suggests that OSv already supports W^X but it is contradicted by the text which says that their "test scripts" verified that it isn't implemented on OSv.
I did come across this article some time ago as well. There is actually a bit of work to accomplish your 3 original parts. Especially the kernel randomization (KASLR) would not be that easy given some of that would require tweaking assembly for both x64 and aarch64, probably writing some code to update some bits of kernel ELF to update addresses, etc.
So looking at your original 3 steps, I think the 1st one is the easiest one: Randomize the base address of loading PIEs and shared objects.
The 2nd one is nice to have but given it somewhat random already maybe it is of lower priority:
Randomize the locations of stacks, results of mmap
, and similar things. (what are other things?)
The 3rd is the most complicated one: Randomize the location of the kernel.
It would be also nice to implement W^X for kernel ELF. Right now I believe it runs with all permissions on, right? You mention tracepoints that modify some portions of kernel text. But I also think there is also some memcpy
related code (look at arch/x64/string.cc
) that picks best implementation depending on cpuid which is similar. I wonder if we can change the kernel code memory protections to X during the boot after we enable all tracepoints but before we load the apps.
I wonder if we should create 4 new (or more) finer granularity issues and kill this one.
Intuitively, it seems like these measures still apply to something like OSv. Grepping the source tree revealed no nontrivial matches for 'ASLR', so I'm wondering if:
Any pointers?