mmap PROT_CAP and PROT_NO_CAP support - #23
Conversation
Support flags PROT_CAP and PROT_NO_CAP in mmap. PROT_CAP is implied by PROT_READ and PROT_WRITE for most mmap calls. PROT_NO_CAP forbids the use of capabilities in this mapping. Storing a tag in this mapping will cause a fault. Note that CHERI on riscv needs additional setting of CHERI_PERMS_LOAD_CAP or CHERI_PERMS_STORE_CAP, otherwise PROT_NO_CAP is not enforced. Signed-off-by: Markus Schneider-Pargmann (The Capable Hub) <msp@baylibre.com>
Implement Zcheripte 0.9.3 tval2 encoding support for CHERI PTE faults. Return SEGV_STORETAG when there is a PTE fault. Signed-off-by: Markus Schneider-Pargmann (The Capable Hub) <msp@baylibre.com>
Add selftests for mmap PROT_CAP and PROT_NO_CAP behavior. Signed-off-by: Markus Schneider-Pargmann (The Capable Hub) <msp@baylibre.com>
| perms |= CHERI_PERMS_LOAD_CAP; | ||
| if (prot & PROT_WRITE) | ||
| perms |= CHERI_PERMS_STORE_CAP; | ||
| } |
There was a problem hiding this comment.
Sorry, but this and the related text in the commit message is bogus. I somewhat see why you are doing this but we really shouldn't add additional permissions to the capability with an explicitly given PROT_NO_CAP. Yes, you will get a CHERI fault instead of a capability fault but I don't think we care. If this fails tests adjust the tests.
There was a problem hiding this comment.
Note that it is even more bogus than that if the CW bit is not supported, either because the hardware does not have it or because for some reason the arch decided to not make use of it.
There was a problem hiding this comment.
Thanks for your comment. I went back to the spec and did a lot of debugging. Without these perms, a tagged store to a PROT_NO_CAP region silently strips the tag without any fault. From src/cheri-pte-ext.adoc:
NOTE: The tag bit of the stored capability is checked _after_ it is potentially
cleared due to lack of permissions.
Keeping the perms is what can get us a real fault on a tagged store. I think that is more useful than silently dropping the tag.
In one of my tests it initially looked like the strip was not working at all which confused me a lot. But I realized now the compiler optimized the readback against the just-stored register. After forcing a real load, the behavior as documented by the spec was observable.
There was a problem hiding this comment.
After discussing this, we take the view that the capability returned should be consistent with PROT_MAX(), PROT_CAP, and PROT_NO_CAP. For example, if the caller passed PROT_READ | PROT_WRITE | PROT_NO_CAP, then the returned capability should have no capability permissions. The small additional benefit to debuggability is likely not worth the inconsistency of returning a capability with capability permissions when creating a PROT_NO_CAP mapping.
As a related note, anonymous MAP_SHARED mappings should not imply PROT_CAP, whereas anonymous MAP_PRIVATE mappings should.
Tagging @brooksdavis and @qwattash in case they have any further comments.
| return -EINVAL; | ||
| /* | ||
| * PROT_CAP is not supported with file-backed MAP_SHARED mapping | ||
| */ |
There was a problem hiding this comment.
Why is this? Isn't it the whole point to allow just that?
| local_flush_tlb_page(addr); | ||
| } | ||
|
|
||
| static inline bool access_error(unsigned long cause, struct vm_area_struct *vma) |
There was a problem hiding this comment.
I don't think this is quite right. First, you are introducing quite a large diff in a function with subtle logic. This is bound to introduce merge errors when the upstream code changes.
But more importantly, you are now saying that a cheri PTE fault on a VMA with VM_CAP_WRITE is ok. That will trigger standard fault handling which is then trying to fix an ordinary page fault and will fail to do so. So I think that all cheri PTE faults should be bad_area_nosemaphore. This also simpifies things because you can just handle those before even calling access_ok().
| @@ -351,11 +366,12 @@ void handle_page_fault(struct pt_regs *regs) | |||
| if (!vma) | |||
| goto lock_mmap; | |||
|
|
|||
There was a problem hiding this comment.
See above: Handle cheri_pte_faults here and always do an access error.
| perms |= CHERI_PERMS_LOAD_CAP; | ||
| if (prot & PROT_WRITE) | ||
| perms |= CHERI_PERMS_STORE_CAP; | ||
| } |
There was a problem hiding this comment.
Note that it is even more bogus than that if the CW bit is not supported, either because the hardware does not have it or because for some reason the arch decided to not make use of it.
| #define SEGV_CAPACCESSERR 14 /* Capability access fault */ | ||
| #define NSIGSEGV 14 | ||
| #define SEGV_STORETAG 15 /* Capability tag store fault */ | ||
| #define NSIGSEGV 15 |
There was a problem hiding this comment.
See the CI run: This breaks build on other archs.
chrehrhardt
left a comment
There was a problem hiding this comment.
See inline comments. At the very least the arch_user_ptr_owning_perms_from_prot needs to go
|
This implementation doesn't appear to square with the design in CheriBSD https://man.cheribsd.org/cgi-bin/man.cgi/mmap. The key thing is that
In the original implementation (see CTSRD-CHERI/cheribsd#2191), I had It would be super helpful if @paul-metzger could update his cheribsdtest port and test this branch with it to see how things align. |
|
Thanks for working on this! As @brooksdavis asked me to, I ran our test suite and two out of three tests for
|
Yes, you are right, I missed that this is just to revert the implication, thanks. From my perspective
I tried reproducing this here. But it works here. How are you testing, are you using the qemu setup with meta-cheri or something else?
mprotect is currently not implemented for PROT_NO_CAP or PROT_CAP. I see BSD has it implemented but doesn't have it documented in the man page. I mainly looked at the man page before implementing. |
I'm using Qemu with cheribuild. Just to make sure we are not talking at cross purposes, you get a |
|
Yes, it does Can you check if PTE.CW is supported? Also do you have a link to other tests? I would love to see what is tested and maybe integrate it into the selftests as well. |
| ptr = mmap(NULL, MMAP_SIZE, | ||
| PROT_READ | PROT_WRITE | PROT_CAP | PROT_NO_CAP, | ||
| MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
| ASSERT_EQ(MAP_FAILED, ptr); |
There was a problem hiding this comment.
I think this can only be the case if CONFIG_CHERI_PURECAP_UABI
Same for
- test_prot_cap_file_shared_einval
- test_prot_cap_mprotect_einval
- test_prot_no_cap_mprotect_einval
| void *ptr; | ||
| int fd; | ||
|
|
||
| fd = mkstemp(tmppath); |
There was a problem hiding this comment.
mkstemp() requires stdlib.h to be included.
Thanks,
The tests that I'm using are a portable adaptation of a |
In most cases PROT_CAP is automatically applied. PROT_NO_CAP removes the ability to store pointers and will create a segfault with SEGV_STORETAG.
This Pull Request also adds a self test, testing the assumptions of how it should work.
See the individual commit messages for more details.
Closes: #10