Skip to content

intcap_t arithmetic

Alexander Richardson edited this page Jun 22, 2021 · 2 revisions

In CHERI C/C++, provenance (i.e. capability bounds and tag bits) can only be inherited from one of the operands to an arithmetic operation. This can result in unexpected results if the provenance source cannot be inferred correctly. This can lead to missing capability metadata and tag bits which will generally result in a crash later on. For more details see Section 4.2.3 Single-origin provenance of the CHERI C/C++ Programming Guide

Commutative operations (addition and bitwise operations):

TODO: Add examples here.

See Section 6.2 Ambiguous provenance of the CHERI C/C++ Programming Guide and Section 3.6.1 Bitwise operations on capabilities in Complete spatial safety for C and C++ using CHERI capabilities

Subtraction

Subtraction is slightly different from other arithmetic operations since it can return valid capabilities where the expected result is an integer.

Consider for example:

uintptr_t example(uintptr_t lhs, uintptr_t rhs) {
    return lhs - rhs;
}

Depending on the inputs, the result of this function can either be a NULL-derived capability (if both inputs are NULL-derived) or a valid capability if lhs is a value derived from a pointer and rhs is an integer. In this function, Clang will use the left-hand-side of an expression as the provenance source since there the intended provenance source cannot be determined. Note: In many cases subtracting two valid uintptr_t capabilities will result in a untagged (but not NULL-derived) result since the difference (often a small integer) will be so far out-of-bounds, that it is no longer representable (see Section 4.3.5 Out-of-bounds pointers of the CHERI C/C++ Programming Guide

If the programmers intention is to return a pointer difference, casting both sides of the expression to char * or ptraddr_t will result in a NULL-derived result. If the intended behaviour is pointer arithmetic, cast the right-hand-side to a non-provenance-carrying integer type (e.g. ptrdiff_t).

uintptr_t pointer_diff_1(uintptr_t lhs, uintptr_t rhs) {
    return (ptraddr_t)lhs - (ptraddr_t)rhs;
}
uintptr_t pointer_diff_2(uintptr_t lhs, uintptr_t rhs) {
    return (char *)lhs - (char *)rhs;
}
uintptr_t lhs_relative_1(uintptr_t lhs, uintptr_t rhs) {
    return lhs - (ptrdiff_t)rhs;
}
uintptr_t lhs_relative_2(uintptr_t lhs, ptrdiff_t rhs) {
    return lhs - rhs;
}