Add a new configuration option QuarantineDisabled that allows all of the
quarantine code to be compiled out.
Add new tests that verify that the code is removed properly.
On Android, this saves ~4000 bytes for 32 bit and ~6000 bytes for 64
bit.
On Android, I used some microbenchmarks that do malloc/free in a loop
and for allocations in the primary, the performance is about the same
for both 32 bit and 64 bit. For secondary allocations, I saw ~8% speed
up on 32 bit and ~3% on 64 bit speed up which feels like it could just
be code size improvements.
Mark as many of the reportXX functions that take pointers const. This
avoid the need to use const_cast when calling these functions on an
already const pointer.
Fix reportHeaderCorruption calls where an argument was passed into an
append call that didn't use them.
Fixes bug where a device that supports tagged pointers doesn't use
the tagged pointer when computing the checksum.
Add tests to verify that double frees result in chunk state error
not corrupted header errors.
If called on an address that is actually not owned, the header tag might not
match. This would cause an MTE fault in Chunk::isValid.
Disable tag checks in isOwned().
Currently, only Android supports using a hard-code page size. Make this
a bit more generic so any platform that wants to can use this.
In addition, add a getPageSizeLogCached() function since this value is
used in release.h and can avoid keeping this value around in objects.
Finally, change some of the release.h page size multiplies to shifts
using the new page size log value.
https://github.com/llvm/llvm-project/pull/83493 slightly
changed the order of computation of block addresses and
pointers, causing the value of DefaultAlignedPtr to
include the MTE tag. Move this computation earlier so it
matches the old behavior.
This fixes a UBSan failure in Trusty:
secure os: UBSan: (overflow:-)
external/scudo/standalone/combined.h:1070:35
secure os: Details: unsigned integer overflow: 8988807738704 -
144124176883594576 cannot be represented in type 'uptr'
The code paths for mte enabled and disabled were interleaving and which
increases the difficulty of reading each path in both source level and
assembly level. In this change, we move the parts that they have
different logic into functions and minor refactors on the code
structure.
Only attempt to initialize the ring buffer when tracking is enabled.
Updated unit tests, and added a few new unit tests to verify the
RingBuffer is not initialized by default.
Verified that the two maps associated with the RingBuffer are not
created in processes by default.
Instead of explicitly disabling a feature by declaring the variable and
set it to false, this change supports the optional flags. I.e., you can
skip certain flags if you are not using it.
This optional feature supports both forms,
1. Value: A parameter for a feature. E.g., EnableRandomOffset
2. Type: A C++ type implementing a feature. E.g., ConditionVariableT
On the other hand, to access the flags will be through one of the
wrappers, BaseConfig/PrimaryConfig/SecondaryConfig/CacheConfig
(CacheConfig is embedded in SecondaryConfig). These wrappers have the
getters to access the value and the type. When adding a new feature, we
need to add it to `allocator_config.def` and mark the new variable with
either *_REQUIRED_* or *_OPTIONAL_* macro so that the accessor will be
generated properly.
In addition, also remove the need of `UseConditionVariable` to flip
on/off of condition variable. Now we only need to define the type of
condition variable.
8 was very low and it is likely that in real workloads we have more than
an average of 8 frames per stack given on Android we have 3 at the
bottom: __start_main, __libc_init, main, and three at the top: malloc,
scudo_malloc and Allocator::allocate. That leaves 2 frames for
application code, which is clearly unreasonable.
First commit of the stack is a clean reland, second is the fix.
There was a typo in the `static_assert` that meant we were asserting the
size of the pointer, not the struct.
Also changed `alignas` to be more intuitive, but that is NFC.
Ran builds in Android here: https://r.android.com/2954411
This makes the use of TSD be RAII style and avoid the exposing of the
type of TSDs.
Also move some thread safety analyses from static to runtime because of
its limitation. Even we mark some code path as NO_THREAD_SAFETY_ANALYSIS
but we still have the `assertLocked()` cover the correctness.
Scudo grabs all allocator locks in a pthread_atfork before the fork, and releases them after. This allows malloc to be used in a fork child of a multithreaded process, which is expressly forbidden by the standard, but very widely used. For example, Android's init uses std::string after fork when spawning services in android::init::EnterNamespaces and other places.
Any lock that is necessary to serve an allocator call must be handled this way. Otherwise there is a possibility that the lock is held during the call to fork, which results in it being held forever in the child process, and the next operation that needs it deadlocks.
free(nullptr) is guaranteed by ISO and POSIX to be a no-op, we should not pay for the overhead of maybeInit() in this case.
Additionally, Bionic calls free(nullptr) before the allocator settings are finalized.
Scudo should not run allocator initialization at that time. Doing so
causes various bad things to happen, like mapping primary regions with
the wrong PROT_MTE setting.
`realloc` may involve both allocation and deallocation. Given that the
reporting the events is not atomic and which may lead the hook user to a
false case that the double-use pattern happens, we always report the old
pointer is released and report the new allocation afterward (even it's
the same pointer).
This also fixes that we didn't report the new size when it doesn't need
to allocate a new space.
In this CL, we move the printing of allocator stats from primary.h to
combined.h. This will also dump the secondary stats and reduce the log
spam when an OOM happens
Also change the symbol `F` to `E` to indicate region pages exhausted. It
means the region can't map more pages for blocks but it may still have
free blocks to allocate. `F` may hint the failure of fatel error in the
region. Also update the related comments.
In getCache()/getQuarantineCache(), they return a reference to variable
guarded by a mutex. After #67776, thread-safey analysis checks if a
variable return by reference has the lock held. The ASSERT_CAPABILITY
only claims after calling that function, the lock will be held. But not
asserting that the lock is held *before* calling that function.
In the patch, we switch to use REQUIRES() and assertLocked() to mark the
code paths. Also remove the misused ASSERT_CAPABILITY.
Fixes#67795, #67796
We used to update the deallocated block with
atomic_compare_exchange_strong to ensure the concurrent double-free will
be detected. However, this operation incurs huge performance overhead
which takes over 50% execution time in deallocate(). Given that we
already have the checksum to guard the most double-free cases and other
block verifications in the primary allocator, use atomic-store instead.
To maintain the convention of Scudo names starting with "scudo:",
which is used by some tooling to categorize memory usage.
Reviewed By: Chia-hungDuan
Differential Revision: https://reviews.llvm.org/D157102
The RSS code is not very useful and can be replicated by using
ulimit. Remove it and remove the options associated with it.
Reviewed By: Chia-hungDuan
Differential Revision: https://reviews.llvm.org/D159155
Move the invocation of hooks from Scudo internal to wrapper_c.cpp and
wrapper_c_bionic.cpp respectively. Therefore, Scudo's core algorithm
doesnt need to worry about the reentrant of hooks and leave the caring
of reentrant to the hook users.
Reviewed By: hctim, cferris, chelfi
Differential Revision: https://reviews.llvm.org/D152188
Modify all places that use the Options structure to be a const
reference. The underlying structure is a u32 so making it a
reference doesn't really do anything. However, if the structure
changes in the future it already works and avoids future coders
wondering why a structure is being passed by value. This also
makes it clear that the Options should not be modified in those functions.
Reviewed By: Chia-hungDuan
Differential Revision: https://reviews.llvm.org/D156372
Scudo has zero-tagged headers between any two allocation that will catch
a linear buffer overflow of up to 16 bytes. OddEvenTags extends this
guarantee to one chunk of the given SizeClass at the cost of the reduced
entropy for all heap tags (i.e. lower chance to catch use-after-free and
large overflows).
Given that the first 16 bytes are already deterministic, I feel this is
a bad tradeoff.
Differential Revision: https://reviews.llvm.org/D152984
To define custom allocation, you only need to put the configuration in
custom_scudo_config.h and define two required aliases, then you will be
switched to the customized config and the tests will also run with your
configuration.
In this CL, we also have a minor refactor the structure of
configuration. Now the essential fields are put under the associated
hierarchy and which will make the defining new configuration easier.
Reviewed By: cferris
Differential Revision: https://reviews.llvm.org/D150481
The AllocatorRingBuffer is allocated dynamically when Allocator is
initialized. This patch adds a corresponding deinitialization call in
unmapTestOnly, to avoid running out of virtual memory if the tests are run
a large number of times on memory-constrained platforms.
Reviewed By: Chia-hungDuan
Differential Revision: https://reviews.llvm.org/D149266