Static Analysis for CHERI
Fork of Clang Static Analyzer for detecting portability issues in C/C++ on CHERI
Porting to CHERI for many projects is relatively easy and requires minimal changes to the source code. Most of the incompatibility issues are detected at compile time, and compiler warnings are usually very helpful to understand and fix the problem.
However, some issues, especially in low-level code, are not flagged by the compiler and only surface at runtime, hopefully during testing. In this case, the chances of uncovering the issue depend on the test coverage of the project.
A static analysis tool could be helpful to increase the number of incompatibility issues detected at the early stage of the porting process, get an insight into the root cause of the problem, and a suggestion on how this might be fixed.
CHERI CSA tool is based on a Clang Static Analyzer and provides a couple of useful checkers to detect common issues missed by the compiler. It’s just a start, and more checkers are to be added in the future.

Illustration created in NightCafe Studio by AI
Clang Static Analyzer (CSA)
CSA performs inter-procedural path-sensitive analysis in the boundary of one translation unit. It means that it analyses many possible paths in the function one at a time, also looking into called functions. Therefore it could detect problems that arise only in specific scenarios (on a particular path) and track inter-procedural dependencies.
Checkers
Provenance Source Checker
Consider a binary arithmetic C-expression with operands of type intptr_t
. The result of this expression is a capability that should be derived according to the operator’s semantics from one of the sides of the expression. Unless one of the arguments is a cast from a non-capability (integer) type, the compiler will not know which side should the resulting capability be derived from and will emit a warning. To fix this warning, one should explicitly tell the compiler what side to pick by casting the other argument to a non-capability type. For more details, see §6.2 of CHERI C/C++ Programming Guide [1].
Provenance Source Checker is designed to help with resolving such warnings. It tracks integers and pointers stored as (u)intptr_t
type and then ,at the binary operation with “ambiguous provenance”, tells which side could be a valid capability on the considered path. For example, consider a code snippet from the compiler-rt project below.
static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { ... uintptr_t result = 0; switch (encoding & 0x0F) { case DW_EH_PE_sdata8: // Warning note: // NULL-derived capability: implicit cast from 'int64_t' // to 'unsigned __intcap __attribute__((cheri_no_provenance))' result = *((const int64_t *)p); } ... return result; } uintptr_t funcStart = // ... uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding); // Warning message from CSA: // Result of '+' on capability type 'unsigned __intcap'; it is unclear which side should be used // as the source of provenance; consider indicating the provenance-carrying argument explicitly // by casting the other argument to 'size_t'. Note: along this path, RHS was derived from NULL _Unwind_SetIP(context, (funcStart + landingPad));
Ambiguous provenance warning in newlib
As long as funcStart
and landingPad
variables have type uintptr_t
, the compiler will emit a warning for funcStart + landingPad
expression. CSA can additionally track the origin of the values of those variables, and it was able to see inside readEncodedPointer
function, therefore suggested that landingPad
variable may hold a NULL-derived capability. During porting of this project, the type of this variable was changed to size_t
.
Additionally, Provenance Source Checker fires a warning when the (u)intptr_t
value obtained from the ambiguous-provenance operation is cast to the pointer type and also reports NULL
-derived (u)intptr_t
capabilities that are being cast to the pointer type. See the warning emitted by this checker from the newlib library below.
BUFHEAD *bp; ... // Warning message: // Invalid capability is used as pointer segp[segment_ndx] = // Warning note: // Result of '|' is a NULL-derived capability (BUFHEAD *)((ptrdiff_t)bp | (intptr_t)is_disk_mask);
Invalid capability warning in newlib
Variable bp
here was cast to the non-capability type ptrdiff_t
, therefore the result of the (ptrdiff_t)bp | (intptr_t)is_disk_mask
expression is a NULL-derived capability, that is later used as a pointer. During porting of this project, (ptrdiff_t)
cast was replaces with (intptr_t)
cast.
Capability Copy Checker
Whenever there is a need to copy or move (swap) a capability, it must be copied as a whole, otherwise, the copy will lose the tag and will not be a valid capability. Therefore, any routines that could potentially move around capabilities (memcpy
, realloc
, qsort
, etc.) should be aware of this requirement. See §4.2 of CHERI C/C++ Programming Guide [1] for more details.
Capability Copy Checker is aimed to detect functions that move around arbitrary data in a way that could potentially result in a tag-stripping capability copying. For example, an original implementation of memcpy
function in the newlib would copy capabilities by long
-sized chunks, which could lead to having an invalid capability in dst
. Capability Copy Checker reports such issues.
#define UNALIGNED(X, Y) \ (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) #define BIGBLOCKSIZE (sizeof (long) << 2) void * memcpy (void *dst0, const void *src0, size_t len0) { // Warning note: // void* argument may be a pointer to capability char *dst = dst0;const char *src = src0; if (len0 >= BIGBLOCKSIZE && !UNALIGNED (src, dst)) { long *aligned_dst = (long*)dst; long *aligned_src = (long*)src; while (len0 >= BIGBLOCKSIZE) { // Warning message: // Tag-stripping store of a capability *aligned_dst++ = *aligned_src++; *aligned_dst++ = *aligned_src++; *aligned_dst++ = *aligned_src++; *aligned_dst++ = *aligned_src++; len0 -= BIGBLOCKSIZE; } } return dst0; }
Tag-stripping capability copy warning in newlib
Using the analyzer
Source code and instructions on how to build and run the analyzer can be found in GitHub repo.
Comments are closed
Comments to this thread have been closed by the post author or by an administrator.