This is an archive of the discontinued LLVM Phabricator instance.

[scudo] Manage pages with MemMap in Secondary Allocator
ClosedPublic

Authored by Chia-hungDuan on Mar 20 2023, 1:39 PM.

Details

Summary

Replace the uses of raw map()/unmap(), .etc calls with MemMap. Also
remove the direct use of MapPlatformData in the secondary allocator.

Also add setMemoryPermission() in MemMap.

Diff Detail

Event Timeline

Chia-hungDuan created this revision.Mar 20 2023, 1:39 PM
Chia-hungDuan requested review of this revision.Mar 20 2023, 1:39 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 20 2023, 1:39 PM
Herald added a subscriber: Restricted Project. · View Herald Transcript
cryptoad accepted this revision.Mar 20 2023, 2:39 PM
This revision is now accepted and ready to land.Mar 20 2023, 2:39 PM
cferris requested changes to this revision.Apr 4 2023, 6:04 PM
cferris added inline comments.
compiler-rt/lib/scudo/standalone/secondary.h
345

Do you expect that this struct will ever have other members in the near future?

If not, it might be better to dump the struct and create an array like MemMaps[] instead.

This revision now requires changes to proceed.Apr 4 2023, 6:04 PM
Chia-hungDuan marked an inline comment as done.

Address review comment

compiler-rt/lib/scudo/standalone/secondary.h
345

Nice catch!

cferris accepted this revision.Apr 5 2023, 1:34 PM

LGTM

This revision is now accepted and ready to land.Apr 5 2023, 1:34 PM
fabio-d reopened this revision.Apr 11 2023, 11:36 AM
fabio-d added a subscriber: fabio-d.
fabio-d added inline comments.
compiler-rt/lib/scudo/standalone/secondary.h
114

Like in the other CL, this breaks releasePagesToOS on Fuchsia. I think it's the usual bad offset due to the fact that MemMap was created like this

MemMapT MemMap = ReservedMemory.dispatch(ReservedMemory.getBase(),
                                         ReservedMemory.getCapacity());

but ReservedMemory.getBase() != CommitBase

This revision is now accepted and ready to land.Apr 11 2023, 11:36 AM
Chia-hungDuan added inline comments.Apr 11 2023, 6:12 PM
compiler-rt/lib/scudo/standalone/secondary.h
114

Sorry, I'm not following. The logic here is supposed to be the same,

Before:

const uptr MapSize = RoundedSize + 2 * PageSize;
uptr MapBase = reinterpret_cast<uptr>(
      map(nullptr, MapSize, nullptr, MAP_NOACCESS | MAP_ALLOWNOMEM, &Data));
...
mapSecondary<Config>(Options, CommitBase, CommitSize, AllocPos, 0, &Data);

After

ReservedMemoryT ReservedMemory;
const uptr MapSize = RoundedSize + 2 * PageSize;
ReservedMemory.create(/*Addr=*/0U, MapSize, nullptr, MAP_ALLOWNOMEM);

  // Take the entire ownership of reserved region.
MemMapT MemMap = ReservedMemory.dispatch(ReservedMemory.getBase(), ReservedMemory.getCapacity());
...
mapSecondary<Config>(Options, CommitBase, CommitSize, AllocPos, 0, MemMap);

The arguments of mapSecondary<Config> are the same as before (except the MapPlatformData) and the call to map() is still the same. I.e., it's CommitBase, not MapBase(which is ReservedMemory.getBase()) since before.

Besides, I think Fuchsia doesn't enable cache in secondary allocator so it's supposed not to do releasePagesToOS in secondary allocator. Can you share more details about why it didn't cause problem before?

fabio-d added inline comments.Apr 12 2023, 3:03 AM
compiler-rt/lib/scudo/standalone/secondary.h
114

Sure! What used to happen was that a VMAR was created which was slightly larger than the actual VMO, to leave room for guard pages (at the beginning and at then end) and skipping the misaligned portion.

Now it's still the case. However, since the MemMap is now initialized initialized to cover the whole VMAR range, it thinks that the VMO is mapped at at ReservedMemory.getBase() while in fact it starts at CommitBase:

MemMapT MemMap = ReservedMemory.dispatch(ReservedMemory.getBase(), // this ends up being stored in its Base field
                                         ReservedMemory.getCapacity());
...
mapSecondary<Config>(Options, CommitBase, CommitSize, AllocPos, 0, MemMap);

but in mapSecondary:

MemMap.remap(CommitBase, CommitSize, "scudo:secondary", RemapFlags); // the VMO is created and mapped now at CommitBase

This leaves a hole at the beginning of the MemMap which is causing the same issue as the other CL. In particular, when the Secondary allocator decides it's time to decommit the whole [CommitBase, CommitBase + CommitSize] range, it will call:

// Base = ReservedMemory.getBase()
// From = CommitBase
// Size = CommitSize
void MemMapDefault::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {
  return ::scudo::releasePagesToOS(Base, From - Base, Size, &Data); // From - Base should have been zero, but it isn't
}

As for the question of why this matters on Fuchsia at all, given that it doesn't enable the secondary allocator cache in its config, the answer is that some tests exercise other configs too. In this case, for instance, I'm seeing this issue manifesting as a ScudoCombinedDeathTestBasicCombined1_AndroidSvelteConfig failure.

Chia-hungDuan added inline comments.Apr 12 2023, 9:24 AM
compiler-rt/lib/scudo/standalone/secondary.h
114

So the problem is not the mapping, it's the releasePagesToOS instead. The offset should be the offset from MappedBase Given that MemMapDefault is a temporary bridge before the transition complete. I would suggest two ways for this,

  1. Apply D148141 (and/or revert D147792 because the problem is not in mapping)
  2. Temporarily disable the AndroidSvelteConfig in combined_test.cpp. (Given that AndroidSvelte is being deprecated)

Sorry for the breakage.

Chia-hungDuan added inline comments.Apr 12 2023, 11:11 AM
compiler-rt/lib/scudo/standalone/secondary.h
114

After thinking this a little bit, let me take the solution 2 back. It'll be better to keep everything pass with MmeMapDefault at this moment.

So let's go for 1. and 2. can be an option after it.

Chia-hungDuan closed this revision.Aug 3 2023, 3:57 PM