Fix pointer provenance in SDL_SIMDRealloc

This is needed to support CHERI, and thus Arm's experimental Morello
prototype, where pointers are implemented using unforgeable capabilities
that include bounds and permissions metadata to provide fine-grained
spatial and referential memory safety, as well as revocation by sweeping
memory to provide heap temporal memory safety.

The C standard does not guarantee that if two pointers compare equal
they are the same pointer, as C pointers have a notion of provenance,
and compilers have been known to exploit this during optimisation. For
CHERI, this becomes even more important, as in-place expansion can
result in realloc returning a capability to the same address but with
increased capability bounds, and so reusing the old capability will trap
trying to access outside the bounds of the original allocation.

In the case that ptr == mem, memdiff and ptrdiff should still be equal,
so the only overhead is a small amount of pointer arithmetic and a store
of the new pointer (which is required per the C standard in order to not
be undefined behaviour when next loaded).

This also fixes the calculation of oldmem to use uintptr_t rather than
size_t as casting the pointer to size_t on CHERI will strip the
capability metadata, including the validity tag, with the subsequent
cast back to void * resulting in a null-derived capability whose
validity tag is clear and thus cannot be dereferenced without trapping.
This commit is contained in:
Jessica Clarke 2021-07-29 18:09:38 +01:00 committed by Sam Lantinga
parent 8f38ba4d68
commit 02daab8736

View file

@ -1087,9 +1087,6 @@ SDL_SIMDRealloc(void *mem, const size_t len)
ptr = (Uint8 *) SDL_realloc(mem, padded + alignment + sizeof (void *)); ptr = (Uint8 *) SDL_realloc(mem, padded + alignment + sizeof (void *));
if (ptr == mem) {
return retval; /* Pointer didn't change, nothing to do */
}
if (ptr == NULL) { if (ptr == NULL) {
return NULL; /* Out of memory, bail! */ return NULL; /* Out of memory, bail! */
} }
@ -1102,7 +1099,7 @@ SDL_SIMDRealloc(void *mem, const size_t len)
if (mem) { if (mem) {
ptrdiff = ((size_t) retval) - ((size_t) ptr); ptrdiff = ((size_t) retval) - ((size_t) ptr);
if (memdiff != ptrdiff) { /* Delta has changed, copy to new offset! */ if (memdiff != ptrdiff) { /* Delta has changed, copy to new offset! */
oldmem = (void*) (((size_t) ptr) + memdiff); oldmem = (void*) (((uintptr_t) ptr) + memdiff);
/* Even though the data past the old `len` is undefined, this is the /* Even though the data past the old `len` is undefined, this is the
* only length value we have, and it guarantees that we copy all the * only length value we have, and it guarantees that we copy all the