Problem discovered on cfarm240 (CheriBSD 15.0) when building GNU Tar with ‘./configure 'CC=cc -march=morello -mabi=purecap'’. The problem is that using cheri_bounds_set on a pointer derived from posix_memalign, and later passing that pointer to ‘free’, corrupts the heap and can cause a later ‘free (NULL)’ to crash. Work around this CHERI bug via simplifying the workaround for the size-zero allocation glitch. The following changes take effect only if __CHERI_PURE_CAPABILITY__ is defined. * lib/alignalloc.h (alignalloc): * lib/eealloc.h (eemalloc): * lib/realloc.c (rpl_realloc): Do not add 1 to size if the size is zero, and do not call cheri_bounds_set on the result. Do not include <cheri.h>. --- ChangeLog | 17 +++++++++++++++++ lib/alignalloc.h | 18 +++++++++--------- lib/eealloc.h | 36 ++++++++++++++++-------------------- lib/realloc.c | 9 --------- 4 files changed, 42 insertions(+), 38 deletions(-)
diff --git a/ChangeLog b/ChangeLog index e0b841eb3d..18b17897db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2026-08-20 Paul Eggert <[email protected]> + + alignalloc: work around CHERI ‘free’ bug + Problem discovered on cfarm240 (CheriBSD 15.0) when building + GNU Tar with ‘./configure 'CC=cc -march=morello -mabi=purecap'’. + The problem is that using cheri_bounds_set on a pointer derived + from posix_memalign, and later passing that pointer to ‘free’, + corrupts the heap and can cause a later ‘free (NULL)’ to crash. + Work around this CHERI bug via simplifying the workaround + for the size-zero allocation glitch. The following changes take + effect only if __CHERI_PURE_CAPABILITY__ is defined. + * lib/alignalloc.h (alignalloc): + * lib/eealloc.h (eemalloc): + * lib/realloc.c (rpl_realloc): + Do not add 1 to size if the size is zero, and do not call + cheri_bounds_set on the result. Do not include <cheri.h>. + 2026-08-20 Bruno Haible <[email protected]> physmem: Clarify relation with getrlimit(). diff --git a/lib/alignalloc.h b/lib/alignalloc.h index b0f3f607cd..f09cd26141 100644 --- a/lib/alignalloc.h +++ b/lib/alignalloc.h @@ -29,9 +29,6 @@ #include <errno.h> #include <stdlib.h> #include "idx.h" -#if defined __CHERI_PURE_CAPABILITY__ -# include <cheri.h> -#endif _GL_INLINE_HEADER_BEGIN #ifndef ALIGNALLOC_INLINE @@ -100,13 +97,16 @@ alignalloc (idx_t alignment, idx_t size) if (alignment < sizeof (void *)) alignment = sizeof (void *); void *ptr = NULL; - /* Work around posix_memalign glitch by treating a 0 size as if it were 1, - so that returning NULL is equivalent to failing. */ - errno = posix_memalign (&ptr, alignment, size ? size : 1); -# if defined __CHERI_PURE_CAPABILITY__ - if (ptr != NULL) - ptr = cheri_bounds_set (ptr, size); + + /* Work around POSIX allocator glitch by treating a 0 size as if it were 1, + so that returning NULL is equivalent to failing. Skip this workaround + on CHERI, though, as it yields non-NULL anyway and adding 1 would + cause it to yield a too-generous (dereferencable) pointer. */ +# ifndef __CHERI_PURE_CAPABILITY__ + size |= !size; # endif + + errno = posix_memalign (&ptr, alignment, size); return ptr; # endif } diff --git a/lib/eealloc.h b/lib/eealloc.h index 62692f0688..b8cd86c4de 100644 --- a/lib/eealloc.h +++ b/lib/eealloc.h @@ -46,9 +46,6 @@ #endif #include <stdlib.h> -#if defined __CHERI_PURE_CAPABILITY__ -# include <cheri.h> -#endif _GL_INLINE_HEADER_BEGIN #ifndef EEALLOC_INLINE @@ -69,16 +66,15 @@ EEALLOC_INLINE void *eemalloc (size_t n) EEALLOC_INLINE void * eemalloc (size_t n) { - /* If n is zero, allocate a 1-byte block. */ - size_t nx = n; - if (n == 0) - nx = 1; - void *ptr = malloc (nx); -# if defined __CHERI_PURE_CAPABILITY__ - if (ptr != NULL) - ptr = cheri_bounds_set (ptr, n); + /* Work around POSIX allocator glitch by treating a 0 size as if it were 1, + so that returning NULL is equivalent to failing. Skip this workaround + on CHERI, though, as it yields non-NULL anyway and adding 1 would + cause it to yield a too-generous (dereferencable) pointer. */ +# ifndef __CHERI_PURE_CAPABILITY__ + n |= !n; # endif - return ptr; + + return malloc (n); } #endif @@ -90,15 +86,15 @@ EEALLOC_INLINE void *eerealloc (void *p, size_t n) EEALLOC_INLINE void * eerealloc (void *p, size_t n) { - /* Work around realloc glitch by treating a 0 size as if it were 1, - to avoid undefined behavior in strict C23 platforms, - and so that returning NULL is equivalent to failing. */ - void *ptr = realloc (p, n ? n : 1); -# if defined __CHERI_PURE_CAPABILITY__ - if (ptr != NULL) - ptr = cheri_bounds_set (ptr, n); + /* Work around POSIX allocator glitch by treating a 0 size as if it were 1, + so that returning NULL is equivalent to failing. Skip this workaround + on CHERI, though, as it yields non-NULL anyway and adding 1 would + cause it to yield a too-generous (dereferencable) pointer. */ +# ifndef __CHERI_PURE_CAPABILITY__ + n |= !n; # endif - return ptr; + + return realloc (p, n); } #endif diff --git a/lib/realloc.c b/lib/realloc.c index 9b35c23510..0b42582c64 100644 --- a/lib/realloc.c +++ b/lib/realloc.c @@ -28,10 +28,6 @@ #include <errno.h> #include <stdckdint.h> -#ifdef __CHERI_PURE_CAPABILITY__ -# include <cheri.h> -#endif - #ifndef _GL_INLINE_RPL_REALLOC /* Change the size of an allocated block of memory P to N bytes, @@ -99,11 +95,6 @@ rpl_realloc (void *p, size_t n) errno = ENOMEM; # endif -# ifdef __CHERI_PURE_CAPABILITY__ - if (result != NULL) - result = cheri_bounds_set (result, n); -# endif - return result; } -- 2.55.0
