This is an archive of the discontinued LLVM Phabricator instance.

[scudo] Manage pages with MemMap in SizeClassAllocator64
ClosedPublic

Authored by Chia-hungDuan on Mar 13 2023, 10:30 PM.

Details

Summary

Introduce a new data structure to manage the allocated pages from the
system. This is meant to deprecate certain memory system call wrappers
in Scudo, e.g., map()/unmap(). Besides, we would like to make
MapPlatformData to be appeared in platform specific data structure only.
Given that there are several allocators in Scudo and each of them has
different way of page management. The deprecation will be done in
several CLs. In this commit, we start from SizeClassAllocator64.

Diff Detail

Event Timeline

Chia-hungDuan created this revision.Mar 13 2023, 10:30 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 13 2023, 10:30 PM
Chia-hungDuan requested review of this revision.Mar 13 2023, 10:30 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 13 2023, 10:30 PM
Herald added a subscriber: Restricted Project. · View Herald Transcript

So far I call it RFC but it's a formal CL as well. There are still many places we can simplify. In order to make the review process easier, I'll do the simplification in coming CLs.

Chia-hungDuan added inline comments.
compiler-rt/lib/scudo/standalone/allocator_config.h
104

As @fabio-d suggested, I'll move this out from the config. So that we can still easily mix different configs on different platforms

Don't define MapReserver in allocator config

Chia-hungDuan added inline comments.Mar 14 2023, 11:46 AM
compiler-rt/lib/scudo/standalone/release.h
50

Note:This will be deprecated as well.

Make getCapacity as required and add few more comments

Chia-hungDuan updated this revision to Diff 505268.EditedMar 14 2023, 2:44 PM

Add unmap() and remove the use of MapPlatformData in primary64

Chia-hungDuan retitled this revision from [scudo] Manage pages with MemMap in SizeClassAllocator64 [RFC] to [scudo] Manage pages with MemMap in SizeClassAllocator64.Mar 14 2023, 2:44 PM

Remove the defining of MapReserver in the tests

Chia-hungDuan added inline comments.Mar 15 2023, 11:34 AM
compiler-rt/lib/scudo/standalone/mem_map.h
76

Per discussion, this may not be a good name. It may lead some confusion. I'm considering to make it either ReservedMemory or change it to has-a relation.

  1. Rename MapReserver to ReservedMap.
  2. Add more comments on why ReservedMap still derives from MemMapBase
  3. Leave the unused functions in ReservedMap unimplemented (which were having default implementations which does nothing)
Chia-hungDuan added inline comments.Mar 15 2023, 5:09 PM
compiler-rt/lib/scudo/standalone/mem_map.h
81

Will be rephrased as

`ReservedMap` is a special MemMap which still manages a contiguous pages
Chia-hungDuan added inline comments.Mar 16 2023, 1:17 PM
compiler-rt/lib/scudo/standalone/mem_map.h
68

We may consider having two versions of this operation,

  1. releaseAndZeroPagesToOS will ensure the later accessing the released pages will get it zero-filled
  2. releasePagesToOS is optional to zero-fill

The reason for this is these two operations have performance difference on some platforms and in Scudo, not all releasePagesToOS calls reply on the zero-fill property.

Will update the CL with this change

Add releaseAndZeroPagesToOS and add more DCHECK in the base classes

Move some stuff to mem_map_base.h so that it will make the use of platform MemMap easier.

Also add more comments and checks

Slightly improve the semantic of DefaultMemMap::remap()

cferris added inline comments.Mar 21 2023, 8:01 PM
compiler-rt/lib/scudo/standalone/mem_map.cpp
42

Should you verify that Addr is within the actual map?

47

Similar to above, should you verify that this is within the actual map?

compiler-rt/lib/scudo/standalone/mem_map.h
58

This is slightly confusing. Perhaps reworded something like:

The following functions correspond to the Impl functions from MemMapBase.

compiler-rt/lib/scudo/standalone/mem_map_base.h
18 ↗(On Diff #507135)

this

19–20 ↗(On Diff #507135)

Do you mean before the operation is executed? The wording is a bit confusing.

22 ↗(On Diff #507135)

A class derived from

22 ↗(On Diff #507135)

handle contiguous pages

23 ↗(On Diff #507135)

derived class uses.

28–29 ↗(On Diff #507135)

it has to implement all of the 'Impl' named functions.

34 ↗(On Diff #507135)

set of pages

35 ↗(On Diff #507135)

any

58 ↗(On Diff #507135)

set of physical

85 ↗(On Diff #507135)

a set of contiguous pages

86 ↗(On Diff #507135)

a set of contiguous pages

97 ↗(On Diff #507135)

functions are not used and should not be defined in

98 ↗(On Diff #507135)

following

99 ↗(On Diff #507135)

that a derived class has to implement.

Chia-hungDuan marked 16 inline comments as done.

Address review comment

compiler-rt/lib/scudo/standalone/mem_map.cpp
42

The basic invariant of the API is verified in the base class (mem_map_base.h), For example,

bool remap(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) {              
  DCHECK(isAllocated());                                                          
  DCHECK((Addr >= getMapBase()) ||                                                
         (Addr + Size <= getMapBase() + getMapCapacity()));                       
  return static_cast<Derived *>(this)->remapImpl(Addr, Size, Name, Flags);        
}
47

Same as above.

compiler-rt/lib/scudo/standalone/mem_map_base.h
19–20 ↗(On Diff #507135)

Yes, reworded a little bit.

86 ↗(On Diff #507135)

The suggestion is a little bit different from above. I follow the suggestion here (suppose the above didn't mark the right range to update)

Chia-hungDuan added inline comments.Mar 22 2023, 3:17 PM
compiler-rt/lib/scudo/standalone/mem_map_base.h
45 ↗(On Diff #507215)

I'm wrapping this ugly cast as Fabio suggested

wrap static_cast<Derived *>(this)->...Impl(...)

Rename ReservedMap to ReservedMemory and untangle it with MemMap

Change the names of some APIs

Hi @Chia-hungDuan, thank you for bringing this new design forward!

I've left some notes on specific APIs, and I also have some other general suggestions:

  • It would be useful to document what flags are acceptable in a comment next to each method.
  • As you know, Fuchsia doesn't accept addresses and sizes that are not page-aligned, so I suggest to add page-alignment checks to all the arguments.
  • I am slightly confused by the fact that some arguments are called Size but the corresponding property is called Capacity. Is there any specific reason why you didn't use Size everywhere?
compiler-rt/lib/scudo/standalone/mem_map.h
35–37

Since these methods are only called by the wrappers in MemMapBase and they always pass a Flags value, I suggest to remove the default values here

60–61

Same suggestion as above on the Flags.

compiler-rt/lib/scudo/standalone/mem_map_base.h
22 ↗(On Diff #507936)

I assume that the contract is that the ctor creates an object such that !isAllocated(). Should we also have a dtor that automatically unmaps if still isAllocated()?

24–25 ↗(On Diff #507936)

If Addr is only a suggestion, I suggest to remove it completely from this API. For carefully-placed memory ranges, clients will use ReservedMemory and dispatch

26–27 ↗(On Diff #507936)

Are you referring to Trusty? I would move this comment to unmap

29 ↗(On Diff #507936)

Should this also check that !isAllocated() as a precondition?

34 ↗(On Diff #507936)

I think we do not need Flags for unmap in this new design. Is there any specific flag that you have in mind? From a quick look, it seems to me that we can safely get rid of the UNMAP_ALL flag if we add explicit alignment control to cover primary32.h's special case.

Also, Fuchsia does not have a syscall to shrink a VMO from the beginning. Would it be a problem if we only allowed to shrink from the end?

40–42 ↗(On Diff #507936)

I would separate resizing and changing permissions into different methods (even on Linux, mremap and mprotect are separate syscalls).
Also, I'm not sure we actually need Addr at all:

  • If this is being called with the intent of setting new permissions, I would assume that the upper layers would always want to apply them to the entire range covered by the MemMap. Unless we already have use case for this level flexibility, I would suggest to leave it out of the new API and we cann add it back later as needed.
  • If this is being called with the intent of resizing, I also would assume that it applies to the entire range too (in fact, I cannot think of a good formal definition of "resizing a subrange", especially if the resized range ends before the end of the MemMap).
49–51 ↗(On Diff #507936)

What do "physical" and "virtual" mean in this context? (maybe this a stale comment from the previous revision of this CL)

59 ↗(On Diff #507936)

The distinction between releasePagesToOS and releaseAndZeroPagesToOS does not currently exist in Scudo, right? Can we add it as a new feature in a later CL?

86 ↗(On Diff #507936)

Similar to above, I assume that the ctor creates an object such that !isCreated(). Should we also have a dtor that automatically releases if still isCreated()?

91 ↗(On Diff #507936)

Should this also check that !isCreated() as a precondition?

95 ↗(On Diff #507936)

Similarly, I think we do not need Flags for release either.

Chia-hungDuan marked 5 inline comments as done.

Address review comment

compiler-rt/lib/scudo/standalone/mem_map_base.h
22 ↗(On Diff #507936)

These two are supposed to be lazy initialized so the creation needs to be done explicitly.

The order of destructing allocator fields is tricky. Especially we have the thread local cache. Given that tearing down the allocator only happens at program exit, we just make them being released naturally. Therefore, the creation/deletion are done explicitly.

We have been thinking to make it more elegantly but that depends on too many stuff (e.g., libc). Besides, this CL is supposed to do the abstraction transition only, I prefer to do further logic changes in the later CLs.

24–25 ↗(On Diff #507936)

I prefer to keep this suggestion because it can still benefit some contexts (like some cache management stuff we will have in the future). It's a different case than ReservedMemory supposed to cover.

34 ↗(On Diff #507936)

Like the release one, the only case I was thinking is the UNMAP_ALL for Fuchsia, if you think it's fine to remove it. I'm happy to do that :)

is there any alternative way to achieve this? Like create a new VMO ?

40–42 ↗(On Diff #507936)

The MemMap is a handle of range of virtual address and it's supposed to do the supported operations in the range. So it can remap part of the virtual address. For example, the RegionBeg is unlikely to be equal to Region base address. That's why we have this Addr argument here (For future function it'll be useful as well). If a platform doesn't allow this, then we just do the valid operation in that platform to fulfill their constraint.

BTW, we have setMemoryPermission (which is added in the follow up CL)

49–51 ↗(On Diff #507936)

It means virtual memory and physical memory.

59 ↗(On Diff #507936)

In general, yes, we may consider adding this in a separate CL.

The reason I do it here is that we are revising API contract and try to reduce ambiguity. For example, the arguments of releasePagesToOS was confusing. And then we found that the zero-page stuff is ambiguous as well. That's why I bring it in this CL.

86 ↗(On Diff #507936)

Same as above.

91 ↗(On Diff #507936)

This is intended to make it implementation defined behavior. In some case we may want few reserved spaces.

95 ↗(On Diff #507936)

I was hesitating this before and the only case is UNMAP_ALL used by Fuchsia. If you agree that we can get rid of it. I'm happy with that :) Done.

Chia-hungDuan added inline comments.Mar 24 2023, 12:13 PM
compiler-rt/lib/scudo/standalone/mem_map_base.h
91 ↗(On Diff #507936)

Oops, please ignore this wrong reply.

cferris accepted this revision.Apr 3 2023, 3:29 PM

LGTM.

This revision is now accepted and ready to land.Apr 3 2023, 3:29 PM

Make ReservedMemory::create return boolean to indicate if the creation succeeds.
This makes the page allocation have the consistent style of API

cferris accepted this revision.Apr 4 2023, 2:52 PM

Still looks good.

fabio-d reopened this revision.Apr 7 2023, 6:49 AM
fabio-d added inline comments.
compiler-rt/lib/scudo/standalone/primary64.h
741–753

This used to be a grow operation for a memory region that always started at RegionBeg. Now we are dispatching at getRegionBaseByClassId(ClassId) and then resizing starting from RegionBeg. Can we directly dispatch at RegionBeg instead?
This results in MemMapDefault::releaseAndZeroPagesToOSImpl passing an offset relative to getRegionBaseByClassId(ClassId) instead of RegionBeg to the ::releasePagesToOS function, which makes Scudo release in-use pages on Fuchsia.

This revision is now accepted and ready to land.Apr 7 2023, 6:49 AM
Chia-hungDuan added inline comments.Apr 7 2023, 9:24 AM
compiler-rt/lib/scudo/standalone/primary64.h
741–753

Because it's a region's base, I tended to think it's still part of a region. To make it work before transition to each platform specific implementation. I agree that we can dispatch the reserved memory from RegionBeg and fix it back later

fabio-d closed this revision.Apr 20 2023, 5:12 AM

Closing because the issue on Fuchsia was fixed in D147792