-
Notifications
You must be signed in to change notification settings - Fork 6
mmap PROT_CAP and PROT_NO_CAP support #23
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
base: codasip-cheri-riscv-7.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -246,29 +246,33 @@ static inline void vmalloc_fault(struct pt_regs *regs, int code, unsigned long a | |
| local_flush_tlb_page(addr); | ||
| } | ||
|
|
||
| static inline bool access_error(unsigned long cause, struct vm_area_struct *vma) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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. |
||
| /* | ||
| * Returns 0 if the access is permitted, otherwise the SIGSEGV si_code to | ||
| * deliver. | ||
| */ | ||
| static inline int access_error(unsigned long cause, struct vm_area_struct *vma, | ||
| bool cheri_pte_fault) | ||
| { | ||
| switch (cause) { | ||
| case EXC_INST_PAGE_FAULT: | ||
| if (!(vma->vm_flags & VM_EXEC)) { | ||
| return true; | ||
| } | ||
| if (!(vma->vm_flags & VM_EXEC)) | ||
| return SEGV_ACCERR; | ||
| break; | ||
| case EXC_LOAD_PAGE_FAULT: | ||
| /* Write implies read */ | ||
| if (!(vma->vm_flags & (VM_READ | VM_WRITE))) { | ||
| return true; | ||
| } | ||
| if (!(vma->vm_flags & (VM_READ | VM_WRITE))) | ||
| return SEGV_ACCERR; | ||
| break; | ||
| case EXC_STORE_PAGE_FAULT: | ||
| if (!(vma->vm_flags & VM_WRITE)) { | ||
| return true; | ||
| } | ||
| if (!(vma->vm_flags & VM_WRITE)) | ||
| return SEGV_ACCERR; | ||
| if (cheri_pte_fault && !(vma->vm_flags & VM_WRITE_CAPS)) | ||
| return SEGV_STORETAG; | ||
| break; | ||
| default: | ||
| panic("%s: unhandled cause %lu", __func__, cause); | ||
| } | ||
| return false; | ||
| return 0; | ||
| } | ||
|
|
||
| /* | ||
|
|
@@ -283,11 +287,22 @@ void handle_page_fault(struct pt_regs *regs) | |
| unsigned long addr, cause; | ||
| unsigned int flags = FAULT_FLAG_DEFAULT; | ||
| int code = SEGV_MAPERR; | ||
| int access_code; | ||
| vm_fault_t fault; | ||
| bool cheri_pte_fault = false; | ||
|
|
||
| cause = regs->cause; | ||
| addr = regs->badaddr; | ||
|
|
||
| #ifdef CONFIG_RISCV_CHERI | ||
| if (cause == EXC_STORE_PAGE_FAULT || cause == EXC_LOAD_PAGE_FAULT) { | ||
| unsigned long t2 = csr_read(CSR_TVAL2); | ||
|
|
||
| cheri_pte_fault = (t2 == TVAL2_PF_CHERI || | ||
| t2 == TVAL2_PF_RISCV_CHERI); | ||
| } | ||
| #endif | ||
|
|
||
| tsk = current; | ||
| mm = tsk->mm; | ||
|
|
||
|
|
@@ -351,11 +366,12 @@ void handle_page_fault(struct pt_regs *regs) | |
| if (!vma) | ||
| goto lock_mmap; | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above: Handle cheri_pte_faults here and always do an access error. |
||
| if (unlikely(access_error(cause, vma))) { | ||
| access_code = access_error(cause, vma, cheri_pte_fault); | ||
| if (unlikely(access_code)) { | ||
| vma_end_read(vma); | ||
| count_vm_vma_lock_event(VMA_LOCK_SUCCESS); | ||
| tsk->thread.bad_cause = cause; | ||
| bad_area_nosemaphore(regs, SEGV_ACCERR, addr); | ||
| bad_area_nosemaphore(regs, access_code, addr); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -390,11 +406,10 @@ void handle_page_fault(struct pt_regs *regs) | |
| * Ok, we have a good vm_area for this memory access, so | ||
| * we can handle it. | ||
| */ | ||
| code = SEGV_ACCERR; | ||
|
|
||
| if (unlikely(access_error(cause, vma))) { | ||
| access_code = access_error(cause, vma, cheri_pte_fault); | ||
| if (unlikely(access_code)) { | ||
| tsk->thread.bad_cause = cause; | ||
| bad_area(regs, mm, code, addr); | ||
| bad_area(regs, mm, access_code, addr); | ||
| return; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -244,7 +244,8 @@ typedef struct siginfo { | |
| #define SEGV_CAPBOUNDSERR 12 /* Capability bounds fault */ | ||
| #define SEGV_CAPPERMERR 13 /* Capability permission fault */ | ||
| #define SEGV_CAPACCESSERR 14 /* Capability access fault */ | ||
| #define NSIGSEGV 14 | ||
| #define SEGV_STORETAG 15 /* Capability tag store fault */ | ||
| #define NSIGSEGV 15 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the CI run: This breaks build on other archs. |
||
|
|
||
| /* | ||
| * SIGBUS si_codes | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -404,9 +404,26 @@ unsigned long do_mmap(struct file *file, unsigned long addr, | |
| * to. we assume access permissions have been handled by the open | ||
| * of the memory object, so we don't do any here. | ||
| */ | ||
| if (IS_ENABLED(CONFIG_CHERI_PURECAP_UABI)) { | ||
| if ((prot & PROT_CAP) && (prot & PROT_NO_CAP)) | ||
| return -EINVAL; | ||
| /* | ||
| * PROT_CAP is not supported with file-backed MAP_SHARED mapping | ||
| */ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this? Isn't it the whole point to allow just that? |
||
| if ((prot & PROT_CAP) && file && (flags & MAP_SHARED)) | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(file, flags) | | ||
| mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; | ||
|
|
||
| if (IS_ENABLED(CONFIG_CHERI_PURECAP_UABI)) { | ||
| if (prot & PROT_CAP) | ||
| vm_flags |= VM_READ_CAPS | VM_WRITE_CAPS; | ||
| else if (prot & PROT_NO_CAP) | ||
| vm_flags &= ~(VM_READ_CAPS | VM_WRITE_CAPS); | ||
| } | ||
|
|
||
| /* Obtain the address to map to. we verify (or select) it and ensure | ||
| * that it represents a valid section of the address space. | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # SPDX-License-Identifier: GPL-2.0 | ||
|
|
||
| CFLAGS += -std=gnu99 -I. | ||
|
|
||
| TEST_GEN_PROGS := mmap_prot_cap | ||
|
|
||
| include ../../lib.mk | ||
|
|
||
| $(OUTPUT)/mmap_prot_cap: mmap_prot_cap.c | ||
| $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^ |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.