Skip to content

Commit

Permalink
Add a Capability::bounds().set_inexact_at_most() method (#310)
Browse files Browse the repository at this point in the history
This copies "the TLS stack buffer trick" into the base RTOS for broader
use. The implementation can be replaced with
[CSetBoundsRoundDown](CHERIoT-Platform/cheriot-sail#74)
if and when that lands in the ISA.
  • Loading branch information
nwf authored Oct 7, 2024
1 parent 78a5a21 commit 3ab6978
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 24 deletions.
98 changes: 74 additions & 24 deletions sdk/include/cheri.hh
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,30 @@ namespace CHERI
}
};

/**
* Rounds `len` up to a CHERI representable length for the current
* architecture.
*/
__always_inline inline size_t representable_length(size_t length)
{
return __builtin_cheri_round_representable_length(length);
}

/**
* Returns the alignment mask required for a given length.
*/
__always_inline inline size_t representable_alignment_mask(size_t length)
{
return __builtin_cheri_representable_alignment_mask(length);
}

/// Can the range [base, base + size) be precisely covered by a capability?
inline bool is_precise_range(ptraddr_t base, size_t size)
{
return (base & ~representable_alignment_mask(size)) == 0 &&
representable_length(size) == size;
}

/**
* Helper class for accessing capability properties on pointers.
*/
Expand Down Expand Up @@ -521,6 +545,56 @@ namespace CHERI
set(__builtin_cheri_bounds_set(ptr(), bounds));
return *this;
}

/**
* Set the bounds to `length` if `length` is representable with the
* current alignment of `buffer`. If not, then reduce `length` until
* it is representable. Unlike set_inexact(), the resulting base
* will always be the current address; that is, there will be no
* padding below the current address.
*
* See is_precise_range().
*/
BoundsProxy &set_inexact_at_most(size_t &bounds)
{
ptraddr_t alignmentMask = representable_alignment_mask(bounds);

ptraddr_t newBaseAddress =
static_cast<ptraddr_t>(reinterpret_cast<uintptr_t>(ptr()));

// If the new base address has bits set below the alignment
// requirement for representability of the requested length,
// then we need to do some bit-twiddling to find the largest
// length that is both not larger than the request and
// representable given the base address.
if ((newBaseAddress & alignmentMask) != newBaseAddress)
{
// The number of bits in CHERIoT's capability encoding's
// mantissa. This is part of the capability encoding and
// so, ideally, wouldn't be hard coded here.
static constexpr size_t MantissaBits = 9;

// The maximum possible representable length given the new
// base is a full mantissa width of 1s followed by 0s with
// its least significant 1 aligned to the least significant
// 1 in the base address.
size_t maximumLength = ((1 << MantissaBits) - 1)
<< __builtin_ctz(newBaseAddress);

// Ensure that the requested length is representable by
// making sure that it fits within a mantissa width,
// rounding down by dropping any lower bits. This is
// equivalent to masking by a mantissa width of 1s with its
// MSB aligned to the highest set bit in the requested
// length.
size_t alignedLength = bounds & alignmentMask;

// Select the smaller of those two lengths.
bounds = std::min<size_t>(alignedLength, maximumLength);
}
*this = bounds;
return *this;
}
};

/**
Expand Down Expand Up @@ -1019,30 +1093,6 @@ namespace CHERI
}
};

/**
* Rounds `len` up to a CHERI representable length for the current
* architecture.
*/
__always_inline inline size_t representable_length(size_t length)
{
return __builtin_cheri_round_representable_length(length);
}

/**
* Returns the alignment mask required for a given length.
*/
__always_inline inline size_t representable_alignment_mask(size_t length)
{
return __builtin_cheri_representable_alignment_mask(length);
}

/// Can the range [base, base + size) be precisely covered by a capability?
inline bool is_precise_range(ptraddr_t base, size_t size)
{
return (base & ~representable_alignment_mask(size)) == 0 &&
representable_length(size) == size;
}

/**
* Concept that matches pointers.
*/
Expand Down
32 changes: 32 additions & 0 deletions tests/misc-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,44 @@ void check_shared_object(const char *name,
permissions);
}

void check_capability_set_inexact_at_most()
{
void *p = malloc(2048);

debug_log("Test Capability::BoundsProxy::set_inexact_at_most with {}", p);

{
Capability<void> q = {p};
q.address() += 2; // misalign

size_t reqlen = 1024;
q.bounds().set_inexact_at_most(reqlen);
debug_log("Requesting 1024 at align 2 resulted in {}: {}", reqlen, q);
TEST(reqlen < 1024, "set_inexact_at_most failed to truncate");
TEST(q.length() == reqlen, "set_inexact_at_most failed to bound");
}

{
Capability<void> q = {p};
q.address() += 1; // misalign

size_t reqlen = 511;
q.bounds().set_inexact_at_most(reqlen);
debug_log("Requesting 511 at align 1 resulted in {}: {}", reqlen, q);
TEST(reqlen == 511, "set_inexact_at_most truncated unnecessarily");
TEST(q.length() == reqlen, "set_inexact_at_most failed to bound");
}

free(p);
}

int test_misc()
{
check_timeouts();
check_memchr();
check_memrchr();
check_pointer_utilities();
check_capability_set_inexact_at_most();
debug_log("Testing shared objects.");
check_shared_object("exampleK",
SHARED_OBJECT(void, exampleK),
Expand Down

0 comments on commit 3ab6978

Please sign in to comment.