The out-of-memory handling in modules 'string-buffer' and 'xstring-buffer'
is not consistent: An OOM situation that occurred while accumulating the
string via functions without 'x' causes sb_xdupfree_c to return NULL,
while an OOM during sb_xdupfree_c (that is, during the last step) causes
xalloc_die() to be invoked.

In other words, there is the assumption that if the caller uses sb_xdupfree_c,
he will also use sb_xappend1, *not* sb_append1.

With this patch, it becomes consistent without making this assumption:
OOM situations that occurred while accumulating the string in sb_append1
ultimately cause an xalloc_die() invocation when sb_xdupfree_c is invoked.


2025-02-09  Bruno Haible  <br...@clisp.org>

        string-buffer, string-buffer-reversed: Make OOM handling consistent.
        * lib/string-buffer.h (struct string_buffer): New field 'oom'.
        * lib/string-buffer.c (sb_init): Initialize the 'oom' field.
        (sb_append1, sb_append_desc, sb_append_c): Upon out-of-memory, set
        buffer->oom, not buffer->error.
        (sb_dupfree, sb_dupfree_c): If there was an OOM, return NULL.
        * lib/string-buffer-printf.c (sb_appendvf, sb_appendf): Upon
        out-of-memory, set buffer->oom, not buffer->error.
        * lib/string-buffer-reversed.h (struct string_buffer_reversed): New
        field 'oom'.
        * lib/string-buffer-reversed.c (sbr_init): Initialize the 'oom' field.
        (sbr_prepend1, sbr_prepend_desc, sbr_prepend_c): Upon out-of-memory, set
        buffer->oom, not buffer->error.
        (sbr_dupfree, sbr_dupfree_c): If there was an OOM, return NULL.
        * lib/string-buffer-reversed-printf.c (sbr_prependvf, sbr_prependf):
        Upon out-of-memory, set buffer->oom, not buffer->error.

diff --git a/lib/string-buffer-printf.c b/lib/string-buffer-printf.c
index e3a4d9b6da..ef9ad122e8 100644
--- a/lib/string-buffer-printf.c
+++ b/lib/string-buffer-printf.c
@@ -43,7 +43,7 @@ sb_appendvf (struct string_buffer *buffer, const char 
*formatstring,
     {
       if (sb_ensure_more_bytes (buffer, 64) < 0)
         {
-          buffer->error = true;
+          buffer->oom = true;
           errno = ENOMEM;
           return -1;
         }
@@ -58,7 +58,10 @@ sb_appendvf (struct string_buffer *buffer, const char 
*formatstring,
   if (ret < 0)
     {
       /* Failed.  errno is set.  */
-      buffer->error = true;
+      if (errno == ENOMEM)
+        buffer->oom = true;
+      else
+        buffer->error = true;
       ret = -1;
     }
   else
@@ -75,7 +78,7 @@ sb_appendvf (struct string_buffer *buffer, const char 
*formatstring,
              vsnzprintf() call.  */
           if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
             {
-              buffer->error = true;
+              buffer->oom = true;
               errno = ENOMEM;
               ret = -1;
             }
@@ -88,7 +91,10 @@ sb_appendvf (struct string_buffer *buffer, const char 
*formatstring,
               if (ret < 0)
                 {
                   /* Failed.  errno is set.  */
-                  buffer->error = true;
+                  if (errno == ENOMEM)
+                    buffer->oom = true;
+                  else
+                    buffer->error = true;
                   ret = -1;
                 }
               else
@@ -124,7 +130,7 @@ sb_appendf (struct string_buffer *buffer, const char 
*formatstring, ...)
     {
       if (sb_ensure_more_bytes (buffer, 64) < 0)
         {
-          buffer->error = true;
+          buffer->oom = true;
           errno = ENOMEM;
           return -1;
         }
@@ -139,7 +145,10 @@ sb_appendf (struct string_buffer *buffer, const char 
*formatstring, ...)
   if (ret < 0)
     {
       /* Failed.  errno is set.  */
-      buffer->error = true;
+      if (errno == ENOMEM)
+        buffer->oom = true;
+      else
+        buffer->error = true;
       ret = -1;
     }
   else
@@ -156,7 +165,7 @@ sb_appendf (struct string_buffer *buffer, const char 
*formatstring, ...)
              vsnzprintf() call.  */
           if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
             {
-              buffer->error = true;
+              buffer->oom = true;
               errno = ENOMEM;
               ret = -1;
             }
@@ -171,7 +180,10 @@ sb_appendf (struct string_buffer *buffer, const char 
*formatstring, ...)
               if (ret < 0)
                 {
                   /* Failed.  errno is set.  */
-                  buffer->error = true;
+                  if (errno == ENOMEM)
+                    buffer->oom = true;
+                  else
+                    buffer->error = true;
                   ret = -1;
                 }
               else
diff --git a/lib/string-buffer-reversed-printf.c 
b/lib/string-buffer-reversed-printf.c
index 76db571cf3..adcdc54bc3 100644
--- a/lib/string-buffer-reversed-printf.c
+++ b/lib/string-buffer-reversed-printf.c
@@ -45,7 +45,7 @@ sbr_prependvf (struct string_buffer_reversed *buffer, const 
char *formatstring,
     {
       if (sbr_ensure_more_bytes (buffer, 64) < 0)
         {
-          buffer->error = true;
+          buffer->oom = true;
           errno = ENOMEM;
           return -1;
         }
@@ -59,7 +59,10 @@ sbr_prependvf (struct string_buffer_reversed *buffer, const 
char *formatstring,
   if (ret < 0)
     {
       /* Failed.  errno is set.  */
-      buffer->error = true;
+      if (errno == ENOMEM)
+        buffer->oom = true;
+      else
+        buffer->error = true;
       ret = -1;
     }
   else
@@ -79,7 +82,7 @@ sbr_prependvf (struct string_buffer_reversed *buffer, const 
char *formatstring,
              vsnzprintf() call.  */
           if (sbr_ensure_more_bytes (buffer, (size_t) ret) < 0)
             {
-              buffer->error = true;
+              buffer->oom = true;
               errno = ENOMEM;
               ret = -1;
             }
@@ -91,7 +94,10 @@ sbr_prependvf (struct string_buffer_reversed *buffer, const 
char *formatstring,
               if (ret < 0)
                 {
                   /* Failed.  errno is set.  */
-                  buffer->error = true;
+                  if (errno == ENOMEM)
+                    buffer->oom = true;
+                  else
+                    buffer->error = true;
                   ret = -1;
                 }
               else
@@ -131,7 +137,7 @@ sbr_prependf (struct string_buffer_reversed *buffer, const 
char *formatstring,
     {
       if (sbr_ensure_more_bytes (buffer, 64) < 0)
         {
-          buffer->error = true;
+          buffer->oom = true;
           errno = ENOMEM;
           return -1;
         }
@@ -145,7 +151,10 @@ sbr_prependf (struct string_buffer_reversed *buffer, const 
char *formatstring,
   if (ret < 0)
     {
       /* Failed.  errno is set.  */
-      buffer->error = true;
+      if (errno == ENOMEM)
+        buffer->oom = true;
+      else
+        buffer->error = true;
       ret = -1;
     }
   else
@@ -165,7 +174,7 @@ sbr_prependf (struct string_buffer_reversed *buffer, const 
char *formatstring,
              vsnzprintf() call.  */
           if (sbr_ensure_more_bytes (buffer, (size_t) ret) < 0)
             {
-              buffer->error = true;
+              buffer->oom = true;
               errno = ENOMEM;
               ret = -1;
             }
@@ -179,7 +188,10 @@ sbr_prependf (struct string_buffer_reversed *buffer, const 
char *formatstring,
               if (ret < 0)
                 {
                   /* Failed.  errno is set.  */
-                  buffer->error = true;
+                  if (errno == ENOMEM)
+                    buffer->oom = true;
+                  else
+                    buffer->error = true;
                   ret = -1;
                 }
               else
diff --git a/lib/string-buffer-reversed.c b/lib/string-buffer-reversed.c
index a2bce83b9c..4f093f66dc 100644
--- a/lib/string-buffer-reversed.c
+++ b/lib/string-buffer-reversed.c
@@ -45,6 +45,7 @@ sbr_init (struct string_buffer_reversed *buffer)
   buffer->data[sizeof (buffer->space) - 1] = '\0';
   buffer->length = 1;
   buffer->allocated = sizeof (buffer->space);
+  buffer->oom = false;
   buffer->error = false;
 }
 
@@ -100,7 +101,7 @@ sbr_prepend1 (struct string_buffer_reversed *buffer, char c)
 {
   if (sbr_ensure_more_bytes (buffer, 1) < 0)
     {
-      buffer->error = true;
+      buffer->oom = true;
       return -1;
     }
   buffer->data[buffer->allocated - buffer->length - 1] = c;
@@ -114,7 +115,7 @@ sbr_prepend_desc (struct string_buffer_reversed *buffer, 
string_desc_t s)
   size_t len = sd_length (s);
   if (sbr_ensure_more_bytes (buffer, len) < 0)
     {
-      buffer->error = true;
+      buffer->oom = true;
       return -1;
     }
   memcpy (buffer->data + buffer->allocated - buffer->length - len, sd_data 
(s), len);
@@ -128,7 +129,7 @@ sbr_prepend_c (struct string_buffer_reversed *buffer, const 
char *str)
   size_t len = strlen (str);
   if (sbr_ensure_more_bytes (buffer, len) < 0)
     {
-      buffer->error = true;
+      buffer->oom = true;
       return -1;
     }
   memcpy (buffer->data + buffer->allocated - buffer->length - len, str, len);
@@ -159,7 +160,7 @@ sbr_contents_c (struct string_buffer_reversed *buffer)
 string_desc_t
 sbr_dupfree (struct string_buffer_reversed *buffer)
 {
-  if (buffer->error)
+  if (buffer->oom || buffer->error)
     goto fail;
 
   size_t length = buffer->length;
@@ -190,7 +191,7 @@ sbr_dupfree (struct string_buffer_reversed *buffer)
 char *
 sbr_dupfree_c (struct string_buffer_reversed *buffer)
 {
-  if (buffer->error)
+  if (buffer->oom || buffer->error)
     goto fail;
 
   size_t length = buffer->length;
diff --git a/lib/string-buffer-reversed.h b/lib/string-buffer-reversed.h
index 7fe05e4869..4915aa5297 100644
--- a/lib/string-buffer-reversed.h
+++ b/lib/string-buffer-reversed.h
@@ -43,7 +43,8 @@ struct string_buffer_reversed
   sbr_heap_allocated_pointer_t data;
   size_t length;     /* used bytes, <= allocated */
   size_t allocated;  /* allocated bytes */
-  bool error;        /* true if there was an error */
+  bool oom;          /* true if there was an out-of-memory error */
+  bool error;        /* true if there was an error other than out-of-memory */
   char space[1024];  /* stack allocated space */
 };
 
diff --git a/lib/string-buffer.c b/lib/string-buffer.c
index efb95adf6a..ac1606f23d 100644
--- a/lib/string-buffer.c
+++ b/lib/string-buffer.c
@@ -41,6 +41,7 @@ sb_init (struct string_buffer *buffer)
   buffer->data = buffer->space;
   buffer->length = 0;
   buffer->allocated = sizeof (buffer->space);
+  buffer->oom = false;
   buffer->error = false;
 }
 
@@ -91,7 +92,7 @@ sb_append1 (struct string_buffer *buffer, char c)
 {
   if (sb_ensure_more_bytes (buffer, 1) < 0)
     {
-      buffer->error = true;
+      buffer->oom = true;
       return -1;
     }
   buffer->data[buffer->length++] = c;
@@ -104,7 +105,7 @@ sb_append_desc (struct string_buffer *buffer, string_desc_t 
s)
   size_t len = sd_length (s);
   if (sb_ensure_more_bytes (buffer, len) < 0)
     {
-      buffer->error = true;
+      buffer->oom = true;
       return -1;
     }
   memcpy (buffer->data + buffer->length, sd_data (s), len);
@@ -118,7 +119,7 @@ sb_append_c (struct string_buffer *buffer, const char *str)
   size_t len = strlen (str);
   if (sb_ensure_more_bytes (buffer, len) < 0)
     {
-      buffer->error = true;
+      buffer->oom = true;
       return -1;
     }
   memcpy (buffer->data + buffer->length, str, len);
@@ -152,7 +153,7 @@ sb_contents_c (struct string_buffer *buffer)
 string_desc_t
 sb_dupfree (struct string_buffer *buffer)
 {
-  if (buffer->error)
+  if (buffer->oom || buffer->error)
     goto fail;
 
   size_t length = buffer->length;
@@ -185,7 +186,7 @@ sb_dupfree (struct string_buffer *buffer)
 char *
 sb_dupfree_c (struct string_buffer *buffer)
 {
-  if (buffer->error)
+  if (buffer->oom || buffer->error)
     goto fail;
 
   if (sb_ensure_more_bytes (buffer, 1) < 0)
diff --git a/lib/string-buffer.h b/lib/string-buffer.h
index 48c40eb3de..19b54ea6e7 100644
--- a/lib/string-buffer.h
+++ b/lib/string-buffer.h
@@ -42,7 +42,8 @@ struct string_buffer
   sb_heap_allocated_pointer_t data;
   size_t length;     /* used bytes, <= allocated */
   size_t allocated;  /* allocated bytes */
-  bool error;        /* true if there was an error */
+  bool oom;          /* true if there was an out-of-memory error */
+  bool error;        /* true if there was an error other than out-of-memory */
   char space[1024];  /* stack allocated space */
 };
 




Reply via email to