Introduction

PaXtest is a set of programs, originally meant to test various aspects of the userland-focused features of the PaX patch (now part of grsecurity) which, over time, partly emerged in some form in upstream Linux and other operating systems, namely address space layout randomization (ASLR), exclusive memory protection (PAGEEXEC and MPROTECT, NX bit in x86-64 CPUs, W^X in *BSD, DEP in Windows).

The initial set of tests were created by Peter Busser more than two decades ago and were maintained by PaX Team and Brad Spengler after Peter stopped working on it. Brad added support for non-Intel based architectures and added new tests, as well as fixing bugs. However, development of paxtest has stalled for a while.

New Release after a Long Hiatus

Today we announce a new release of PaXtest, version 0.10.0. 🥳

It’s the first release in a long time, fixing numerous bugs related to compiler optimizations detecting undefined behaviour in various buffer overflow tests, simplifying them to no-ops, leading to false positives.

PaXtest 0.10.0 also gained new tests, probing various forms of huge-page mappings, in particular:

  1. Anonymous huge page mappings,
  2. huge-page sized file mappings,
  3. position independent executables with text sections requiring huge-page alignment and
  4. shared libraries with text sections requiring huge-page alignment.

Cases 1 to 3 are completely under control of the kernel and the amount of entropy provided gets directly mirrored by the returned address. Case 4, however, is largely subject to the runtime linker ld-linux.so.

Test Results

It’s been a long time since fresh numbers were provided. About time to post an update!

Below are the results of running "paxtest blackhat" on a i7-1260P Debian sid system running a 64-bit kernel.

The kernel configurations for both kernels were left at default. For the Debian kernel, the settings of the relevant ASLR knobs are:

CONFIG_ARCH_MMAP_RND_BITS_MIN=28
CONFIG_ARCH_MMAP_RND_BITS_MAX=32
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
CONFIG_ARCH_MMAP_RND_BITS=28
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8

64-bit Native Tests

Debian

PaXtest - Copyright(c) 2003-2016 by Peter Busser <peter@adamantix.org> and Brad Spengler <spender@grsecurity.net>
Released under the GNU Public Licence version 2 or later

Mode: 1
Blackhat
Kernel: 
Linux nuc 6.6.15-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.6.15-2 (2024-02-04) x86_64 GNU/Linux

Relase information: 
Distributor ID: Debian
Description:    Debian GNU/Linux trixie/sid
Release:    n/a
Codename:   trixie
Test results:
Executable anonymous mapping             : Killed
Executable bss                           : Killed
Executable data                          : Killed
Executable heap                          : Killed
Executable stack                         : Killed
Executable shared library bss            : Killed
Executable shared library data           : Killed
Executable anonymous mapping (mprotect)  : Vulnerable
Executable bss (mprotect)                : Vulnerable
Executable data (mprotect)               : Vulnerable
Executable heap (mprotect)               : Vulnerable
Executable stack (mprotect)              : Vulnerable
Executable shared library bss (mprotect) : Vulnerable
Executable shared library data (mprotect): Vulnerable
Writable text segments                   : Vulnerable
Anonymous mapping randomization test     : 28 quality bits (guessed)
Anonymous huge mapping randomization test: 28 quality bits (guessed)
Heap randomization test (ET_EXEC)        : 13 quality bits (guessed)
Heap randomization test (PIE)            : 28 quality bits (guessed)
Main executable randomization (ET_EXEC)  : No randomization
Main executable randomization (PIE)      : 28 quality bits (guessed)
Big main executable randomization (PIE)  : 19 quality bits (guessed)
File mapping randomization test          : 28 quality bits (guessed)
File huge mapping randomization test     : 19 quality bits (guessed)
Shared library randomization test        : 28 quality bits (guessed)
Big shared library randomization test    : 19 quality bits (guessed)
VDSO randomization test                  : 20 quality bits (guessed)
Stack randomization test (SEGMEXEC)      : Skipped, not applicable
Stack randomization test (PAGEEXEC)      : 30 quality bits (guessed)
Arg/env randomization test (SEGMEXEC)    : Skipped, not applicable
Arg/env randomization test (PAGEEXEC)    : 22 quality bits (guessed)
Offset to library randomisation (ET_EXEC): 28 quality bits (guessed)
Offset to library randomisation (ET_DYN) : 28 quality bits (guessed)
Randomization under memory exhaustion @~0: 29 bits (guessed)
Randomization under memory exhaustion @0 : 29 bits (guessed)
Return to function (strcpy)              : paxtest: return address contains a NULL byte.
Return to function (memcpy)              : Vulnerable
Return to function (strcpy, PIE)         : paxtest: return address contains a NULL byte.
Return to function (memcpy, PIE)         : Vulnerable

grsecurity

PaXtest - Copyright(c) 2003-2016 by Peter Busser <peter@adamantix.org> and Brad Spengler <spender@grsecurity.net>
Released under the GNU Public Licence version 2 or later

Mode: 1
Blackhat
Kernel: 
Linux nuc 6.6.18-grsec-privkstack+dbg+ #36 SMP PREEMPT_DYNAMIC Wed Feb 28 14:37:51 CET 2024 x86_64 GNU/Linux

Relase information: 
Distributor ID: Debian
Description:    Debian GNU/Linux trixie/sid
Release:    n/a
Codename:   trixie
Test results:
Executable anonymous mapping             : Killed
Executable bss                           : Killed
Executable data                          : Killed
Executable heap                          : Killed
Executable stack                         : Killed
Executable shared library bss            : Killed
Executable shared library data           : Killed
Executable anonymous mapping (mprotect)  : Killed
Executable bss (mprotect)                : Killed
Executable data (mprotect)               : Killed
Executable heap (mprotect)               : Killed
Executable stack (mprotect)              : Killed
Executable shared library bss (mprotect) : Killed
Executable shared library data (mprotect): Killed
Writable text segments                   : Killed
Anonymous mapping randomization test     : 33 quality bits (guessed)
Anonymous huge mapping randomization test: 33 quality bits (guessed)
Heap randomization test (ET_EXEC)        : 22 quality bits (guessed)
Heap randomization test (PIE)            : 40 quality bits (guessed)
Main executable randomization (ET_EXEC)  : No randomization
Main executable randomization (PIE)      : 32 quality bits (guessed)
Big main executable randomization (PIE)  : 32 quality bits (guessed)
File mapping randomization test          : 33 quality bits (guessed)
File huge mapping randomization test     : 33 quality bits (guessed)
Shared library randomization test        : 33 quality bits (guessed)
Big shared library randomization test    : 24 quality bits (guessed)
VDSO randomization test                  : 33 quality bits (guessed)
Stack randomization test (SEGMEXEC)      : Skipped, not applicable
Stack randomization test (PAGEEXEC)      : 40 quality bits (guessed)
Arg/env randomization test (SEGMEXEC)    : Skipped, not applicable
Arg/env randomization test (PAGEEXEC)    : 44 quality bits (guessed)
Offset to library randomisation (ET_EXEC): 33 quality bits (guessed)
Offset to library randomisation (ET_DYN) : 32 quality bits (guessed)
Randomization under memory exhaustion @~0: 33 bits (guessed)
Randomization under memory exhaustion @0 : 33 bits (guessed)
Return to function (strcpy)              : paxtest: return address contains a NULL byte.
Return to function (memcpy)              : Vulnerable
Return to function (strcpy, PIE)         : paxtest: return address contains a NULL byte.
Return to function (memcpy, PIE)         : Vulnerable

64-bit Compat Tests

Next are the results of running 32-bit (compat) tests on the very same 64-bit kernels.

Debian

PaXtest - Copyright(c) 2003-2016 by Peter Busser <peter@adamantix.org> and Brad Spengler <spender@grsecurity.net>
Released under the GNU Public Licence version 2 or later

Mode: 1
Blackhat
Kernel: 
Linux nuc 6.6.15-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.6.15-2 (2024-02-04) x86_64 GNU/Linux

Relase information: 
Distributor ID: Debian
Description:    Debian GNU/Linux trixie/sid
Release:    n/a
Codename:   trixie
Test results:
Executable anonymous mapping             : Killed
Executable bss                           : Killed
Executable data                          : Killed
Executable heap                          : Killed
Executable stack                         : Killed
Executable shared library bss            : Killed
Executable shared library data           : Killed
Executable anonymous mapping (mprotect)  : Vulnerable
Executable bss (mprotect)                : Vulnerable
Executable data (mprotect)               : Vulnerable
Executable heap (mprotect)               : Vulnerable
Executable stack (mprotect)              : Vulnerable
Executable shared library bss (mprotect) : Vulnerable
Executable shared library data (mprotect): Vulnerable
Writable text segments                   : Vulnerable
Anonymous mapping randomization test     : 8 quality bits (guessed)
Anonymous huge mapping randomization test: No randomization
Heap randomization test (ET_EXEC)        : 13 quality bits (guessed)
Heap randomization test (PIE)            : 13 quality bits (guessed)
Main executable randomization (ET_EXEC)  : No randomization
Main executable randomization (PIE)      : 8 quality bits (guessed)
Big main executable randomization (PIE)  : 0 quality bits (guessed)
File mapping randomization test          : 8 quality bits (guessed)
File huge mapping randomization test     : No randomization
Shared library randomization test        : No randomization
Big shared library randomization test    : No randomization
VDSO randomization test                  : 8 quality bits (guessed)
Stack randomization test (SEGMEXEC)      : 19 quality bits (guessed)
Stack randomization test (PAGEEXEC)      : 19 quality bits (guessed)
Arg/env randomization test (SEGMEXEC)    : 11 quality bits (guessed)
Arg/env randomization test (PAGEEXEC)    : 11 quality bits (guessed)
Offset to library randomisation (ET_EXEC): No randomization
Offset to library randomisation (ET_DYN) : 8 quality bits (guessed)
Randomization under memory exhaustion @~0: No randomization
Randomization under memory exhaustion @0 : No randomization
Return to function (strcpy)              : Vulnerable
Return to function (memcpy)              : Vulnerable
Return to function (strcpy, PIE)         : Vulnerable
Return to function (memcpy, PIE)         : Vulnerable

grsecurity

PaXtest - Copyright(c) 2003-2016 by Peter Busser <peter@adamantix.org> and Brad Spengler <spender@grsecurity.net>
Released under the GNU Public Licence version 2 or later

Mode: 1
Blackhat
Kernel: 
Linux nuc 6.6.18-grsec-privkstack+dbg+ #36 SMP PREEMPT_DYNAMIC Wed Feb 28 14:37:51 CET 2024 x86_64 GNU/Linux

Relase information: 
Distributor ID: Debian
Description:    Debian GNU/Linux trixie/sid
Release:    n/a
Codename:   trixie
Test results:
Executable anonymous mapping             : Killed
Executable bss                           : Killed
Executable data                          : Killed
Executable heap                          : Killed
Executable stack                         : Killed
Executable shared library bss            : Killed
Executable shared library data           : Killed
Executable anonymous mapping (mprotect)  : Killed
Executable bss (mprotect)                : Killed
Executable data (mprotect)               : Killed
Executable heap (mprotect)               : Killed
Executable stack (mprotect)              : Killed
Executable shared library bss (mprotect) : Killed
Executable shared library data (mprotect): Killed
Writable text segments                   : Killed
Anonymous mapping randomization test     : 16 quality bits (guessed)
Anonymous huge mapping randomization test: 16 quality bits (guessed)
Heap randomization test (ET_EXEC)        : 22 quality bits (guessed)
Heap randomization test (PIE)            : 24 quality bits (guessed)
Main executable randomization (ET_EXEC)  : No randomization
Main executable randomization (PIE)      : 16 quality bits (guessed)
Big main executable randomization (PIE)  : 16 quality bits (guessed)
File mapping randomization test          : 16 quality bits (guessed)
File huge mapping randomization test     : 16 quality bits (guessed)
Shared library randomization test        : 16 quality bits (guessed)
Big shared library randomization test    : 7 quality bits (guessed)
VDSO randomization test                  : 16 quality bits (guessed)
Stack randomization test (SEGMEXEC)      : 24 quality bits (guessed)
Stack randomization test (PAGEEXEC)      : 24 quality bits (guessed)
Arg/env randomization test (SEGMEXEC)    : 28 quality bits (guessed)
Arg/env randomization test (PAGEEXEC)    : 28 quality bits (guessed)
Offset to library randomisation (ET_EXEC): 18 quality bits (guessed)
Offset to library randomisation (ET_DYN) : 17 quality bits (guessed)
Randomization under memory exhaustion @~0: 17 bits (guessed)
Randomization under memory exhaustion @0 : 9 bits (guessed)
Return to function (strcpy)              : Vulnerable
Return to function (memcpy)              : Vulnerable
Return to function (strcpy, PIE)         : Vulnerable
Return to function (memcpy, PIE)         : Vulnerable

Digesting the Data

The above raw logs might be too verbose to notice the subtle differences.

Ignoring the “Executable” tests and focusing instead on the randomization ones, we get the following numbers for 64-bit native tests:

Test (results are bits of entropy) Debian grsecurity Delta
Anonymous mapping randomization 28 33 +5
Anonymous huge mapping randomization 28 33 +5
Heap randomization (ET_EXEC) 13 22 +9
Heap randomization (PIE) 28 40 +12
Main executable randomization (ET_EXEC) - - -
Main executable randomization (PIE) 28 32 +4
Big main executable randomization (PIE) 19 32 +13
File mapping randomization 28 33 +4
File huge mapping randomization 19 33 +14
Shared library randomization 28 33 +5
Big shared library randomization 19 24 +5
VDSO randomization 20 33 +13
Stack randomization (SEGMEXEC) n/a n/a -
Stack randomization (PAGEEXEC) 30 40 +10
Arg/env randomization (SEGMEXEC) n/a n/a -
Arg/env randomization (PAGEEXEC) 22 44 +22
Offset to library randomisation (ET_EXEC) 28 33 +5
Offset to library randomisation (ET_DYN) 28 32 +4
Randomization under memory exhaustion @~0 1 n/a n/a -
Randomization under memory exhaustion @0 1 n/a n/a -

Grsecurity’s ASLR does better across the board. 🦾

Here are the numbers for 32-bit compat tests run on a 64-bit kernel which are even more impressive:

Test (results are bits of entropy) Debian grsecurity delta
Anonymous mapping randomization 8 16 +8
Anonymous huge mapping randomization - 16 +16
Heap randomization (ET_EXEC) 13 22 +9
Heap randomization (PIE) 13 24 +11
Main executable randomization (ET_EXEC) - - -
Main executable randomization (PIE) 8 16 +8
Big main executable randomization (PIE) 0 16 +16
File mapping randomization 8 16 +8
File huge mapping randomization - 16 +16
Shared library randomization - 16 +16
Big shared library randomization - 7 +7
VDSO randomization 8 16 +8
Stack randomization (SEGMEXEC)2 n/a n/a -
Stack randomization (PAGEEXEC) 19 24 +5
Arg/env randomization (SEGMEXEC)2 n/a n/a -
Arg/env randomization (PAGEEXEC) 11 28 +17
Offset to library randomisation (ET_EXEC) - 18 +18
Offset to library randomisation (ET_DYN) 8 17 +9
Randomization under memory exhaustion @~0 - 17 +17
Randomization under memory exhaustion @0 - 9 +9

1 For native 64-bit binaries, the randomization under memory exhaustion tests aren't performed and are marked as not applicable accordingly.

2 Even though the SEGMEXEC tests provided results, they’re not applicable as we dropped support for SEGMEXEC a few releases ago. Users were advised to switch to PAGEEXEC instead, as systems lacking native NX support tend to be rare these days.

The new huge-page randomization tests nicely show what was discovered earlier this year, that randomization of big shared libraries reduces ASLR by a fair chunk—up to the point where no more randomization happens for 32 bit binaries. Under grsecurity, OTOH, even in the worst-case scenario where the runtime linker enforces the huge-page alignment itself, 7 bits of randomness can still be achieved.

It's worth a short final note that while the amount of randomization for grsecurity's ASLR is higher across the board, this isn't merely due to different changeable settings but due to differences in implementation. Further, grsecurity doesn't rely solely upon the extra bits of randomness (as this can be rather quickly bruteforced on a local system against suid root binaries for example), but has always provided anti-bruteforcing capabilities to thwart such attempts.

Availability

We made the release (and all previous PaXtest releases) available via GitHub as well as on our website.

Teaser

If you paid very close attention to this blog, you may have noticed a few things that haven't been explained anywhere yet. Stay tuned: on Monday, we'll dig to the root of the problem and release all the gory details.