Skip to content

Commit

Permalink
Avoid using a "fake" XOR mask for Morello
Browse files Browse the repository at this point in the history
Instead follow what the sail model does and invert the exponent and IE
values when reading them as part of the decode. This will also be useful
for supporting the RISC-V standard encoding which also avoids the XOR
on load and instead deals with the inversion during decode.
Introduce new _CC_N({ENCODE,EXTRACT}_{IE,EXPONENT}) macros to deal with
the differences in formats instead of reading the raw field values.

This also includes various Morello test changes since the input PESBT
values now need to be passed without the previous mask applied.
  • Loading branch information
arichardson committed Nov 20, 2024
1 parent b9a2b24 commit 9d5a656
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 75 deletions.
7 changes: 7 additions & 0 deletions cheri_compressed_cap_128.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ enum _CC_N(OTypes) {

_CC_STATIC_ASSERT_SAME(CC128_MANTISSA_WIDTH, CC128_FIELD_EXP_ZERO_BOTTOM_SIZE);

// CHERI ISA v9 uses the "internal exponent" bit.
#define CC128_ENCODE_IE(IE) _CC_ENCODE_FIELD(IE, INTERNAL_EXPONENT)
#define CC128_EXTRACT_IE(pesbt) _CC_EXTRACT_FIELD(pesbt, INTERNAL_EXPONENT)
// The exponent bits in memory are xored on load/store, so we encode the raw exponent value.
#define CC128_ENCODE_EXPONENT(E) _CC_ENCODE_SPLIT_EXPONENT(E)
#define CC128_EXTRACT_EXPONENT(pesbt) _CC_EXTRACT_SPLIT_EXPONENT(pesbt)

#include "cheri_compressed_cap_common.h"

// Sanity-check mask is the expected NULL encoding
Expand Down
14 changes: 12 additions & 2 deletions cheri_compressed_cap_128m.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ enum {
_CC_FIELD(FLAGS, 64, 65),
_CC_FIELD(RESERVED, 64, 65),

_CC_FIELD(INTERNAL_EXPONENT, 94, 94),
_CC_FIELD(EXPONENT_ZERO, 94, 94),
// The FIELD_INTERNAL_EXPONENT_SIZE name is currently required by various static assertions.
_CC_N(FIELD_INTERNAL_EXPONENT_SIZE) = _CC_N(FIELD_EXPONENT_ZERO_SIZE),
_CC_FIELD(TOP_ENCODED, 93, 80),
_CC_FIELD(BOTTOM_ENCODED, 79, 64),

Expand All @@ -124,6 +126,7 @@ enum {
#define CC128M_BOT_WIDTH CC128M_FIELD_EXP_ZERO_BOTTOM_SIZE
#define CC128M_BOT_INTERNAL_EXP_WIDTH CC128M_FIELD_EXP_NONZERO_BOTTOM_SIZE
#define CC128M_EXP_LOW_WIDTH CC128M_FIELD_EXPONENT_LOW_PART_SIZE
#define CC128M_EXPONENT_WIDTH (CC128M_FIELD_EXPONENT_LOW_PART_SIZE + CC128M_FIELD_EXPONENT_HIGH_PART_SIZE)

#define CC128M_PERM_GLOBAL (1 << 0)
#define CC128M_PERM_EXECUTIVE (1 << 1)
Expand Down Expand Up @@ -179,10 +182,17 @@ enum _CC_N(OTypes) {

_CC_STATIC_ASSERT_SAME(CC128M_MANTISSA_WIDTH, CC128M_FIELD_EXP_ZERO_BOTTOM_SIZE);

// Morello uses an "exponent zero" flag instead of "internal exponent".
#define CC128M_ENCODE_IE(value) _CC_ENCODE_FIELD(!(value), EXPONENT_ZERO)
#define CC128M_EXTRACT_IE(pesbt) (!_CC_EXTRACT_FIELD(pesbt, EXPONENT_ZERO))
// The exponent bits in memory are negated when decoding in the IE case.
#define CC128M_ENCODE_EXPONENT(E) _CC_ENCODE_SPLIT_EXPONENT(~(E))
#define CC128M_EXTRACT_EXPONENT(pesbt) ((~_CC_EXTRACT_SPLIT_EXPONENT(pesbt)) & _CC_BITMASK64(CC128M_EXPONENT_WIDTH))

#include "cheri_compressed_cap_common.h"

// Sanity-check mask is the expected NULL encoding
_CC_STATIC_ASSERT_SAME(CC128M_MEM_XOR_MASK, UINT64_C(0x0000000040070007));
_CC_STATIC_ASSERT_SAME(CC128M_MEM_XOR_MASK, UINT64_C(0));

#define CC128M_FIELD(name, last, start) _CC_FIELD(name, last, start)
#define CC128M_ENCODE_FIELD(value, name) _CC_ENCODE_FIELD(value, name)
Expand Down
7 changes: 7 additions & 0 deletions cheri_compressed_cap_64.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ enum _CC_N(OTypes) {

_CC_STATIC_ASSERT_SAME(CC64_MANTISSA_WIDTH, CC64_FIELD_EXP_ZERO_BOTTOM_SIZE);

// CHERI ISA v9 uses the "internal exponent" bit.
#define CC64_ENCODE_IE(IE) _CC_ENCODE_FIELD(IE, INTERNAL_EXPONENT)
#define CC64_EXTRACT_IE(pesbt) _CC_EXTRACT_FIELD(pesbt, INTERNAL_EXPONENT)
// The exponent bits in memory are xored on load/store, so we encode the raw exponent value.
#define CC64_ENCODE_EXPONENT(E) _CC_ENCODE_SPLIT_EXPONENT(E)
#define CC64_EXTRACT_EXPONENT(pesbt) _CC_EXTRACT_SPLIT_EXPONENT(pesbt)

#include "cheri_compressed_cap_common.h"

// Sanity-check mask is the expected NULL encoding
Expand Down
60 changes: 16 additions & 44 deletions cheri_compressed_cap_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,23 @@ enum {
_CC_N(RESET_EXP) = _CC_N(MAX_EXPONENT),
_CC_N(RESET_T) = 1u << (_CC_N(ADDR_WIDTH) - _CC_N(RESET_EXP) - _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE)),
#ifdef CC_IS_MORELLO
// Due to magic constant XOR aversion (i.e. fields are either entirely
// inverted or not at all, rather than select bits within them like in
// normal CHERI Concentrate), NULL is special in Morello.
// In order to have an all-zeroes memory representation, Morello encodes NULL as internal exponent with an
// "all-ones" exponent (a value larger than the maximum exponent). Therefore NULL and reset have different EBT.
_CC_N(NULL_EXP) = _CC_N(MAX_ENCODABLE_EXPONENT),
_CC_N(NULL_T) = 0,
#else
// NULL uses identical bounds encoding to the reset capability.
_CC_N(NULL_EXP) = _CC_N(RESET_EXP),
_CC_N(NULL_T) = _CC_N(RESET_T),
#endif
_CC_N(RESET_EBT) =
_CC_ENCODE_EBT_FIELD(1, INTERNAL_EXPONENT) | _CC_ENCODE_EBT_FIELD(_CC_N(RESET_T), EXP_NONZERO_TOP) |
_CC_ENCODE_EBT_FIELD(0, EXP_NONZERO_BOTTOM) |
_CC_ENCODE_EBT_FIELD(_CC_N(RESET_EXP) >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), EXPONENT_HIGH_PART) |
_CC_ENCODE_EBT_FIELD(_CC_N(RESET_EXP) & _CC_N(FIELD_EXPONENT_LOW_PART_MAX_VALUE), EXPONENT_LOW_PART),
_CC_N(RESET_EBT) = _CC_N(ENCODE_IE)(true) | _CC_N(ENCODE_EXPONENT)(_CC_N(RESET_EXP)) |
_CC_ENCODE_EBT_FIELD(_CC_N(RESET_T), EXP_NONZERO_TOP) |
_CC_ENCODE_EBT_FIELD(0, EXP_NONZERO_BOTTOM),
_CC_N(RESET_PESBT) = _CC_N(RESET_EBT) | _CC_ENCODE_FIELD(_CC_N(UPERMS_ALL), UPERMS) |
_CC_ENCODE_FIELD(_CC_N(PERMS_ALL), HWPERMS) | _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE),
_CC_N(NULL_PESBT) = _CC_ENCODE_FIELD(0, UPERMS) | _CC_ENCODE_FIELD(0, HWPERMS) | _CC_ENCODE_FIELD(0, RESERVED) |
_CC_ENCODE_FIELD(0, FLAGS) | _CC_ENCODE_FIELD(1, INTERNAL_EXPONENT) |
_CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE) |
_CC_ENCODE_FIELD(_CC_N(NULL_T), EXP_NONZERO_TOP) | _CC_ENCODE_FIELD(0, EXP_NONZERO_BOTTOM) |
_CC_ENCODE_FIELD(_CC_N(NULL_EXP) >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), EXPONENT_HIGH_PART) |
_CC_ENCODE_FIELD(_CC_N(NULL_EXP) & _CC_N(FIELD_EXPONENT_LOW_PART_MAX_VALUE), EXPONENT_LOW_PART),
_CC_N(NULL_EBT) = _CC_N(ENCODE_IE)(true) | _CC_N(ENCODE_EXPONENT)(_CC_N(NULL_EXP)) |
_CC_ENCODE_FIELD(_CC_N(NULL_T), EXP_NONZERO_TOP) | _CC_ENCODE_FIELD(0, EXP_NONZERO_BOTTOM),
_CC_N(NULL_PESBT) = _CC_N(NULL_EBT) | _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE),
// We mask on store/load so this invisibly keeps null 0 whatever we choose it to be.
_CC_N(MEM_XOR_MASK) = _CC_N(NULL_PESBT),
_CC_N(NULL_XOR_MASK) __attribute__((deprecated("Use _MEM_XOR_MASK instead"))) = _CC_N(MEM_XOR_MASK),
Expand Down Expand Up @@ -240,7 +234,7 @@ TRUNCATE_LSB_FUNC(64)
#define _cc_truncateLSB(type_width) _cc_N(_CC_CONCAT(truncateLSB_, type_width))

struct _cc_N(bounds_bits) {
uint16_t B; // bottom bits (currently 14 bits)
uint16_t B; // bottom bits (currently 14 bits, 16 for Morello)
uint16_t T; // top bits (12 bits plus two implied bits)
uint8_t E; // exponent
bool IE; // internal exponent flag
Expand Down Expand Up @@ -277,23 +271,17 @@ static inline _cc_bounds_bits _cc_N(extract_bounds_bits)(_cc_addr_t pesbt) {
_CC_STATIC_ASSERT(sizeof(result.E) * __CHAR_BIT__ >=
_CC_N(FIELD_EXPONENT_LOW_PART_SIZE) + _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE),
"E field too small");
result.IE = (bool)(uint32_t)_CC_EXTRACT_FIELD(pesbt, INTERNAL_EXPONENT);
result.IE = (bool)_CC_N(EXTRACT_IE)(pesbt);
uint8_t L_msb;
if (result.IE) {
result.E = (uint8_t)(_CC_EXTRACT_FIELD(pesbt, EXPONENT_LOW_PART) |
(_CC_EXTRACT_FIELD(pesbt, EXPONENT_HIGH_PART) << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE)));
result.E = (uint8_t)_CC_N(EXTRACT_EXPONENT)(pesbt);
// Do not offset by 1! We also need to encode E=0 even with IE
// Also allow nonsense values over 64 - BWidth + 2: this is expected by sail-generated tests
// E = MIN(64 - BWidth + 2, E);
result.B = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_NONZERO_BOTTOM) << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE);
result.T = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_NONZERO_TOP) << _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE);
L_msb = 1;
} else {
// So, I cheated by inverting E on memory load (to match the rest of CHERI), which Morello does not do.
// This means parts of B and T are incorrectly inverted. So invert back again.
#ifdef CC_IS_MORELLO
pesbt ^= _CC_N(MEM_XOR_MASK);
#endif
result.E = 0;
L_msb = 0;
result.B = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_ZERO_BOTTOM);
Expand Down Expand Up @@ -554,13 +542,8 @@ static inline uint32_t _cc_N(compute_ebt)(_cc_addr_t req_base, _cc_length_t req_
// lostSignificantTop : bool = false;
// lostSignificantBase : bool = false;
// incE : bool = false;
uint32_t ebt_bits = _CC_ENCODE_EBT_FIELD(0, INTERNAL_EXPONENT) | _CC_ENCODE_EBT_FIELD(req_top, EXP_ZERO_TOP) |
uint32_t ebt_bits = _CC_N(ENCODE_IE)(false) | _CC_ENCODE_EBT_FIELD(req_top, EXP_ZERO_TOP) |
_CC_ENCODE_EBT_FIELD(req_base, EXP_ZERO_BOTTOM);
#ifdef CC_IS_MORELLO
// Due to morello conditionally inverting bits, we need to invert the bits that would be an internal exponent
// here
ebt_bits ^= _CC_ENCODE_EBT_FIELD(~0, EXPONENT_HIGH_PART) | _CC_ENCODE_EBT_FIELD(~0, EXPONENT_LOW_PART);
#endif
if (alignment_mask)
*alignment_mask = _CC_MAX_ADDR; // no adjustment to base required
*exact = true;
Expand Down Expand Up @@ -630,24 +613,13 @@ static inline uint32_t _cc_N(compute_ebt)(_cc_addr_t req_base, _cc_length_t req_
top_ie = _cc_N(truncate64)(top_ie + 1, _CC_BOT_INTERNAL_EXP_WIDTH);
}
}
//
// Bbits = B_ie @ 0b000;
// Tbits = T_ie @ 0b000;
const _cc_addr_t Bbits = bot_ie << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE);
const _cc_addr_t Tbits = top_ie << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE);
const uint8_t newE = E + (incE ? 1 : 0);

// };
// let exact = not(lostSignificantBase | lostSignificantTop);
*exact = !lostSignificantBase && !lostSignificantTop;
// Split E between T and B
const _cc_addr_t expHighBits =
_cc_N(getbits)(newE >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), 0, _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE));
const _cc_addr_t expLowBits = _cc_N(getbits)(newE, 0, _CC_N(FIELD_EXPONENT_LOW_PART_SIZE));
const _cc_addr_t Te = Tbits | expHighBits;
const _cc_addr_t Be = Bbits | expLowBits;
return _CC_ENCODE_EBT_FIELD(1, INTERNAL_EXPONENT) | _CC_ENCODE_EBT_FIELD(Te, TOP_ENCODED) |
_CC_ENCODE_EBT_FIELD(Be, BOTTOM_ENCODED);
// Split E between T and B, use the remaining bits to encode Bbits/TBits
const _cc_addr_t expBits = _CC_N(ENCODE_EXPONENT)(newE);
return expBits | _CC_N(ENCODE_IE)(true) | _CC_ENCODE_FIELD(top_ie, EXP_NONZERO_TOP) |
_CC_ENCODE_FIELD(bot_ie, EXP_NONZERO_BOTTOM);
}

static inline bool _cc_N(precise_is_representable_new_addr)(const _cc_cap_t* oldcap, _cc_addr_t new_cursor) {
Expand Down
9 changes: 8 additions & 1 deletion cheri_compressed_cap_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,18 @@
#define _CC_ENCODE_FIELD(value, name) \
((uint64_t)((value)&_CC_N(FIELD_##name##_MAX_VALUE)) << _CC_N(FIELD_##name##_START))

#define _CC_EXTRACT_FIELD(value, name) _cc_N(getbits)((value), _CC_N(FIELD_##name##_START), _CC_N(FIELD_##name##_SIZE))
#define _CC_EXTRACT_FIELD(pesbt, name) _cc_N(getbits)((pesbt), _CC_N(FIELD_##name##_START), _CC_N(FIELD_##name##_SIZE))

#define _CC_ENCODE_EBT_FIELD(value, name) \
((uint64_t)((value)&_CC_N(FIELD_##name##_MAX_VALUE)) << (_CC_N(FIELD_##name##_START) + _CC_N(FIELD_EBT_START)))

#define _CC_ENCODE_SPLIT_EXPONENT(E) \
_CC_ENCODE_EBT_FIELD((E) >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), EXPONENT_HIGH_PART) | \
_CC_ENCODE_EBT_FIELD(E, EXPONENT_LOW_PART)
#define _CC_EXTRACT_SPLIT_EXPONENT(pesbt) \
(_CC_EXTRACT_FIELD(pesbt, EXPONENT_LOW_PART) | \
(_CC_EXTRACT_FIELD(pesbt, EXPONENT_HIGH_PART) << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE)))

#define _CC_SPECIAL_OTYPE(name, val) \
_CC_N(name) = (_CC_N(SPECIAL_OTYPE_VAL)(val)), _CC_N(name##_SIGNED) = (_CC_N(SPECIAL_OTYPE_VAL_SIGNED)(val))

Expand Down
4 changes: 2 additions & 2 deletions test/random_inputs_test_128m.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include "random_inputs_test_common.cpp"

TEST_CASE("Assertion failure during set_addr found by fuzzer", "[fuzz]") {
uint64_t pesbt = 0xa98aa9a9ff7ea9a9;
uint64_t pesbt = 0xa98aa9a9bf79a9ae;
uint64_t cursor = 0xada9a9a9a9e9a9f4;
TestAPICC::cap_t cap = TestAPICC::decompress_raw(pesbt, cursor, false);
TestAPICC::cap_t cap = TestAPICC::decompress_mem(pesbt, cursor, false);
// NB: flag bits for base and top are not a sign-extensio of the 56-bit address.
uint64_t expected_base = 0x5350000000000000;
TestAPICC::length_t expected_top = _CC_MAX_TOP | 0xfef0000000000000;
Expand Down
3 changes: 1 addition & 2 deletions test/sail_wrapper_128m.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ static inline uint64_t extract_sail_cap_bits(sail_cap_bits* bits, uint64_t start

/* Exported API */
struct cc128m_bounds_bits sail_extract_bounds_bits_128m(uint64_t pesbt) {
// We have to XOR the pesbt bits here since the Morello sail model does not invert on load/store.
lbits sailcap = to_sail_cap(pesbt ^ CC128M_MEM_XOR_MASK, 0, false);
lbits sailcap = to_sail_cap(pesbt, 0, false);
struct cc128m_bounds_bits result = {.E = sailgen_CapGetExponent(sailcap),
.B = sailgen_CapGetBottom(sailcap),
.T = sailgen_CapGetTop(sailcap),
Expand Down
17 changes: 7 additions & 10 deletions test/sail_wrapper_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,7 @@ static lbits to_sail_cap(uint64_t mem_pesbt, uint64_t mem_cursor, bool tag) {
return capbits;
}

static lbits cap_t_to_sail_cap(const _cc_cap_t* c) {
return to_sail_cap(c->cr_pesbt ^ _CC_N(MEM_XOR_MASK), c->_cr_cursor, c->cr_tag);
}
static lbits cap_t_to_sail_cap(const _cc_cap_t* c) { return to_sail_cap(c->cr_pesbt, c->_cr_cursor, c->cr_tag); }

/* Exported API */
static _cc_cap_t from_sail_cap(const lbits* sail_cap) {
Expand Down Expand Up @@ -167,8 +165,7 @@ static _cc_cap_t from_sail_cap(const lbits* sail_cap) {
_cc_N(update_reserved)(&result, 0);
_cc_N(update_flags)(&result, 0);
_cc_N(update_uperms)(&result, 0);
// Morello sail does not include the XOR, so we have to apply it here to match the C compression library.
result.cr_pesbt = extract_bits(*sail_cap, 64, 64) ^ _CC_N(MEM_XOR_MASK);
result.cr_pesbt = extract_bits(*sail_cap, 64, 64);
return result;
}

Expand All @@ -179,9 +176,9 @@ _cc_cap_t _cc_sail_decode_mem(uint64_t mem_pesbt, uint64_t mem_cursor, bool tag)
return result;
}

_cc_cap_t _cc_sail_decode_raw(uint64_t mem_pesbt, uint64_t mem_cursor, bool tag) {
// Morello RAW has no mask. If this has been masked, undo it.
return _cc_sail_decode_mem(mem_pesbt ^ _CC_N(MEM_XOR_MASK), mem_cursor, tag);
_cc_cap_t _cc_sail_decode_raw(uint64_t pesbt, uint64_t cursor, bool tag) {
// There is no difference between raw and memory format for Morello
return _cc_sail_decode_mem(pesbt, cursor, tag);
}

uint64_t sail_compress_common_mem(const _cc_cap_t* csp) {
Expand All @@ -193,8 +190,8 @@ uint64_t sail_compress_common_mem(const _cc_cap_t* csp) {
}

uint64_t sail_compress_common_raw(const _cc_cap_t* csp) {
// Morello sail does not include the XOR, so we have to apply it here to match the C compression library.
return sail_compress_common_mem(csp) ^ _CC_N(MEM_XOR_MASK);
// There is no difference between raw and memory format for Morello
return sail_compress_common_mem(csp);
}

#else
Expand Down
3 changes: 2 additions & 1 deletion test/setbounds_test_128m.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ TEST_CASE("Fuzzer generated behaviour difference 1", "[fuzz]") {

TEST_CASE("Fuzzer incorrect exact result 1", "[fuzz]") {
TestAPICC::cap_t cap =
TestAPICC::decompress_raw(/*pesbt=*/0x27ff00007ff6fe00, /*cursor=*/0xfe00000000000000, /*tag=*/true);
TestAPICC::decompress_mem(/*pesbt=*/0x27ff00003ff1fe07, /*cursor=*/0xfe00000000000000, /*tag=*/true);
uint64_t req_len = 0x2300000000000000;
CHECK(cap.base() == 0xfe00000000000000);
CHECK(cap.top() == (_CC_MAX_TOP | 0x3ff0000000000000));
CHECK(cap.cr_exp == 48);
bool was_exact = false;
auto result = do_csetbounds<TestAPICC>(cap, &was_exact, req_len);
// Top is > MAX_TOP so tag should be cleared
Expand Down
Loading

0 comments on commit 9d5a656

Please sign in to comment.