[PATCH] alignalloc: work around CHERI ‘ free’ bug

Paul Eggert <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.