/* * Given an existing more privileged capability (fromcrn), build a new * capability in tocrn with the contents of the passed flattened * representation. * * XXXRW: It's not yet clear how important ordering is here -- try to do the * privilege downgrade in a way that will work when doing an "in place" * downgrade, with permissions last. * * XXXRW: How about the sealed bit? * * XXXRW: In the new world order of CSetBounds, it's not clear that taking * explicit base/length/offset arguments is quite the right thing. */ void cheri_capability_set(struct chericap *cp, uint32_t perms, void *otypep, void *basep, size_t length, off_t off) { #ifdef INVARIANTS register_t r; #endif /* 'basep' is relative to $kdc. */ CHERI_CINCOFFSET(CHERI_CR_CTEMP0, CHERI_CR_KDC, (register_t)basep); CHERI_CSETBOUNDS(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)length); CHERI_CANDPERM(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)perms); CHERI_CINCOFFSET(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)off); #if 0 /* XXXRW: For now, don't set type. */ CHERI_CSETTYPE(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)otypep); #endif /* * NB: With imprecise bounds, we want to assert that the results will * be 'as requested' -- i.e., that the kernel always request bounds * that can be represented precisly. * * XXXRW: Given these assupmtions, we actually don't need to do the * '+= off' above. */ #ifdef INVARIANTS CHERI_CGETPERM(r, CHERI_CR_CTEMP0); KASSERT(r == (register_t)perms, ("%s: permissions 0x%x rather than 0x%x", __func__, (unsigned int)r, perms)); CHERI_CGETBASE(r, CHERI_CR_CTEMP0); KASSERT(r == (register_t)basep, ("%s: base %p rather than %p", __func__, (void *)r, basep)); CHERI_CGETLEN(r, CHERI_CR_CTEMP0); KASSERT(r == (register_t)length, ("%s: length 0x%x rather than %p", __func__, (unsigned int)r, (void *)length)); CHERI_CGETOFFSET(r, CHERI_CR_CTEMP0); KASSERT(r == (register_t)off, ("%s: offset %p rather than %p", __func__, (void *)r, (void *)off)); #if 0 CHERI_CGETTYPE(r, CHERI_CR_CTEMP0); KASSERT(r == (register_t)otypep, ("%s: otype %p rather than %p", __func__, (void *)r, otypep)); #endif #endif CHERI_CSC(CHERI_CR_CTEMP0, CHERI_CR_KDC, (register_t)cp, 0); }
void cheri_serialize(struct cheri_serial *csp, struct chericap *cap) { register_t r; cheri_capability_load(CHERI_CR_CTEMP0, cap); #if CHERICAP_SIZE == 16 csp->cs_storage = 3; csp->cs_typebits = 16; csp->cs_permbits = 23; #else /* CHERICAP_SIZE == 32 */ csp->cs_storage = 4; csp->cs_typebits = 24; csp->cs_permbits = 31; #endif KASSERT(csp != NULL, ("Can't serialize to a NULL pointer")); if (cap == NULL) { memset(csp, 0, sizeof(*csp)); return; } CHERI_CGETTAG(r, CHERI_CR_CTEMP0); csp->cs_tag = r; if (csp->cs_tag) { CHERI_CGETTYPE(r, CHERI_CR_CTEMP0); csp->cs_type = r; CHERI_CGETPERM(r, CHERI_CR_CTEMP0); csp->cs_perms = r; CHERI_CGETSEALED(r, CHERI_CR_CTEMP0); csp->cs_sealed = r; CHERI_CGETBASE(r, CHERI_CR_CTEMP0); csp->cs_base = r; CHERI_CGETLEN(r, CHERI_CR_CTEMP0); csp->cs_length = r; CHERI_CGETOFFSET(r, CHERI_CR_CTEMP0); csp->cs_offset = r; } else memcpy(&csp->cs_data, cap, CHERICAP_SIZE); }
int cheriabi_sysarch(struct thread *td, struct cheriabi_sysarch_args *uap) { struct trapframe *regs = &td->td_pcb->pcb_regs; int error; int parms_from_cap = 1; size_t reqsize; register_t reqperms; /* * The sysarch() fill_uap function is machine-independent so can not * check the validity of the capabilty which becomes uap->parms. As * such, it makes no attempt to convert the result. We need to * perform those checks here. */ switch (uap->op) { case MIPS_SET_TLS: reqsize = 0; reqperms = 0; break; case MIPS_GET_TLS: case CHERI_GET_STACK: case CHERI_GET_TYPECAP: reqsize = sizeof(struct chericap); reqperms = CHERI_PERM_STORE|CHERI_PERM_STORE_CAP; break; case CHERI_SET_STACK: reqsize = sizeof(struct chericap); reqperms = CHERI_PERM_LOAD|CHERI_PERM_LOAD_CAP; break; case CHERI_MMAP_GETBASE: case CHERI_MMAP_GETLEN: case CHERI_MMAP_GETOFFSET: case CHERI_MMAP_GETPERM: case CHERI_MMAP_SETOFFSET: case CHERI_MMAP_SETBOUNDS: reqsize = sizeof(uint64_t); reqperms = CHERI_PERM_STORE; break; case CHERI_MMAP_ANDPERM: reqsize = sizeof(uint64_t); reqperms = CHERI_PERM_LOAD|CHERI_PERM_STORE; break; case MIPS_GET_COUNT: parms_from_cap = 0; break; #ifdef CPU_QEMU_MALTA case QEMU_GET_QTRACE: reqsize = sizeof(int); reqperms = CHERI_PERM_STORE; break; case QEMU_SET_QTRACE: reqsize = sizeof(int); reqperms = CHERI_PERM_LOAD; break; #endif default: return (EINVAL); } if (parms_from_cap) { error = cheriabi_cap_to_ptr(&uap->parms, ®s->c3, reqsize, reqperms, 0); if (error != 0) return (error); } switch (uap->op) { case MIPS_SET_TLS: return (cheriabi_set_user_tls(td, ®s->c3)); case MIPS_GET_TLS: error = copyoutcap(&td->td_md.md_tls_cap, uap->parms, sizeof(struct chericap)); return (error); case CHERI_MMAP_GETBASE: { size_t base; PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETBASE(base, CHERI_CR_CTEMP0); PROC_UNLOCK(td->td_proc); if (suword64(uap->parms, base) != 0) return (EFAULT); return (0); } case CHERI_MMAP_GETLEN: { size_t len; PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETLEN(len, CHERI_CR_CTEMP0); PROC_UNLOCK(td->td_proc); if (suword64(uap->parms, len) != 0) return (EFAULT); return (0); } case CHERI_MMAP_GETOFFSET: { ssize_t offset; PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETOFFSET(offset, CHERI_CR_CTEMP0); PROC_UNLOCK(td->td_proc); if (suword64(uap->parms, offset) != 0) return (EFAULT); return (0); } case CHERI_MMAP_GETPERM: { uint64_t perms; PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETPERM(perms, CHERI_CR_CTEMP0); PROC_UNLOCK(td->td_proc); if (suword64(uap->parms, perms) != 0) return (EFAULT); return (0); } case CHERI_MMAP_ANDPERM: { uint64_t perms; perms = fuword64(uap->parms); if (perms == -1) return (EINVAL); PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CANDPERM(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)perms); CHERI_CSC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETPERM(perms, CHERI_CR_CTEMP0); PROC_UNLOCK(td->td_proc); if (suword64(uap->parms, perms) != 0) return (EFAULT); return (0); } case CHERI_MMAP_SETOFFSET: { size_t len; ssize_t offset; offset = fuword64(uap->parms); /* Reject errors and misaligned offsets */ if (offset == -1 || (offset & PAGE_MASK) != 0) return (EINVAL); PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETLEN(len, CHERI_CR_CTEMP0); /* Don't allow out of bounds offsets, they aren't useful */ if (offset < 0 || offset > len) { PROC_UNLOCK(td->td_proc); return (EINVAL); } CHERI_CSETOFFSET(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)offset); CHERI_CSC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); PROC_UNLOCK(td->td_proc); return (0); } case CHERI_MMAP_SETBOUNDS: { size_t len, olen; ssize_t offset; len = fuword64(uap->parms); /* Reject errors or misaligned lengths */ if (len == (size_t)-1 || (len & PAGE_MASK) != 0) return (EINVAL); PROC_LOCK(td->td_proc); CHERI_CLC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); CHERI_CGETLEN(olen, CHERI_CR_CTEMP0); CHERI_CGETOFFSET(offset, CHERI_CR_CTEMP0); /* Don't try to set out of bounds lengths */ if (offset > olen || len > olen - offset) { PROC_UNLOCK(td->td_proc); return (EINVAL); } CHERI_CSETBOUNDS(CHERI_CR_CTEMP0, CHERI_CR_CTEMP0, (register_t)len); CHERI_CSC(CHERI_CR_CTEMP0, CHERI_CR_KDC, &td->td_proc->p_md.md_cheri_mmap_cap, 0); PROC_UNLOCK(td->td_proc); return (0); } default: return (sysarch(td, (struct sysarch_args*)uap)); } }