-
Notifications
You must be signed in to change notification settings - Fork 42
Unaligned capability copies
Copying a capability to an unaligned destination will result in tag bits being stripped since they are only retained when using copies with csc
/clc
instructions.
Diagnosed function calls: memcpy()
, memmove()
, __builtin_memcpy()
and __builtin_memmove()
There are two issues that need to be considered here:
For example the following code will sometimes result in buffer
not containing the tag bit from value
:
void test(uintcap_t value) {
char buffer[sizeof(uintcap_t)];
memcpy(buffer, &value, sizeof(value));
do_something(buffer);
}
If buffer
happens to be aligned to alignof(void*__capability)
(i.e. 16 bytes or 32 for CHERI256) at runtime then a call to memcpy()
will
result in the tag bits being copied because memcpy()
will use csc
/clc
. However, if it is only aligned to four or eight bytes, then memcpy()
will end up using clw
or cld
instructions and produce a byte-by-byte indetical copy of the capability but without the tag bit set.
If you use memcpy() with a source type containing capability to copy to a buffer that the compiler cannot statically prove to be at least capability aligned, the compiler will not be able to expand the memcpy()
to a sequence of loads and stores (even for a single capability-size copy).
This extra function call can cause performance degradation, however, emitting the clc
/csc
unconditionally could result in traps at runtime.
As memcpy()
must work with unaligned data, this is not an option and we instead warn for potentially tag-stripping copies.
void* my_copy(void* dst, void* src) {
// The following could be expanded inline by the compiler and strip tags.
// This is possible since the size is a constant and small enough to be
// expanded in a few instructions.
return memcpy(dst, src, sizeof(uincap_t));
}
Note: we could diagnose copies with a void*
source and assume these might contain capabilities but that would
almost certainly be an extremely noisy warning.