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

Fix handling of COW pages during revocation #1930

Merged
merged 3 commits into from
Nov 25, 2023

Conversation

markjdb
Copy link
Contributor

@markjdb markjdb commented Nov 22, 2023

No description provided.

pmap_caploadgen_update() previously checked PGA_WRITEABLE to decide
whether the caller may mutate the returned page.  However, this is not
the right check: PGA_WRITEABLE is merely advisory and when set only
means that there _may_ exist some writeable mapping of the page.  Use
the pmap's protections to tell upper layers what they should be doing.
In particular, we must not revoke from a copy-on-write page.
If during a scan we find an unmapped-but-resident page, use state from
the map entry to decide what to do.  In particular, PGA_WRITEABLE is an
advisory flag and shouldn't be used to determine whether a page may be
modified in place.
bin/cheribsdtest/cheribsdtest_vm.c Outdated Show resolved Hide resolved
bin/cheribsdtest/cheribsdtest_vm.c Outdated Show resolved Hide resolved
/*
* Quarantine the third page.
*/
CHERIBSDTEST_CHECK_SYSCALL(cheri_revoke_get_shadow(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't really use these macros in a child process, they don't print anything. Does cheribsdtest have some facility to make it easier to get errors out of a child? If not I'll just use different exit statuses to make it easier to see what went wrong.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, it shares that same misfeature with atf. Several existing tests do use these macros in a forked child, and probably don't work correctly if a system call fails in a child. It might be nice to have some *CHILD* variants that call exit directly with a non-zero error code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that'd be handy. For now I just did something low-tech.

Copy link
Member

Choose a reason for hiding this comment

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

I encountered that the other day too, though it’s even worse for the child func case as ccsp is null so you crash rather than “just” set state that’s clobbered

@markjdb
Copy link
Contributor Author

markjdb commented Nov 22, 2023

The riscv test failures are, I believe, the result of my test assuming particular behaviour of pmap_copy(), which on riscv is a no-op.

Comment on lines 2360 to 2361
* pmap. munmap() would have the same effect of course, but
* would also throw away the contents of the page.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I guess I don't fully understand how munmap() would be different here. Is that munmap() would alter the behavior of the vm_map_entry in the child in a way? I guess maybe you want the revocation pass to scan both pages in the backing VM object, but the revocation pass limits itself to the subset of each VM object that is mapped by the vm_map_entry's? So if you did munmap() then the revoker would only attempt to scan the first page and not the second?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have two pages, each containing a capability referencing the third page. I want the revoker to visit both pages and revoke both of those capabilities. I'm trying to ensure that the revoker's pmap has a PTE for the second page but not the first. munmap() would destroy the PTE, but it would also create a hole in the vm_map, causing the revoker not to visit that page.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

... and that is really just a re-statement of what you wrote, yes. :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Might be worth expanding the comment a bit further then as I think as it stands now saying you want the page unmapped doesn't quite map to that, or at least unmapped is ambiguous. I think of unmapped primarily as "logically removed from the address space", i.e. munmap, but I think here you mean "removed from the pmap map (PTEs)". Just something about the vm_map_entry still being the same so the revoker scans it all, but getting the PTE cleared is probably sufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reworded it to avoid mention of unmapping the page.

Copy link
Member

@nwf nwf left a comment

Choose a reason for hiding this comment

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

Test wibbles aside, the kernel bits LGTM.

Use a three-page heap to verify two cases: one where a copy-on-write
page bearing a to-be-revoked capability is mapped at the time that it is
scanned, and one where the page is not mapped.
@markjdb markjdb merged commit 71585cf into CTSRD-CHERI:dev Nov 25, 2023
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

Successfully merging this pull request may close these issues.

4 participants