In particular, we track separately whether each AtExitEntry has already
been called, through a separate Bitmap. This has several side-effects:
- We now call malloc() during __cxa_finalize(). I believe this is fine,
and at that point during program execution memory pressure should be
low anyway.
- An attacker could prevent arbitrary entries from executing by writing
to atexit_called_entries. However, this already was possible (by
setting atexit_entry_count to zero), and this path is even more
troublesome (the attacker needs to overwrite atexit_called_entries,
and a region serving as *atexit_called_entries.m_data, and magically
know exactly how many entries already exist.)
- This reduces the size of AtExitEntry from 16 to 12 (on i686). As such,
we can reduce the initial memory allocation from two to one page,
reducing the initial capacity from 512 to 341 entries (or 256 to 170,
on x86_64). It seems that most programs only use 36-47 entries anyway.
For 'true', this shaves off about 69 syscalls, as measured by strace.
In 553361d we started mprotecting the atexit handlers when they are not
being modified or executed. As part of that commit, we unintentionally
changed the max number of global destructors from 1024 to 256 (on x86,
only 128 on x86_64). This patch expands the initial size of the global
destructors page to 2 pages from 1, and allows the pool to be expanded
at runtime by mapping a new set of pages and copying the AtExitEntries
over.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.
See: https://spdx.dev/resources/use/#identifiers
This was done with the `ambr` search and replace tool.
ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
(...and ASSERT_NOT_REACHED => VERIFY_NOT_REACHED)
Since all of these checks are done in release builds as well,
let's rename them to VERIFY to prevent confusion, as everyone is
used to assertions being compiled out in release.
We can introduce a new ASSERT macro that is specifically for debug
checks, but I'm doing this wholesale conversion first since we've
accumulated thousands of these already, and it's not immediately
obvious which ones are suitable for ASSERT.
Remap the list of atexit handlers as read-only while we're not actively
writing to it. This prevents an attacker from using a memory write
primitive to gain code execution via the atexit list.
This is based on a technique used in OpenBSD. :^)