-
Notifications
You must be signed in to change notification settings - Fork 112
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
StrictProvenance backend support, take 2022.05 #537
Conversation
2e4ab61
to
e87a1b3
Compare
FWIW, I also have a version of this sitting atop #535. |
@nwf-msr so I think this is good. But I want to understand the precise design and threat model
I believe these are the core design constraints. These force the expanding the SlabMetadata and stashing the original capability in it. Now there is an additional constraint that I am unsure if we want or need
Now, if we don't require meta-data to have reduced bounds, then we do not actually need to modify SmallBuddy or introduce the ProvenanceCaptureRange. The backend and meta data are all bounded to the original mmap range, and only the allocations going out to the user are bounded. So as a principle of least required priviledge, bounding seems good, but it is unclear to me if it actually provides any additional safety guarantees. For instance, bounding the What is the principle you are driving for? Only infrequently accessed pointers have a large capability? It seems the smallest change to get CHERI working is less than this PR? |
I think that's right.
Of the varieties of amplification we've tried, I think the "larger SlabMetadata" is the least bad we've come up with, but I'd not characterize that particular data structure decision as forced, exactly. (I keep hoping for a less-redundant encoding, but that's probably at odds with keeping the number of cache-lines loaded per
If metadata were confined to the (eventual) allocator compartment, that's quite plausible. Right now, though, metadata includes not only the snmalloc/src/snmalloc/mem/pool.h Line 133 in 9464556
However, since we envision the compartmentalized design, in which |
a44bab3
to
a1cea58
Compare
At the moment, malicious code can just grab the PageMap capability from the captable, so I don't think bounding the metadata actually buys us anything. Once we have per-library captables, domain transitions between libraries, and separate stacks, the fact that the allocator object is stored in TLS and can reach the unbounded metadata capabilities becomes concerning, but at that point we can solve it by sealing the capability in TLS, so I'm not sure if there's any point at which bounding the metadata capabilities actually buys us any security. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am happy with this in its current state. When this lands can we enable CHERI CI?
There's still some breakage in the Speaking of, I have the start of a CHERI-specific test I should also add to this PR. It'd also be good to check that none of this runs afoul of @rmn30's work with CHERI+MTE; ideally it should just be a bit of spelling changes to use this for his work, but if it turns out I've made something harder or impossible by mistake I wouldn't be hugely surprised. |
All looks good so far. I'll find out once I try a rebase / merge. |
Rebased and added two things to the test harness (the last two commits): a new bit in |
void arena_set(capptr::Arena<void> a) | ||
{ | ||
arena = a; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this exist, given that it updates a field that has the same visibility as the set method?
Edit: I see that it's necessary to match the interface shared with the Lax version. Can we have a concept that encapsulates this and some comments explaining why the method exists here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose the arena field could be private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be easier to review if the change from the |
I'm happy to split out any subset of the commits, sure, and I should perhaps have noted up front that this PR is structured to be reviewed commit-by-commit rather than all at once. |
void arena_set(capptr::Arena<void> a) | ||
{ | ||
arena = a; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose the arena field could be private?
Most ranges just deal with whatever kinds of ranges their parent deal with, but that might be Chunk- or (soon) Arena-bounded. This commit does not yet introduce nuance, but just sets the stage.
Do not hard-code FrontendSlabMetadata, but rather take it as a template argment. We're going to plumb other types through for StrictProvenance.
Expose a static CapPtr<T,B>::unsafe_from() and use that everywhere instead (though continue to allow implicit and explicit construction of CapPtr from nullptr).
Make these generic, with the SmallBuddyRange taking its cue from the parent Range, since we're about to change them anyway and might want to vary them again in the future.
This allows us to have a single Pipe-line of ranges where we can, nevertheless, jump over the small buddy allocator when making large allocations. This, in turn, will let us differentiate the types coming from the small end and the large "tap" on this Pipe-line.
Now that we've split the range Pipe-line externally, the small-buddy ranges should never be seeing large requests.
Update the backend concept so that metadata allocations are Arena-bounded.
Wrap the FrontendSlabMetadata with a struct that holds the Arena-bounded authority for Chunks that the Backend ships out to the Frontend or, for non-StrictProvenance architecture, encapsulates the sleight of hand that turns Chunk-bounded CapPtr-s to Arena-bounded ones.
These pieces of metadata (specifically, the Allocator structures) are never deallocated at the moment, so we need not consider how we might amplify these bounded pointers back to higher authority.
Bad timing on my part; sorry @rmn30! The changes since what you reviewed are mostly confined to bdb64a2 but, in full, are https://github.com/microsoft/snmalloc/compare/af2adf3..ed88792 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Here we go again. This gets easier every time, at least. Especially after all the recent backend refactoring, I would almost dare suggest it was straightforward. This PR should be reviewable commit-by-commit. It has a prefix of preparatory NFC commits that don't (well, aren't supposed to) change anything and then the rest that actually do something.
This series re-introduces
capptr::Arena
and friends and pushes that through the backend ranges. TheLargeBuddyRange
-s always deal withArena
-bounded pointers in both directions; their consumers -- thebackend
chunk_alloc
-ator and theSmallBuddyRange
-- know how to tighten those down toChunk
-bounded pointers. For chunks that are being handed out to the frontend as such, the backend stashes theArena
-bounded authority inside theSlabMetadata
using a wrapper type (for non-StrictProvenance
architectures, there is a similar wrapper type in the same place that provides the usual sleight of hand). For chunks used by theSmallBuddyRange
, it relies on aProvenanceCaptureRange
to hold theArena
-bounded authority within thePagemap
itself, rather like theLargeBuddyRange
, while communicating usingChunk
-bounded pointers with its child range(s).This is missing the increased care around zeroing out internal pointers present in #510, so for that reason, if no other, is probably not ready to merge.