Skip to content

[compiler-rt][fuchsia] Preallocate a vmar for sanitizer internals #75256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 63 additions & 14 deletions compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,55 @@ uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }

bool ErrorIsOOM(error_t err) { return err == ZX_ERR_NO_MEMORY; }

// For any sanitizer internal that needs to map something which can be unmapped
// later, first attempt to map to a pre-allocated VMAR. This helps reduce
// fragmentation from many small anonymous mmap calls. A good value for this
// VMAR size would be the total size of your typical sanitizer internal objects
// allocated in an "average" process lifetime. Examples of this include:
// FakeStack, LowLevelAllocator mappings, TwoLevelMap, InternalMmapVector,
// StackStore, CreateAsanThread, etc.
//
// This is roughly equal to the total sum of sanitizer internal mappings for a
// large test case.
constexpr size_t kSanitizerHeapVmarSize = 13ULL << 20;
static zx_handle_t gSanitizerHeapVmar = ZX_HANDLE_INVALID;

static zx_status_t GetSanitizerHeapVmar(zx_handle_t *vmar) {
zx_status_t status = ZX_OK;
if (gSanitizerHeapVmar == ZX_HANDLE_INVALID) {
CHECK_EQ(kSanitizerHeapVmarSize % GetPageSizeCached(), 0);
uintptr_t base;
status = _zx_vmar_allocate(
_zx_vmar_root_self(),
ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,
kSanitizerHeapVmarSize, &gSanitizerHeapVmar, &base);
}
*vmar = gSanitizerHeapVmar;
if (status == ZX_OK)
CHECK_NE(gSanitizerHeapVmar, ZX_HANDLE_INVALID);
return status;
}

static zx_status_t TryVmoMapSanitizerVmar(zx_vm_option_t options,
size_t vmar_offset, zx_handle_t vmo,
size_t size, uintptr_t *addr) {
zx_handle_t vmar;
zx_status_t status = GetSanitizerHeapVmar(&vmar);
if (status != ZX_OK)
return status;

status = _zx_vmar_map(gSanitizerHeapVmar, options, vmar_offset, vmo,
/*vmo_offset=*/0, size, addr);
if (status == ZX_ERR_NO_RESOURCES) {
// This means there's no space in the heap VMAR, so fallback to the root
// VMAR.
status = _zx_vmar_map(_zx_vmar_root_self(), options, vmar_offset, vmo,
/*vmo_offset=*/0, size, addr);
}

return status;
}

static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
bool raw_report, bool die_for_nomem) {
size = RoundUpTo(size, GetPageSize());
Expand All @@ -144,11 +193,9 @@ static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
internal_strlen(mem_type));

// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
uintptr_t addr;
status =
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
vmo, 0, size, &addr);
status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
/*vmar_offset=*/0, vmo, size, &addr);
_zx_handle_close(vmo);

if (status != ZX_OK) {
Expand Down Expand Up @@ -243,6 +290,12 @@ void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {

zx_status_t status =
_zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
if (status == ZX_ERR_INVALID_ARGS && target_vmar == gSanitizerHeapVmar) {
// If there wasn't any space in the heap vmar, the fallback was the root
// vmar.
status = _zx_vmar_unmap(_zx_vmar_root_self(),
reinterpret_cast<uintptr_t>(addr), size);
}
if (status != ZX_OK) {
Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
SanitizerToolName, size, size, addr);
Expand Down Expand Up @@ -308,32 +361,28 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
internal_strlen(mem_type));

// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?

// Map a larger size to get a chunk of address space big enough that
// it surely contains an aligned region of the requested size. Then
// overwrite the aligned middle portion with a mapping from the
// beginning of the VMO, and unmap the excess before and after.
size_t map_size = size + alignment;
uintptr_t addr;
status =
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
vmo, 0, map_size, &addr);
status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
/*vmar_offset=*/0, vmo, map_size, &addr);
if (status == ZX_OK) {
uintptr_t map_addr = addr;
uintptr_t map_end = map_addr + map_size;
addr = RoundUpTo(map_addr, alignment);
uintptr_t end = addr + size;
if (addr != map_addr) {
zx_info_vmar_t info;
status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
status = _zx_object_get_info(gSanitizerHeapVmar, ZX_INFO_VMAR, &info,
sizeof(info), NULL, NULL);
if (status == ZX_OK) {
uintptr_t new_addr;
status = _zx_vmar_map(
_zx_vmar_root_self(),
status = TryVmoMapSanitizerVmar(
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
addr - info.base, vmo, 0, size, &new_addr);
addr - info.base, vmo, size, &new_addr);
if (status == ZX_OK)
CHECK_EQ(new_addr, addr);
}
Expand All @@ -357,7 +406,7 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
}

void UnmapOrDie(void *addr, uptr size) {
UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
UnmapOrDieVmar(addr, size, gSanitizerHeapVmar);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to use the fallback logic in case the root vmar was used, as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dispatches to UnmapOrDieVmar above which contains that fallback logic. When this is called, it should try unmapping from gSanitizerHeapVmar first then try unmapping again in the root.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. The way I saw the diff that was hard to find, probably just my lack of familiarity with the GH code review UI.

}

void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
Expand Down