libaslrmalloc
is a LD_PRELOADed library which replaces malloc()
and other memory allocation functions from C library.
The main design goal is not performance or memory consumption but to increase address space
layout randomization (ASLR), hence the name.
This is achieved by not trying to keep the pages together, forcing the kernel to map
pages at random addresses and unmapping old memory immediately when possible.
The amount of ALSR depends on processor type, size of allocation and possible user specified alignment (posix_memalign()
).
Assuming a processor with 48 bits of virtual address space (47 is available to user applications) and no extra alignment restrictions:
Size | Lowest randomized bit | Total |
---|---|---|
16 | 4 | 43 |
32 | 5 | 42 |
64 | 6 | 41 |
128 | 7 | 40 |
256 | 8 | 39 |
512 | 9 | 38 |
1024 | 10 | 37 |
2048 | 11 | 36 |
4096+ | 12 | 35 |
In addition, if the allocation is between the above sizes, the extra space is used to randomize the start address (within alignment restrictions). For example, 1600 bytes fit in a slab of 2048 bytes. There's extra space of 2048 - 1600 = 448 bytes to randomize the start of the allocation, but alignment needs to be taken care of as well. For 16 bytes (default) alignment, 448 / 16 = 28 different random positions are possible, yielding fractional randomization of 4.8 bits.
libaslrmalloc
has also the following features:
- drains kernel random bits pool
- fragments memory layout, consuming more memory in kernel page tables
- trashes caches, slowing down the system
- also buggy
Reading the design document may present more complete view.
libaslrmalloc
is licensed with either LGPL 2.1 (or later) or BSD 3-clause licenses.
Directory LICENCES contains the license texts.
SPDX License Identifiers can be found in source files.
$ sudo apt-get install build-essential meson
$ meson setup builddir/
$ meson compile -C builddir/ -v
$ dpkg-buildpackage --no-sign
$ sudo dpkg -i ../libaslrmalloc1_1-1_amd64.deb
Set the environment variable LD_PRELOAD
to the path to libaslrmalloc
before starting the program.
Example: LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libaslrmalloc.so.1 gedit
To make this easier, libaslrmalloc
comes with a program called libaslrmallocrun
which sets this environment variable for you: libaslrmallocrun gedit
Alternatively you can add /usr/lib/x86_64-linux-gnu/libaslrmalloc.so.1
to /etc/ld.so.preload
.
This activates libaslrmalloc
for all programs on your system including SUID programs (for which LD_PRELOAD
is ignored).
Only programs in containers such as flatpaks will not use libaslrmalloc
.
Create a drop-in configuration and add
Environment=LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libaslrmalloc.so.1
Create a .local and add
env LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libaslrmalloc.so.1
Note also that you can not use LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libaslrmalloc.so.1 program
if program is a symlink to firejail created by firecfg.
libaslrmalloc
understands the following environment variables:
LIBASLRMALLOC_DEBUG
: Boolean: Enable debugging.LIBASLRMALLOC_FILL_JUNK
: Character: Can be used to change the fill character or to disable filling if set to an empty string.LIBASLRMALLOC_PASSTHROUGH
: Boolean: Forward function calls to the libc implementations.LIBASLRMALLOC_STATS
: Boolean: Enable statistics.LIBASLRMALLOC_STRICT_MALLOC0
: Boolean:malloc(0)
will returnNULL
.LIBASLRMALLOC_STRICT_POSIX_MEMALIGN_ERRNO
: Boolean:posix_memalign()
will restore the old errno in case of an error.
The value of boolean variables should be one of 1
, y
, yes
or
true
for enabling a feature, or 0
, n
, no
or false
to disable
it.
Environment variables aren't used if secure execution is required (e.g. SUID programs).
libaslrmalloc
automatically loads a profile named app.profile
using the name of the application.
The profiles are loaded from directories
/usr/lib/libaslrmalloc/profiles
(distro), /etc/libaslrmalloc/profiles
(local admin),
and if the program isn't setuid or setgid,
$XDG_CONFIG_HOME/libaslrmalloc/profiles (or $HOME/.config/libaslrmalloc/profiles).
If an application specific profile doesn't exist, 'default.profile' is
loaded instead from the directories, but dropping path component
'profiles': /usr/lib/libaslrmalloc/default.profile
and so forth.
The settings in the profile are same as with the environment variables but without the prefix and lowercase, for example:
debug=1
fill_junk=X
stats=y
strict_malloc0=yes
strict_posix_memalign_errno=true
or
passthrough=true
The example below disable kernel's ASLR to see the difference:
$ cat /proc/sys/kernel/randomize_va_space
0
Left: vi
uses glibc malloc()
, which typically uses heap only.
Right: libaslrmalloc
has randomized the memory allocations over the address space and there's no heap.
Show statistics on slab use in TSV format:
$ LIBASLRMALLOC_STATS=1 LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libaslrmalloc.so.1 gnome-system-monitor
Size Count
16 154
32 482
64 329
128 1245
256 2146
512 6486
1024 3178
2048 1234
4096 2423
NB: Algorithms are still under development.
Current implementation with hash tables and linked lists is O(N) for free()
for large number of allocations.
With tree structures it should be possible to get O(log N) performance without loss of ASLR.
- Performance is enough to run entire system with
/etc/ld.so.preload
forcinglibaslrmalloc
everywhere, with some patience. - System services seem to run completely fine. They aren't usually performance critical and they may use memory allocations very sparingly.
- For desktop apps, in some cases there's only a small noticeable (less than few seconds) delay on startup, but after that the programs run normally.
- With some programs there are also small pauses or slowness during execution.
- In the other extreme, start up of FreeOrion is unacceptably slow (like 15 minutes).
- Thunderbird doesn't start.
- Firefox uses an internal allocation library and can't benefit from the library.
- Chromium crashes.
- 0ad starts very fast, but crashes later.
- 32 bit applications like Steam are not supported (and Steam also uses Chromium).
GitHub pull requests should be used for enhancements and new features. Bugs and other feedback should be reported as GitHub issues.
Please follow the existing coding style.
When adding new code, new automated tests should be added to keep code test coverage high. It's OK not to test all kernel related issues and some assertions for internal errors can't be tested. Low memory tests could be added in the future to test further code paths.
TODO: probably SemVer, git tags
TODO: just open GitHub issues for now