Skip to content

CHERI CSA Checkers

Irina Dudina edited this page Jun 28, 2024 · 7 revisions

Table of Content

Pointer Alignment

optin.portability.PointerAlignment

Reports

  • Pointer casts of underaligned values to types with strict alignment requirements
  • Not capability-aligned pointer (potentially) used as capability storage
  • Copying capabilities through underaligned memory

See CHERI C/C++ Programming Guide §4.2.2.

1. Pointer casts

Special case for CHERI: casts to pointer to capability. Storing capabilities at not capability-aligned addressed will result in stored capability losing its tag.

double a[2048];
void** foo(void) {
  char *p0 = (char*)a;
  // Pointer p0 is aligned to a 8 byte boundary;
  // type 'void **' requires capability alignment (16 bytes)
  return (void**)p0; 
}

Currently, this checker (deliberately) does not take into account:

  • alignment of static and automatic allocations, enforced by current ABI (other than implied by variable types),
  • alignment of fields that is guaranteed implicitly by the alignment of the whole object and current field offset.

Therefore, a variable or field may always be correctly aligned at runtime due to current ABI restrictions or structure layout, but still reported by the checker. This is not considered a False Positive, as relying on this could make the code less portable and easier to break in the future. Specifying expected alignment explicitly is recommended for this case.

Example:

#define N 100
struct S { // aligned as (void*)
  int a[N];
  
  // offsetof(struct S, f) == 400  =>  field f is always 16-byte aligned
  char f[N]; /* Warn: Original allocation of type 'char [100]' which has an alignment requirement 1 bytes */

  void *p;
} s;

/* Warn: Pointer value aligned to a 1 byte boundary cast to type 'uintptr_t * __capability' with required capability alignment 16 bytes*/
uintptr_t *t = (uintptr_t*)&s.f;
BugType Category Notes
Cast increases required alignment Type Error
Cast increases required alignment to capability alignment CHERI portability Special case for CHERI

2. Not capability-aligned pointer used as capability storage

Example:

char extra[100]; // Aligned to 1 byte
void alloc(void** p) {
  // Warn: Pointer value aligned to a 1 byte boundary stored as value 
  // of type 'intptr_t * __capability'. Memory pointed by it is 
  // supposed to hold capabilities, for which 16-byte capability alignment
  // will be required
  *p = extra;
}

intptr_t *test_assign(void) {
  intptr_t *cp;
  alloc((void**)&cp); // call
  return cp;
}
BugType Category Notes
Not capability-aligned pointer used as capability storage CHERI portability
Not capability-aligned pointer stored as void* CHERI portability Assuming void* can be used as capability storage

3. Copying capabilities through underaligned memory

Example:

char a[100]; // Aligned to 1 byte
void foo(intptr_t *pCap, size_t n) {
  // Warn: Destination memory object pointed by 'intptr_t * __capability'
  // pointer is supposed to contain capabilities that require 16-byte
  // capability alignment. Source address alignment is 1, which means that
  // copied object may have its capabilities tags stripped earlier 
  // due to underaligned storage
  memcpy(pCap, a, n);
}
BugType Category Notes
Copying capabilities through underaligned memory CHERI portability

Provenance Source

cheri.ProvenanceSource

  • Tracks integers and pointers stored as (u)intptr_t type
  • Fires a warning for (u)intptr_t binary operations with ambiguous provenance source (same as clang) and tells which side carries (or not) the provenance along the path
  • Fires a warning when the (u)intptr_t value obtained from the ambiguous-provenance-operation is cast to pointer type
  • Fires a warning when NULL-derived (u)intptr_t capability is cast to pointer type

See CHERI C/C++ Programming Guide §4.2.3.

Reported Bug Types

BugType Notes
CHERI-incompatible pointer arithmetic RHS and LHS are (u)intptr_t values derived from pointers
Capability derived from wrong argument RHS is a (u)intptr_t value derived from pointer (LHS is NULL-derived or unknown)
Binary operation with ambiguous provenance RHS is NULL-derived or unknown
NULL-derived capability: loss of provenance (u)intptr_t value derived from NULL cast to pointer type
NULL-derived capability used as pointer Pointer passed through non‑capability integer type
Capability with ambiguous provenance used as pointer Result of an (u)intptr_t binary operation with ambiguous provenance cast to pointer type

Options

Option Type Default Description
ShowFixIts bool false Enable fix-it hints for this checker
ReportForAmbiguousProvenance bool true Report for binary operations with ambiguous provenance for which the default capability derivation from LHS is fine. Automatically set to false if [-Wcheri-provenance] compiler warning is disabled.

Example:

-analyzer-config cheri.ProvenanceSource:ShowFixIts=true

Capability Copy

cheri.CapabilityCopy

  • Detects tag-stripping loads and stores that may be used to copy or swap capabilities.
  • Detects capability representation bytes being used in binary operator (e.g. as the input to a hash function or similar computations).
BugType Notes
Tag-stripping copy of capability CHERI C/C++ Programming Guide §4.2
Part of capability value used in binary operator CHERI C/C++ Programming Guide §4.6

Example:

void memcpy_impl(void* src0, void *dst0, size_t len) {
  char *src = src0;
  char *dst = dst0;

  while (len--)
    *dst++ = *src++; // Tag-stripping store of a capability
}

Options

Option Type Default Description
ReportForCharPtr bool true Report tag-stripping copy for char* function parameters. Suppression of warnings for C-strings is currently used to reduce the number of false alarms, but it's not very reliable

Example:

-analyzer-config cheri.CapabilityCopy:ReportForCharPtr=true

Pointer Size Assumptions

cheri.PointerSizeAssumptions

This checker detects code where the pointer size was checked against constant, but the case of capabilities was not addressed explicitly.

Example:

long i64;
void *p;
// This code fails to consider the case of 128-bit pointers
if (sizeof(i64) == sizeof(p)) { 
  // [...]
} else {
  // [...]  
}
BugType Notes
Only a limited number of pointer sizes checked AST-checker

Subobject Representability

cheri.SubObjectRepresentability

This checker detects record fields that will not have precise bounds when compiled with -cheri-bounds=subobject-safe due to big size and underaligned offset, as narrowed capability will not be representable.

See CHERI C/C++ Programming Guide §4.3.3, §7.5.

Example:

struct rte_lpm6 {
  struct rte_lpm6_external ext;	// 32/32 bytes exposed (may expose capability!)
  char name[RTE_LPM6_NAMESIZE]; // 32/32 bytes exposed
  uint32_t max_rules;    // 32/32 bytes exposed
  uint32_t used_rules;   //  4/4 bytes exposed
  uint32_t number_tbl8s; //  4/4 bytes exposed
  // Warn: Field 'tbl24' of type 'struct rte_lpm6_tbl_entry[16777216]'
  // (size 67108864) requires 32768 byte alignment for precise bounds;
  // field offset is 128 (aligned to 128); Current bounds: 0-67141632
  struct rte_lpm6_tbl_entry tbl24[RTE_LPM6_TBL24_NUM_ENTRIES]
  ...
BugType Notes
Address taken of a field with imprecise subobject bounds
Field with imprecise subobject bounds superset for previous warning

Allocation

alpha.cheri.Allocation

This checker detects unrelated objects being allocated as adjacent suballocations of the bigger memory allocation. It reports situations when an address of such object escapes the function and suggests narrowing the bounds of the escaping capability, so it covers only the current suballocation, following the principle of least privilege.

See CHERI C/C++ Programming Guide §7.3

Example:

struct S2 * foo(int n1, int n2) {
  struct S1 *p1 = malloc(sizeof(struct S1)*n1 + sizeof(struct S2)*n2);
  struct S2 *p2 = (struct S2 *)(p1+n1);
  // Warn: Pointer to suballocation returned from function
  // (consider narrowing the bounds for suballocation)
  return p2;
}
BugType Notes
Allocation partitioning static, stack, heap allocations
Unknown allocation partitioning untracked allocations
Option Type Default Description
ReportForUnknownAllocations bool true Report for unknown allocations