LWN.net Logo

Advertisement

Connecting kernel, userspace, and graphics - the plumbing of Linux

Advertise here

Kernel Summit 2005: The ExecShield patches

Posted Aug 9, 2006 0:45 UTC (Wed) by bluefoxicy (guest, #25366)
Parent article: Kernel Summit 2005: The ExecShield patches

/me gets on his fireproof underwear :)

* Non-executable data areas. With advances in the hardware area, this issue has just about gone away. The kernel already has support for this sort of protection.

- PaX, first release October 11, full page-granularity NX bit on i386 (he still hasn't figured a way to get NX bit on ARM though).

ExecShield uses a segmentation trick; the bottom cut of the address space is executable, the top cut isn't. The highest executable mapping is also the last by this design. Problem is, that's just below the stack; all non-executable data below there will execute if you tell it to.

PaX copied this trick from ES and W^X around last year; it now serves to take the pressure off access to the stack. For areas not covered, it still uses the original method: Mark those pages SUPERVISOR so that the MMU raises a fault with the kernel on access. If kernel sees an ITLB fault it kills the process (attempt to execute); a DTLB fault (data, read-write) is allowed. This literally gives us a per-page NX bit.

I'm not sure on this, but I believe once an entry is in the DTLB, the MMU assumes it's okay for access until it gets flushed out (by a full TLB or whatnot). It still checks read/write permissions on attempt to read/write.

I'm also not sure on how it works on PPC, PPC64, SPARC, or SPARC64; none of those have NX bits in the PTE structure. Apparently there's an extra bit that can be used to coerce the CPU into letting the OS make one up (like done with i386); but on one of those it's used already for something else. Also there's crappy PLT things where the PLT is executable and shoved in a segment that holds data so a big chunk of data becomes RWX mandatory.

* Various runtime checks built into the kernel API. Arjan mentioned checking for double-free errors in particular. Freeing a chunk of memory twice, as it turns out, is a common bug - and it is exploitable.

- glibc, some time ago.

This is a good idea. It was implemented in glibc (in fact I think the patches came from in-house at RedHat).

* Address-space randomization techniques. Some of the ExecShield randomization patches were merged into 2.6.12 (see this Kernel Page article), but there are some others. The full ExecShield patch can randomize the locations of the program body, brk() area, data area, and more.

- PaX implemented randomization around 2001. It's also got better randomization over a wider area.

Yes we need brk() (heap) and main program randomization, which is in ES but not in mainline. The main program isn't going to move around unless it's a PIE; PaX can do it, but it crashes the program a lot and uses a VMA mirroring hack.

The thing is this isn't really much of a security feature. It's an amortization; if you're attacked, there's a slight chance that you'll be taken in; and a huge chance that the attack will fail. Your risk model says "WE CAN BE HACKED" but it also says "Anyone trying is likely to fail," which seems conflicting but really isn't. Turning up the randomization helps.

* The gcc FORTIFY_SOURCE option. This feature is currently only available for user-space programs, but a patch adding kernel support is circulating. This option, in particular, can check the correctness of many memory copy operations when the size of the affected buffer is known. It is less useful in the kernel - which keeps few buffers on the stack - but still worth having.

- This one came out of RedHat, and is somewhat useful.

First off FORTIFY_SOURCE does not affect only the stack; but the kernel doesn't use malloc() and thus getting this actually working right with freshly allocated buffers from kmalloc() or vmalloc() may be a pain.

FORTIFY_SOURCE may also have issue with being effective in a kernel world where things like memcpy() and strcpy() are not alone; copy_from_user() or whatever it is (I haven't kept up on my kernel hacking) needs some looks. In short, we need an actual FORTIFY_SOURCE for the kernel.

This is not much of a problem, however. FORTIFY_SOURCE uses two things from gcc: the __GNUC__ define (to see if we're using gcc), and the __builtin_* functions (mainly __builtin_object_size()). This could easily be modified for kernel use because, basically, it's completely implemented in headers.

* The -fstack-protector gcc option. It adds a canary to the stack, and can thus detect stack buffer overruns after they happen (but, with luck, before they do any harm).

- Back in the day, Crispin Cowan of Immunix wrote StackGuard (1996); then Etoh and Yoda made it ProPolice (1999); finally RedHat got it into gcc proper, direct port.

This is one I actually owe to RedHat for getting in mainline. I had been getting tired of the "if it's so good why doesn't anyone use it" argument for this, especially when OPENBSD used it forever (as much as I think OBSD is crap).

Unfortunately they made some changes. Originally the darn thing told you what function and sourcefile the stack smash occurred in; now the API can't even support that, you need to actually catch a stack smash in GDB to find out what you smashed. Great fun when you can't reproduce it. At least the function name would have been useful.

On Ubuntu, they'll be trying to figure out what called __stack_chk_fail() by attaching GDB to crashed processes. Modified glibc to trigger this and the debugging versions of the code and we can get it.

* Information hiding. The kernel exports information (through /proc/pid/maps, for example) which can be highly useful to attackers. In particular, disclosing the locations of memory areas defeats much of the advantage of randomization. So restricting access to that information improves the security of the system.

- In grsecurity forever

Can't we protect this using SELinux policy? Might take some hacking of the /proc file system mind you.

This is directly in grsecurity; they evidently had the same thought back in 2001: /proc/$pid/maps breaks ASLR if the attacker has local access. Local access isn't hard either; a vuln in a CGI script, maybe get you a directory traversal, open www.l00t.com/somescript.php?src=.\./.\./.\./.\./.\./proc/$pid/maps (the .\. tricks some sick validation code that tries to clean ..'s out of the filename), just gotta find the right process ID.

* /dev/mem is used primarily by root kits. About the only legitimate user is the X server, which uses /dev/mem for access to the frame buffer. Read access to /dev/mem is an information leak, and there is no reason for allowing write access to kernel memory at all.

- In grsecurity since forever ago as well.

Seriously there's not only a grsecurity option to prevent /dev/mem access EXCEPT for video memory; there's a policy element to do it through the ACL. That should get ported into SELinux as well, probably by an SELinux hook in the /dev/mem device itself.


(Log in to post comments)

Copyright © 2008, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds
Powered by Rackspace Managed Hosting.