Exec-Shield vs. PaX
since a few points have been made regarding $subject, let me clear
up a few of them:
1. 'It seems that exec-shield does 99% of what PaX does'
i don't know the origin of that number above, for now i'll just
stick to the facts i know:
- PaX implements perfect non-executable pages on amd64, i386,
ia64, parisc, ppc, sparc and sparc64 whereas Exec-Shield has
some imitation of it only on i386 (it's not true per-page).
- PaX implements a concept about how runtime code generation
should be done, there's nothing similar in Exec-Shield, and
it seems that Ingo does not even understand why this is
important (for Ingo: please read and understand [1] before
you call something bogus, see below for more).
- PaX implements best-effort randomization of the entire
address space, Exec-Shield does it too but at a higher
code complexity and a lower entropy rate while having
a worse effect on the kernel entropy pool.
2. paxtest 'proofs'
i saw several people point to paxtest results to 'prove' how
good Exec-Shield it is. it is not. first, Exec-Shield has a
fundamental design problem stemming from the lack of understanding
or design on Ingo's part (what i call MPROTECT in PaX). you'll
really have to read the PaX design docs to understand its role
in the grand scheme of things (see below for a bit more).
second, paxtest had some bugs which Exec-Shield exposed and made
Exec-Shield appear better than it is. i've fixed them here and
expect to release 0.9.5 today or so. the results now look like:
PaXtest - Copyright(c) 2003 by Peter Busser <peter@adamantix.org>
Released under the GNU Public Licence version 2 or later
It may take a while for the tests to complete
Test results:
PaXtest - Copyright(c) 2003 by Peter Busser <peter@adamantix.org>
Released under the GNU Public Licence version 2 or later
Executable anonymous mapping : Vulnerable
Executable bss : Vulnerable
Executable data : Vulnerable
Executable heap : Vulnerable
Executable stack : Vulnerable
the above changes are the result of Ingo's approach to create
non-executable memory on i386, they're not per page as a simple
mprotect on the top of the stack shows. before i get accused of
specifically rigging the tests, i'll tell you that running
multithreaded apps would have almost the same effect (only the
main stack would stay non-exec under Exec-Shield). needless to
say, PaX passes all the above as before.
Executable anonymous mapping (mprotect) : Vulnerable
Executable bss (mprotect) : Vulnerable
Executable data (mprotect) : Vulnerable
Executable heap (mprotect) : Vulnerable
Executable shared library bss (mprotect) : Vulnerable
Executable shared library data (mprotect): Vulnerable
Executable stack (mprotect) : Vulnerable
Anonymous mapping randomisation test : 8 bits (guessed)
Heap randomisation test (ET_EXEC) : 13 bits (guessed)
Heap randomisation test (ET_DYN) : 13 bits (guessed)
Main executable randomisation (ET_EXEC) : No randomisation
Main executable randomisation (ET_DYN) : 12 bits (guessed)
Shared library randomisation test : 12 bits (guessed)
Stack randomisation test (SEGMEXEC) : 17 bits (guessed)
Stack randomisation test (PAGEEXEC) : 17 bits (guessed)
Return to function (strcpy) : Vulnerable
Return to function (strcpy, RANDEXEC) : Vulnerable
Return to function (memcpy) : Vulnerable
Return to function (memcpy, RANDEXEC) : Vulnerable
Executable shared library bss : Vulnerable
Executable shared library data : Vulnerable
these two had bugs in them (they were trying to execute code
from the wrong location). i find it somewhat funny that the
Exec-Shield proponents (including Ingo himself) have used the
previous false results as a justification for their claims.
apparently none of you understood what the tests and Exec-
Shield did, otherwise you would have known that Exec-Shield
cannot possibly pass these tests due to its design (or at
least not without going down the OpenBSD road).
Writable text segments : Vulnerable
3. MPROTECT is bogus
it is not. Ingo says so because he did not understand how
PaX works. the short story (there's no substitute to reading
the docs!) is that in PaX we want to handle the problems posed
by memory corruption bugs. all such bugs. the approach chosen
in PaX is based on preventing exploit techniques from working
(vs. preventing specific bug situations from occuring, at least
for now, efficient full runtime bounds checking will have to
wait a bit). in the docs you will find a classificaiton of
exploit techniques:
(1) introduce/execute arbitrary code
(2) execute existing code out of original program order
(3) execute existing code in original program order with arbitrary data
every possible exploit technique against memory corruption bugs
belongs to one of the above (note that it is a classification of
exploit techniques, not bugs, that is, a given technique can be
used against different bugs and a given bug can be exploited by
more than one technique). the idea in PaX is that we try to prevent
entire classes of exploit techniques from working, starting with
the easy ones and going up to the hard problems. in the present
situation (1) has been dealt with, (2) is in the works, and (3)
is somewhere in the future (pending some research).
dispite Ingo's claim, restricting page protection transitions on
memory pages (which is what the mprotect() restrictions do) is
mandatory for our purposes, far from being bogus. without it, it
remains possible to introduce/execute new code into a process.
with MPROTECT and the ACL system (or a read-only chroot) to prevent
arbitrary file mappings and/or creation you can prevent attack
method (1), guaranteed. the fact that PaX does not contain the
ACL part is the simple consequence of its being used by different
projects who want to do it their way, i explicitly chose not to
stand in there (for the same reason PaX itself does not prevent
information leaking in /proc, that's up to the patch integrators).
Ingo also implied that executing an in-memory shell script via
system() does (1). it does not, it does (2). think about it,
(1) is about executable machine code, a shell script is not
machine code.
Ingo also confuses MPROTECT with mprotect() restrictions, the
former is much more than the latter, it's about separating the
writable pages from executable ones for the purposes of preventing
(1), something that Exec-Shield doesn't (cannot) do.
Ingo also argues about non-completness... in the main doc there's
an explicit paragraph for people like him, please read it again.
it will explain to you why you cannot judge a building block on
its own, only together with others. the combined effect is much
more than what the individual pieces are capable of. PaX is not
a finished project, there are several pieces of the puzzle yet
to be written.
4. PaX breaks apps, specs, whatnot
i chose to enable most of PaX by default (while allowing as many
apps to run as possible, e.g., that's why ELF text relocs are
allowed by default). since PaX explicitly wants to prevent runtime
code generation, obviously apps that want to do it will no longer
work. there are two categories of such apps and two ways to fix
them.
- apps that don't really need to generate code runtime but still
do it for whatever reason: they are buggy and must be fixed at
the source level. one prime example is the module loader code
in XFree86 which assumes that malloc()'ed memory was executable
whereas it was not (run strace on any app and you'll see how
glibc uses mmap() to request memory). another good example is
the various .plt stubs that are runtime generated on many risc
archs, for now i added emulation to them, but eventually they'd
better be redesigned a la i386.
- apps that by their nature want to generate code runtime (e.g.,
java). they're broken for good which many people noticed by now.
that's good, that was my purpose because i wanted to draw
attention to the fact that runtime code generation is an
important privilege that should be carefully managed (as it
happens to be also one of the exploit techniques). of course if
you don't agree with my exploit technique classification and
the general solution approach i took with PaX, then there's
nothing to discuss and you can stop reading this ;-). for those
who are interested in solving this problem, please read my
proposal in mprotect.txt (available at [1]).
about PaX breaking specs: i urge Ingo and others saying this to
point me to the precise location in SUSv3 or POSIX 1003.1-2001
that PaX conflicts with. for all i know, none of these standards
give ANY guarantees about the ability to generate code at runtime.
and rightly so, just think of non-coherent cache systems, you
need explicit userland (or kernel assisted, not sure how all such
systems solve it) flush interfaces, nothing like that is in the
standards (msync() is for something else, nor is it used by any
code i know that generates code at runtime, so they'd be non-compliant
even if msync() was meant for this purpose).
about SEGMEXEC and the 1.5GB limit: yes, it's true, yes, it could
be changed (if one can live with a more limited range for executable
mappings), yes, it would need some userland tagging, yes, i have
ideas for it, no, it's got nothing to do with PT_GNU_STACK. the
longer story is that the majority of users don't need to care about
this address space limit, it's mainly databases and some scientific
applications that run into. mind you, they already had this problem
before when people were using 1-2 GB userlands (which back then
was the way to allow the kernel to use more than 1 GB RAM, and even
these days it's the only way to go if you don't want HIGHMEM).
as for userland tagging: i'd like to have much more control over
what userland can request from the kernel than what PT_GNU_STACK
allows. for one, requesting executable stacks is irrelevant, it
should be about requesting permission to generate code at runtime
(or just use my proposed mmap() interface changes), request
randomization, etc. i'd also like to have control over the
placement of the stack and the userland address space size in
the future. for now chpax is the quick & dirty solution for
tagging, although users of complete systems with ACLs (grsec
or RSBAC in Adamantix) can control the PaX flags via ACLs,
no need for binary modifications. mind you, it's somewhat
misleading to state that PT_GNU_STACK is a better solution
than chpax, after all, someone HAS to decide whether a given
module needs it or not (before someone points it out, trampolines
are only a small and least important part of the problem), the
toolchain cannot automatically determine it, so it needs the
exact same manual work as chpax.
i'm sure i left questions unanswered, feel free to ask here or
in private.
[1] http://pageexec.virtualave.net/docs/
PS: please CC on responses.
Reply to: