Cheriops

ALL CHERI/MIPS Instructinos are declared as emulating functions in target/mips/helper.h, with macro DEF_HELPER_*. Implemented in target/mips/op_helper_cheri.c, using macro CHERI_HELPER_IMPL.

For example, cseal instruction is declared as DEF_HELPER_4(cseal, void, env, i32, i32, i32), and implemented as void CHERI_HELPER_IMPL(cseal)(CPUMIPSState *env, uint32_t cd, uint32_t cs, uint32_t ct).

capability runtime check instructions

PCC check

DEF_HELPER_2(ccheck_pc, void, env, i64)

CHERI_HELPER_IMPL(ccheck_pc)(CPUMIPSState *env, uint64_t next_pc)

DDC check

will always be checked on ld/st without explicit instructions. see check_cap.

LD/ST check

Problem: could we do PCC/DDC and pointer checks on their types? assuming types are already available (even if they are not currently).

DEF_HELPER_3(ccheck_store, tl, env, tl, i32)
DEF_HELPER_3(ccheck_store_right, tl, env, tl, i32)
DEF_HELPER_3(ccheck_load, tl, env, tl, i32)
DEF_HELPER_3(ccheck_load_right, tl, env, tl, i32)

Branch check

DEF_HELPER_1(ccheck_btarget, void, env)

type check

DEF_HELPER_3(cchecktype, void, env, i32, i32)

perm check

DEF_HELPER_3(ccheckperm, void, env, i32, tl)

Non-check ISA instructions

perm op instructions
DEF_HELPER_4(candperm, void, env, i32, i32, tl)
sealing/types op instructions
DEF_HELPER_4(cseal, void, env, i32, i32, i32)
DEF_HELPER_4(ccseal, void, env, i32, i32, i32)  // ??????
DEF_HELPER_4(cunseal, void, env, i32, i32, i32)
DEF_HELPER_4(ccopytype, void, env, i32, i32, i32)
DEF_HELPER_3(csealentry, void, env, i32, i32)   // ??????
ccall
DEF_HELPER_3(ccall, void, env, i32, i32)
DEF_HELPER_3(ccall_notrap, tl, env, i32, i32)
tag ops
DEF_HELPER_5(cinvalidate_tag, void, env, tl, i32, i32, tl)
DEF_HELPER_5(cinvalidate_tag_left_right, void, env, tl, i32, i32, tl)
DEF_HELPER_5(cinvalidate_tag32, void, env, tl, i32, i32, i32)

DEF_HELPER_3(ccleartag, void, env, i32, i32)
DEF_HELPER_3(cloadtags, tl, env, i32, i64)
LD/ST ops
DEF_HELPER_5(cload, tl, env, i32, tl, i32, i32)

DEF_HELPER_5(cstore, tl, env, i32, tl, i32, i32)

DEF_HELPER_3(cloadlinked, tl, env, i32, i32)

DEF_HELPER_3(cstorecond, tl, env, i32, i32)

DEF_HELPER_3(cscc_without_tcg, tl, env, i32, i32)
DEF_HELPER_5(csc_without_tcg, void, env, i32, i32, tl, i32)
DEF_HELPER_5(clc_without_tcg, void, env, i32, i32, tl, i32)
DEF_HELPER_3(cllc_without_tcg, void, env, i32, i32)
EPC ops
/* cannot access EPC directly since it is the offset of EPCC */
DEF_HELPER_1(mfc0_epc, tl, env)
DEF_HELPER_2(mtc0_epc, void, env, tl)
DEF_HELPER_1(mfc0_error_epc, tl, env)
DEF_HELPER_2(mtc0_error_epc, void, env, tl)
misc
DEF_HELPER_2(cclearreg, void, env, i32)
DEF_HELPER_4(cfromptr, void, env, i32, i32, tl)
DEF_HELPER_2(cgetaddr, tl, env, i32)
DEF_HELPER_2(cgetbase, tl, env, i32)
DEF_HELPER_1(cgetcause, tl, env)
DEF_HELPER_2(cgetlen, tl, env, i32)
DEF_HELPER_2(cgetoffset, tl, env, i32)
DEF_HELPER_2(cgetpcc, void, env, i32)
DEF_HELPER_3(cgetpccsetoffset, void, env, i32, tl)
DEF_HELPER_2(cgetperm, tl, env, i32)
DEF_HELPER_2(cgetsealed, tl, env, i32)
DEF_HELPER_2(cgettag, tl, env, i32)
DEF_HELPER_2(cgettype, tl, env, i32)
DEF_HELPER_4(cincbase, void, env, i32, i32, tl)
DEF_HELPER_4(cincoffset, void, env, i32, i32, tl)
DEF_HELPER_3(cjalr, tl, env, i32, i32)
DEF_HELPER_2(cjr, tl, env, i32)
DEF_HELPER_1(creturn, void, env)
DEF_HELPER_4(csetbounds, void, env, i32, i32, tl)
DEF_HELPER_4(csetboundsexact, void, env, i32, i32, tl)
DEF_HELPER_2(crap, tl, env, tl)          // ???????
DEF_HELPER_2(cram, tl, env, tl)          // ???????
DEF_HELPER_3(csub, tl, env, i32, i32)
DEF_HELPER_2(csetcause, void, env, tl)
DEF_HELPER_4(csetlen, void, env, i32, i32, tl)
DEF_HELPER_4(csetoffset, void, env, i32, i32, tl)
DEF_HELPER_3(ctoptr, tl, env, i32, i32)
DEF_HELPER_4(cmovz, void, env, i32, i32, tl)
DEF_HELPER_4(cmovn, void, env, i32, i32, tl)


DEF_HELPER_3(ceq, tl, env, i32, i32)
DEF_HELPER_3(cne, tl, env, i32, i32)
DEF_HELPER_3(clt, tl, env, i32, i32)
DEF_HELPER_3(cle, tl, env, i32, i32)
DEF_HELPER_3(cltu, tl, env, i32, i32)
DEF_HELPER_3(cleu, tl, env, i32, i32)
DEF_HELPER_3(cexeq, tl, env, i32, i32)
DEF_HELPER_3(cnexeq, tl, env, i32, i32)
DEF_HELPER_4(csetaddr, void, env, i32, i32, tl)
DEF_HELPER_3(cgetandaddr, tl, env, i32, tl)
DEF_HELPER_4(candaddr, void, env, i32, i32, tl)
DEF_HELPER_3(ctestsubset, tl, env, i32, i32)


DEF_HELPER_2(rdhwr_statcounters_icount, tl, env, i32)
DEF_HELPER_1(rdhwr_statcounters_reset, tl, env)
DEF_HELPER_1(rdhwr_statcounters_itlb_miss, tl, env)
DEF_HELPER_1(rdhwr_statcounters_dtlb_miss, tl, env)
DEF_HELPER_2(rdhwr_statcounters_memory, tl, env, i32)
DEF_HELPER_2(rdhwr_statcounters_ignored, tl, env, i32)

Capability check primitives

check_cap

check_cap() is called by:

  • CHERI_HELPER_IMPL(ccheck_btarget): check whether the branch target is within $pcc
  • void CHERI_HELPER_IMPL(ccheck_pc)(CPUMIPSState *env, uint64_t next_pc):
  • check_ddc(CPUMIPSState *env, uint32_t perm, uint64_t ddc_offset, uint32_t len, bool instavail, uintptr_t retpc)

    // file
    //  target/mips/op_helper_cheri.c
    
    static inline void check_cap(CPUMIPSState *env, const cap_register_t *cr,
        uint32_t perm, uint64_t addr, uint16_t regnum, uint32_t len, bool instavail, uintptr_t pc)
    {
    uint16_t cause;
    /*
     * See section 5.6 in CHERI Architecture.
     *
     * Capability checks (in order of priority):
     * (1) <ctag> must be set (CP2Ca_TAG Violation).
     * (2) Seal bit must be unset (CP2Ca_SEAL Violation).
     * (3) <perm> permission must be set (CP2Ca_PERM_EXE, CP2Ca_PERM_LD,
     * or CP2Ca_PERM_ST Violation).
     * (4) <addr> must be within bounds (CP2Ca_LENGTH Violation).
     */
    if (!cr->cr_tag) {
        cause = CP2Ca_TAG;
        // fprintf(qemu_logfile, "CAP Tag VIOLATION: ");
        goto do_exception;
    }
    if (is_cap_sealed(cr)) {
        cause = CP2Ca_SEAL;
        // fprintf(qemu_logfile, "CAP Seal VIOLATION: ");
        goto do_exception;
    }
    if ((cr->cr_perms & perm) != perm) {
        switch (perm) {
            case CAP_PERM_EXECUTE:
                cause = CP2Ca_PERM_EXE;
                // fprintf(qemu_logfile, "CAP Exe VIOLATION: ");
                goto do_exception;
            case CAP_PERM_LOAD:
                cause = CP2Ca_PERM_LD;
                // fprintf(qemu_logfile, "CAP LD VIOLATION: ");
                goto do_exception;
            case CAP_PERM_STORE:
                cause = CP2Ca_PERM_ST;
                // fprintf(qemu_logfile, "CAP ST VIOLATION: ");
                goto do_exception;
            default:
                break;
        }
    }
    // fprintf(stderr, "addr=%zx, len=%zd, cr_base=%zx, cr_len=%zd\n",
    //     (size_t)addr, (size_t)len, (size_t)cr->cr_base, (size_t)cr->cr_length);
    if (!cap_is_in_bounds(cr, addr, len)) {
        cause = CP2Ca_LENGTH;
        // fprintf(qemu_logfile, "CAP Len VIOLATION: ");
        goto do_exception;
    }
    
    return;
    
    do_exception:
    env->CP0_BadVAddr = addr;
    if (!instavail)
        env->error_code |= EXCP_INST_NOTAVAIL;
    do_raise_c2_exception_impl(env, cause, regnum, pc);
    }

cap_is_in_bounds(), called by functions in target/mips/op_helper_cheri.c

  • check_cap(), ccall_common(), cseal_common()
  • CHERI_HELPER_IMPL(cjalr),
    • (cjr), (cunseal),
    • (cload), (cloadlinked), (cstorecond),
    • (cstore),
  • get_clc_addr(), get_cllc_addr(), get_csc_addr(), get_cscc_addr()

    // file
    //  target/mips/cheri_util.h
    
    
    // Check if num_bytes bytes at addr can be read using capability c
    static inline bool cap_is_in_bounds(const cap_register_t* c, uint64_t addr, uint64_t num_bytes) {
    if (addr < cap_get_base(c)) {
        return false;
    }
    // Use __builtin_add_overflow to avoid wrapping around the end of the addres space
    uint64_t access_end_addr = 0;
    if (__builtin_add_overflow(addr, num_bytes, &access_end_addr)) {
        warn_report("Found capability access that wraps around: 0x%" PRIx64
                    " + %" PRId64 ". Authorizing cap: " PRINT_CAP_FMTSTR,
                    addr, num_bytes, PRINT_CAP_ARGS(c));
        return false;
    }
    if (access_end_addr > cap_get_top(c)) {
        return false;
    }
    return true;
    }

Unsealing in ccall

TODO-1: enable _sbit_for_memory for 128 bit hybrid CHERI_128; including

  • add it in the cap register struct representation; done.
  • store it in the memory while store the capability; done.
  • load it from the memory along with the capability load; done.
  • change the bit when sealing/unsealing operation is being carried out; done.
  • change the API of testing seal or unseal, use _sbit_for_memory only;

Things might need to change:

  • target/mips/cpu.h

    • struct cap_register ==> added the sbit field _sbit_for_memory.
  • target/mips/cheri-compressed-cap/cheri_compressed_cap.h

    • enum: CC128_FILED(name, start, end): ==> added CC128_FIELD(SEALED_BIT, 109,109)
    • compress_128cap_without_xor(): ==> store the sealed bit of the cap struct to the 128bit rep.

      // LLM: read seal bit from csp
      bool S_BIT = csp->_sbit_for_memory > 0 ? true : false;
      uint64_t pesbt =
      CC128_ENCODE_FIELD(csp->cr_uperms, UPERMS) |
      CC128_ENCODE_FIELD(csp->cr_perms, HWPERMS) |
      CC128_ENCODE_FIELD(S_BIT, SEALED_BIT) |
      CC128_ENCODE_FIELD(csp->cr_otype, OTYPE) |
      CC128_ENCODE_FIELD(IE, INTERNAL_EXPONENT) |
      CC128_ENCODE_FIELD(Te, TOP_ENCODED) |
      CC128_ENCODE_FIELD(Be, BOTTOM_ENCODED);
    • decompress_128cap_already_xored(): ==> read sealed bit from 128 rep and stored in the cap struct.

  • target/mips/op_helper_cheri.c:

    • store_cap_to_memory(); // L2197 CHERI_128; L2313 CHERI_MAGIC128; L2484: CHERI 256
    • compress_128cap(csp) -> compress_128cap_without_xor() -> add seal_bit

    • load_cap_from_memory(); //

    • decompress_128cap(pesbt, cursor, &ncd) -> decompress_128cap_already_xored()

  • sealing update in target/mips/cheri_utils.h:

    • cap_set_sealed();
    • cap_set_unsealed();
    • set_max_perms_capability()
  • sealing test in target/mips/cheri_utils.h:

    • cap_is_sealed_with_type();
    • cap_is_unsealed();

TODO-2: duing unsealing, do not reset the otype, keep it instead; things might need to change:

Must change:

  • cap_set_unsealed();

Functions might be affected:

  • target/mips/op_helper_cheri.c: store_cap_to_memory(),

  • target/mips/cheri_utils.h: CAP_OTYPE_UNSEALED;

    • cap_set_sealed();
    • cap_get_otype();
    • cap_is_sealed_entry();
    • cap_unseal_entry();
    • cap_make_sealed_entry();

      // file
      //  target/mips/op_helper_cheri.c
      
      target_ulong CHERI_HELPER_IMPL(ccall_notrap)(CPUMIPSState *env, uint32_t cs, uint32_t cb)
      {
      return ccall_common(env, cs, cb, CCALL_SELECTOR_1, GETPC());
      }
      
      static target_ulong ccall_common(CPUMIPSState *env, uint32_t cs, uint32_t cb, uint32_t selector, uintptr_t _host_return_address)
      {
      const cap_register_t *csp = get_readonly_capreg(&env->active_tc, cs);
      const cap_register_t *cbp = get_readonly_capreg(&env->active_tc, cb);
      /*
      * CCall: Call into a new security domain
      */
      if (!csp->cr_tag) {
      do_raise_c2_exception(env, CP2Ca_TAG, cs);
      } else if (!cbp->cr_tag) {
      do_raise_c2_exception(env, CP2Ca_TAG, cb);
      } else if (!cap_is_sealed_with_type(cspRuntime check)) {
      do_raise_c2_exception(env, CP2Ca_SEAL, cs);
      } else if (!cap_is_sealed_with_type(cbp)) {
      do_raise_c2_exception(env, CP2Ca_SEAL, cb);
      } else if (csp->cr_otype != cbp->cr_otype || csp->cr_otype > CAP_MAX_SEALED_OTYPE) {
      do_raise_c2_exception(env, CP2Ca_TYPE, cs);
      } else if (!(csp->cr_perms & CAP_PERM_EXECUTE)) {
      do_raise_c2_exception(env, CP2Ca_PERM_EXE, cs);
      } else if (cbp->cr_perms & CAP_PERM_EXECUTE) {
      do_raise_c2_exception(env, CP2Ca_PERM_EXE, cb);
      } else if (!cap_is_in_bounds(csp, cap_get_cursor(csp), 1)) {
      // TODO: check for at least one instruction worth of data? Like cjr/cjalr?
      do_raise_c2_exception(env, CP2Ca_LENGTH, cs);
      } else {
      if (selector == CCALL_SELECTOR_0) {
          do_raise_c2_exception(env, CP2Ca_CALL, cs);
      } else if (!(csp->cr_perms & CAP_PERM_CCALL)){
          do_raise_c2_exception(env, CP2Ca_PERM_CCALL, cs);
      } else if (!(cbp->cr_perms & CAP_PERM_CCALL)){
          do_raise_c2_exception(env, CP2Ca_PERM_CCALL, cb);
      } else {
          cap_register_t idc = *cbp;
          cap_set_unsealed(&idc);
          update_capreg(&env->active_tc, CP2CAP_IDC, &idc);
          // The capability register is loaded into PCC during delay slot
          env->active_tc.CapBranchTarget = *csp;
          // XXXAR: clearing these fields is not strictly needed since they
          // aren't copied from the CapBranchTarget to $pcc but it does make
          // the LOG_INSTR output less confusing.
          cap_set_unsealed(&env->active_tc.CapBranchTarget);
          // Return the branch target address
          return cap_get_cursor(csp);
      }
      }
      return (target_ulong)0;
      }
      // file
      //  target/mips/cheri_utils.h
      
      
      static inline void cap_set_unsealed(cap_register_t* c) {
      assert(c->cr_tag);
      assert(cap_is_sealed_with_type(c));
      assert(c->cr_otype <= CAP_MAX_SEALED_OTYPE && "should not use this to unsealed reserved types");
      c->cr_otype = CAP_OTYPE_UNSEALED;
      #ifndef CHERI_128
      assert(c->_sbit_for_memory == true);
      c->_sbit_for_memory = false;
      #endif
      }

cap_register

There is no seal bit (for memory) in 256-bit cap

// file
//  target/mips/cpu.h
/*
 * Please note if this structure is changed then the TCG gen_branch() in
 * translate.c may need to be changed as well.
 */
struct cap_register {
                        /* offset = cursor - base */
    uint64_t cr_offset; /* Capability offset */
    uint64_t cr_base;   /* Capability base addr */
    /* Length is actually 65 bits (TODO: should store top instead) */
    unsigned __int128 _cr_top; /* Capability top */
    uint32_t cr_perms;  /* Permissions */
    uint32_t cr_uperms; /* User Permissions */
#ifdef CHERI_128
    uint64_t cr_pesbt_xored_for_mem;  /* Perms, E, Sealed, Bot, & Top bits (128-bit) */
#endif
    uint32_t cr_otype;  /* Object Type, 24 bits */
    uint8_t  cr_tag;    /* Tag */
#ifndef CHERI_128
    bool _sbit_for_memory;
#endif
};
// file
//  target/mips/cheri-compressed-cap/cheri_compressed_cap.h

// QEMU already provides cap_register_t but if used in other programs
// we want to define it here:
#ifndef HAVE_CAP_REGISTER_T
struct cap_register {
    /* offset = cursor - base */
    uint64_t cr_offset; /* Capability offset */
    uint64_t cr_base;   /* Capability base addr */
    cc128_length_t _cr_top; /* Capability top */
    uint32_t cr_perms;  /* Permissions */
    uint32_t cr_uperms; /* User Permissions */
    uint64_t cr_pesbt_xored_for_mem;  /* Perms, E, Sealed, Bot, & Top bits (128-bit) */
    uint32_t cr_otype;  /* Object Type, 24 bits */
    uint8_t cr_tag;     /* Tag */
    uint8_t _sbit_for_memory; /* sealed flag */
#ifdef __cplusplus
    inline uint64_t base() const {
       return cr_base;
    }
    inline uint64_t address() const {
       return cr_base + cr_offset;
    }
    inline cc128_length_t top() const {
       return _cr_top;
    }
    inline uint64_t top64() const {
        const cc128_length_t t = top();
        return t > UINT64_MAX ? UINT64_MAX : (uint64_t)t;
    }
    inline cc128_length_t length() const {
        return _cr_top - cr_base;
    }
    inline uint64_t length64() const {
        const cc128_length_t l = length();
        return l > UINT64_MAX ? UINT64_MAX : (uint64_t)l;
    }
#endif
};
typedef struct cap_register cap_register_t;

#endif

Reference:

  • CLoadTags
  • Q&A How it is accessing the tag table without access data memory? call cheri_get_many Reference 1 // in disas/mips.c {"cloadtags", "t,+b", 0x4800.07bf, 0xffe0,07ff, 0, 0, I1}, Instruction ISA definition in C: // target/mips/helper.h DEF_HELPER_3(cloadtags, tl, env, i32, cap_checked_ptr) // target/mips/os_helper_cheri.c target_ulong CHERI_HELPER_IMPL(cloadtags(CPUArchState *env, uint32_t cb, uint64_t cbcursor)) Helper function to access tag table in memory. // in target/cheri-common/cheri_tagmem.c int cheri_tag_get_many(CPUArchState *env, target_ulong vaddr, int reg, hwaddr *ret_paddr, uintptr_t pc)// target/mips/translate_cheri.

Created Sep 20, 2019 // Last Updated May 18, 2021

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?