/* * Variation that prints the saved userspace CHERI register frame for a * thread. */ DB_SHOW_COMMAND(cheriframe, ddb_dump_cheriframe) { struct thread *td; struct cheri_frame *cfp; u_int i; if (have_addr) td = db_lookup_thread(addr, TRUE); else td = curthread; cfp = &td->td_pcb->pcb_cheriframe; db_printf("Thread %d at %p\n", td->td_tid, td); db_printf("CHERI frame at %p\n", cfp); /* Laboriously load and print each user capability. */ for (i = 0; i < 27; i++) { cheri_capability_load(CHERI_CR_CTEMP0, (struct chericap *)&cfp->cf_c0 + i); DB_CHERI_REG_PRINT(CHERI_CR_CTEMP0, i); } cheri_capability_load(CHERI_CR_CTEMP0, (struct chericap *)&cfp->cf_c0 + CHERIFRAME_OFF_PCC); db_printf("PCC "); DB_CHERI_CAP_PRINT(CHERI_CR_CTEMP0); }
/* * Because contexts contain tagged capabilities, we can't just use memcpy() * on the data structure. Once the C compiler knows about capabilities, then * direct structure assignment should be plausible. In the mean time, an * explicit capability context copy routine is required. * * XXXRW: Compiler should know how to do copies of tagged capabilities. * * XXXRW: Compiler should be providing us with the temporary register. */ void cheri_capability_copy(struct chericap *cp_to, struct chericap *cp_from) { cheri_capability_load(CHERI_CR_CTEMP0, cp_from); cheri_capability_store(CHERI_CR_CTEMP0, cp_to); }
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); }
/* * If a signal is delivered while in a sandbox, forceably unwind the trusted * stack simulating a CReturn. Clear the regular and capability register * files. * * When a signal is thrown in a sandbox, one option is for the kernel to * forceably unwind the stack by a frame. * * Note that the callee has not had a chance to clean up the mess -- and * particular, can't clear the register file before returning. We therefore * have to do that for the callee or information/rights may leak!. * * XXXRW: Really we want to delegate this to userspace via SIGSANDBOX or * similar, but in the mean time. * * XXXRW: We don't yet handle floating point. */ int cheri_stack_sandboxexception(struct thread *td, struct trapframe *tf, int signum) { struct cheri_stack_frame *csfp; struct pcb *pcb = td->td_pcb; register_t s, sr, badvaddr, cause; f_register_t fsr; if (pcb->pcb_cheristack.cs_tsp == CHERI_STACK_SIZE) return (0); printf("%s: processing sandbox exception signal %d, pid %d\n", __func__, signum, td->td_proc->p_pid); #if DDB if (security_cheri_debugger_on_sandbox_exception) kdb_enter(KDB_WHY_CHERI, "CHERI sandbox exception"); #endif /* * XXXRW: It is my belief that the trap frame in a thread is always a * pointer to the PCB. Check this is true, however, because I rely on * it. */ KASSERT(td->td_frame == &pcb->pcb_regs, ("%s: td_frame != pcb_regs", __func__)); KASSERT(td->td_frame == tf, ("%s: td_frame != tf", __func__)); /* * XXXRW: It would actually be quite nice to print out some exception * information here. Otherwise all the state required to debug the * sandbox failure will be lost. * * XXXRW: Or, has it all been sent to printf? * * XXXRW: Or, maybe that is actually a bad thing, since printf is * quite slow and noisy, and not something we want to do on every * sandbox failure. */ /* * Clear the regular and capability register files to ensure no state * (information, rights) is returned to the caller that shouldn't be * when the callee exits unexpectedly. Save and restore kernel-side * registers, however. * * XXXRW: What about floating-point registers? */ sr = pcb->pcb_regs.sr; badvaddr = pcb->pcb_regs.badvaddr; cause = pcb->pcb_regs.cause; fsr = pcb->pcb_regs.fsr; bzero(&pcb->pcb_regs, sizeof(pcb->pcb_regs)); bzero(&pcb->pcb_cheriframe, sizeof(pcb->pcb_cheriframe)); pcb->pcb_regs.sr = sr; pcb->pcb_regs.badvaddr = badvaddr; pcb->pcb_regs.cause = cause; pcb->pcb_regs.fsr = fsr; /* * Reproduce CReturn. */ csfp = &pcb->pcb_cheristack.cs_frames[pcb->pcb_cheristack.cs_tsp / CHERI_FRAME_SIZE]; pcb->pcb_cheristack.cs_tsp += CHERI_FRAME_SIZE; /* * Pop IDC, PCC. */ s = intr_disable(); cheri_capability_load(CHERI_CR_CTEMP, &csfp->csf_idc); cheri_capability_store(CHERI_CR_CTEMP, &pcb->pcb_cheriframe.cf_idc); cheri_capability_load(CHERI_CR_CTEMP, &csfp->csf_pcc); cheri_capability_store(CHERI_CR_CTEMP, &pcb->pcb_cheriframe.cf_pcc); intr_restore(s); /* * Pop SP, PC (+4 already done). */ pcb->pcb_regs.sp = csfp->csf_sp; pcb->pcb_regs.pc = csfp->csf_pc; /* * Set 'v0' to -1, and 'v1' to the signal number so that the consumer * of CCall can handle the error. * * XXXRW: That isn't really quite what we want, however. What about * CCall failures themselves, and what if CReturn returns a -1 -- how * should the caller interpret that? */ pcb->pcb_regs.v0 = -1; pcb->pcb_regs.v1 = signum; return (1); }