Skip to content

Warning description

Irina Dudina edited this page Apr 1, 2024 · 3 revisions

Pointer Alignment

1. Cast increases required alignment to capability alignment

Not capability-aligned pointer cast to pointer to capability-aligned type.

Example:

typedef struct S_t {
  /* other fields */
  double f[1];  // aligned to 8-byte boundary
} S;

void foo(int n) {
  S* p = malloc(sizeof(S) + (n-1)*sizeof(double) + n*sizeof(T*));

  /* 1. &p->f[n] is 8-bytes aligned
   * 2. T** type requires 16-byte alignment
   */
  T** t = (T**)&p->f[n];
}

Warning message:

Pointer value aligned to a 8 byte boundary cast to type 'T * __capability * __capability' with 16-byte capability alignment

2. Not capability-aligned pointer used as capability storage

Underaligned pointer value assigned to a pointer that must be capability-aligned.

Example:

uint8_t extra[100]; // aligned to 1-byte boundary

void alloc(void** pp) {
    *pp = &extra[0]; // assign
}

intptr_t *foo(void) {
  intptr_t *cp; // requires 16-byte alignment
  alloc((void**)&cp); // cp initialised with underaligned value
  return cp;
}

Warning message:

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

3. Not capability-aligned pointer stored as 'void*'

This checker assumes that void* fields and global variables are intended to hold pointers to arbitrary data, potentially including capability-containing objects. Assigning an address of an underaligned allocation to such variable is reported (unless this allocation is already initialized).

Example:

struct S {
  void *p; // *p may be used to hold capabilities
};

uint8_t extra[100]; // aligned to 1-byte boundary

void foo(struct S *pS) {
  pS->p = &extra[0]; // assigning underaligned allocation to void* field
}

Warning message:

Pointer value aligned to a 1 byte boundary stored as value of type 'void * __capability'. Memory pointed by it may be used to hold capabilities, for which 16-byte capability alignment will be required

4. Copying capabilities through underaligned memory

Example:

uint8_t extra[100]; // aligned to 1-byte boundary

void foo(intptr_t *pCap, int n) {
  memcpy(extra, pCap, n * sizeof(intptr_t));
}

Warning message:

Copied memory object pointed by 'intptr_t * __capability' pointer contains capabilities that require 16-byte capability alignment. Destination address alignment is 1. Storing a capability at an underaligned address leads to tag stripping.

Pointers as Integers

1. NULL-derived capability used as pointer

Example:

char *foo(char *a, unsigned x) {
  intptr_t ip = ((ptrdiff_t)a | (intptr_t)x); // ip derived from x
  char *p = (char*) ip; // p is not a valid pointer
  return p;
}

Warning message:

NULL-derived capability used as pointer

2. cheri_no_provenance capability used as pointer

Similar to previous warning, but with immediate integer->(u)intptr_t->pointer conversion. This is usually intentional.

Example:

void* foo(unsigned x) {
  void *q = (void*)(uintptr_t)x; // q is not a valid pointer
}

Warning message:

cheri_no_provenance capability used as pointer

3. CHERI-incompatible pointer arithmetic

Example:

intptr_t cmp1 (char *e0, char *e1) {
  intptr_t x = (intptr_t)e0 ^ (intptr_t)e1; // single-provenance rule violated
  return x & (cmp(e0, e1) >> 31); // x is not a valid capability
}

Clang warning:

binary expression on capability types 'uintptr_t' (aka 'unsigned __intcap') and 'uintptr_t'; it is not clear which should be used as the source of provenance; currently provenance is inherited from the left-hand side

CSA-warning:

Result of '^' on capability type 'unsigned __intcap'; it is unclear which side should be used as the source of provenance. Note: along this path, LHS and RHS were derived from pointers. Result capability will be derived from LHS by default. This code may need to be rewritten for CHERI.

4. Capability derived from wrong argument

Example:

uintptr_t foo(unsigned d, char *p) {
  uintptr_t a = d; // NULL-derived
  uintptr_t b = (uintptr_t)p;

  uintptr_t s = a + b; // s is derived from a
  return s;
}

Clang warning:

binary expression on capability types 'uintptr_t' (aka 'unsigned __intcap') and 'uintptr_t'; it is not clear which should be used as the source of provenance; currently provenance is inherited from the left-hand side

CSA-warning:

Result of '+' on capability type 'unsigned __intcap'; it is unclear which side should be used as the source of provenance. Note: along this path, LHS was derived from NULL, RHS was derived from pointer. Currently, provenance is inherited from LHS, therefore result capability will be invalid

5. Binary operation with ambiguous provenance

Example:

uintptr_t align_down(void *p, size_t a) {
    uintptr_t sz = (uintptr_t)p;
    uintptr_t m = a - 1;
    return (sz & ~m);
}

Clang warning:

binary expression on capability types 'uintptr_t' (aka 'unsigned __intcap') and 'uintptr_t'; it is not clear which should be used as the source of provenance; currently provenance is inherited from the left-hand side

CSA-warning:

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, LHS was derived from pointer, RHS was derived from NULL

Capability Copy

1. Tag-stripping copy of capability

Capability-unaware implementation of data copying routines.

Example:

static void swapfunc(void *a, void *b, int n) {
  long i = n;
  char *pi = (char *)(a);
  char *pj = (char *)(b);
  /* if *a or *b is a capability, it will lose the tag after swap */
  do {
    char t = *pi;
    *pi++ = *pj;
    *pj++ = t;
  } while (--i > 0);
}

Warning message:

Tag-stripping store of a capability

2. Part of capability value used in binary operator

Individual bytes of a capability are used in arithmetic. This may be an indication of using pointer values to compute hash value or for the similar purposes. It's recommended to extract address value from a capability and use it instead for the sake of performance.

Example:

int hash(void *key0, size_t len) {
  char *k = key0;
  int h = 0;
  while (len--)
    h = (h << 5) + *k++; 
  return h; 
}

int ptr_hash(void) {
  int *p = &x;
  return hash(&p, sizeof(int*));
}

Warning message:

Part of capability representation used as argument in binary operator