Skip to content
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

DDC switching API #45

Open
ltratt opened this issue Oct 4, 2021 · 2 comments
Open

DDC switching API #45

ltratt opened this issue Oct 4, 2021 · 2 comments

Comments

@ltratt
Copy link
Collaborator

ltratt commented Oct 4, 2021

#44 is our first attempt to write a DDC-altering compartmentalisation scheme. We're trying to keep that PR simple, so it doesn't do everything we think we want. This issue is a quick attempt to sketch out a more powerful (and secure) API. This won't be complete, but it might help us think about where we want to go.

I think the basic C API for a data-only compartmentalisation scheme might look something like:

// Create a compartment with an initial capacity of `size` bytes.
void *__capability compartment_create(size_t size);
// Call function `f` in compartment `comp`, passing `param` to `f`
// and returning the capability returned by `f`. `f` will be passed
// a pointer to the range of memory on the heap covered by the DDC.
void *__capability compartment_call(
  void *__capability comp,
  void *__capability (*f)(void *heap, void *__capability), void *__capability param
);

where the compartment_* functions must (I think) be sentries that can access any compartment created by them. My assumption is that the compartments returned by compartment_malloc are sealed so that a caller can't do anything with them other than call compartment_call (and compartment_call can carefully validate the capability!). An interesting question is whether f has to be passed heap -- I guess it could recreate it from the DDC but that seems horrible. One will also need compartment_free and probably compartment_realloc: both seem easy enough not to need sketching out.

To use this API in a basic way, I would do something like:

void *__capability f(void *heap, void *__capability param) {
  // Do whatever with `heap` or `param`.
  return NULL;
}

void main() {
  void *__capability comp1 = compartment_malloc(100);
  void *__capability comp2 = compartment_malloc(100);
  compartment_call(comp1, f, NULL);
  compartment_call(comp2, f, NULL);
}

This would create two compartments and then call f in the context of comp1 and then call f in the context of comp2.

I can pass compartments around and call one compartment from another:

void *__capability f1(void *heap, void *__capability param) {
  // f is called in the context of `comp1`.
  compartment_call(param, f2, NULL);
  return NULL;
}

void *__capability f2(void *heap, void *__capability param) {
  // f2 is called in the context of `comp2`.
  return NULL;
}

void main() {
  void *__capability comp1 = compartment_malloc(100);
  void *__capability comp2 = compartment_malloc(100);
  compartment_call(comp1, f1, comp2);
}

In terms of the underlying memory layout, my assumption is that compartment_malloc allocate size+<something> bytes where something is enough to hold the new call stack that the compartment will need. There are some slightly tricky questions to think about like: where should the stack go (before or after the compartments main data?) and how does that affect things like resizing a compartment (should we actually pre-allocate huge virtual address ranges to a compartment in the expectation that it will grow?). At least at first I don't think we need to worry about those considerations and, hopefully, they mostly wouldn't leak out into our API either.

@jacobbramley
Copy link
Contributor

The first example & surrounding paragraphs look a little confused. Did some thing get renamed? I think I got the gist, though.

My first impression is that the proposed "compartment" is a compartmentalisation of data only (i.e. DDC not PCC). This is stated explicitly in the first line, but it's also why f has to be passed in. I don't think it can be derived from DDC. I think it's worth pointing out that with a very small change to make compartment_malloc return both a data region and a code region, you end up with something possibly more interesting:

void * __capability compartment_malloc(void * __capability entry, size_t data_size);

The result could be called with an invoke-like mechanism (e.g. Morello's lpdblr), and it scales to more complex uses: that entry function would be a sentry, but could be used as a constructor, or indeed a JavaScript function (returning multiple derived sentries) if multiple entry points are needed.

Also, how can I call from f1 under comp1 to f1 under comp2? Is that disallowed on purpose? If so, that's a valid design, but a restriction worth noting. It can be permitted by sealing an {f1, comp2} pair and passing that to comp2; this denies general access to the data region but allows the function to be called, for example.

@ltratt
Copy link
Collaborator Author

ltratt commented Oct 6, 2021

The first example & surrounding paragraphs look a little confused. Did some thing get renamed?

I think I just did things too quickly! Hopefully they look more like prototypes now!

My first impression is that the proposed "compartment" is a compartmentalisation of data only (i.e. DDC not PCC)

Yes, this is definitely true. I'm not sure that we need PCC compartmentalisation quite yet and, if we do, I'm not sure the best way to do it, though your suggestion seems like a reasonable one!

Also, how can I call from f1 under comp1 to f1 under comp2?

That depends entirely on whether you pass the right sealed capability to the other compartment: if you do, that other compartment can call another compartment. But, by default, I think it's simple (and most secure) to disallow one compartment from calling another. I think that's what you mean by "It can be permitted by sealing an {f1, comp2} pair and passing that to comp2"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants