On 3/18/24 12:41, Chet Ramey wrote:
I'm not sure what you're using, but that was not my experience on
macOS.

I am using Fedora 39 (the current version) on x86-64. That could explain our differing experiences.

I see several diagnostics (mostly diff output) with "make check" on Fedora 39. The diagnostics can vary from run to run, i.e., they aren't necessarily reproducible. I assumed these were OK because I got 'em before any patches. From my point of view the diff output was sort of random, so I eyeballed it and guessed which outputs mattered and which didn't. Apparently I guessed incorrectly with fw.

At some point I suppose it'd be nice if 'make check' succeeded (exit status 0) or failed (nonzero exit status) so that it is easy for non-experts to tell which diagnostics matter; that's what many other packages do. For now I'd rather focus on the integer overflow issues in Bash, while they're fresh in my mind.


First, the patched version doesn't build on macOS because your patches
don't include <stdbool.h>. Once you get past that, printf goes into an
infinite loop on

printf -v s "%b" ""

in printstr because `fw' is used unititialized (and randomly set to some
ridiculously large value). That and the old test's incorrect expectation
that a field width of 9223372036854775825 would always overflow to -1 (a
left-adjusted field width of 1) instead of being flagged as overflow are
the UB I was talking about.

Yes, I see now. I didn't get that behavior on Fedora, perhaps because the junk in fw was benign there.

Perhaps at some point we could enable more of GCC's static checking to catch silly mistakes like that. Again, a task for another time.


It should be mostly there in the changes I pushed today, once I made it
through the above.

Thanks, I checked the devel branch against what I submitted, found a few errors, and while reviewing all this found and fixed a few other integer-overflow issues in Bash. Proposed patches attached, in "git format-patch" format so you can use "git am" on them. If there's some reason a patch shouldn't be applied please let me know so that I can stop worrying about that subissue.
From ccf906084c3aaf7cd75f0ee1e035986af7d58a82 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 18 Mar 2024 13:44:27 -0700
Subject: [PATCH 01/10] Improve use of HAVE_C_BOOL
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* bashansi.h: If HAVE_C_BOOL, bool works as per C23, so
we needn’t include <stdbool.h> or define any workarounds.
In pre-C99 compilers arrange for ‘false’ and ‘true’ as well;
although Bash doesn't use either symbol it's safer to
be compatible in case some system .h file unwisely uses it.
---
 bashansi.h | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/bashansi.h b/bashansi.h
index 4feaadfb..0bac61b4 100644
--- a/bashansi.h
+++ b/bashansi.h
@@ -35,13 +35,15 @@
 #  include "ansi_stdlib.h"
 #endif /* !HAVE_STDLIB_H */
 
-/* Prefer stdbool.h if we have it, maybe have to rethink this later */
-#if defined (HAVE_STDBOOL_H)
-#  include <stdbool.h>
-#else
-#  ifndef HAVE_C_BOOL
+/* If bool is not builtin, prefer stdbool.h if we have it. */
+#ifndef HAVE_C_BOOL
+#  ifdef HAVE_STDBOOL_H
+#    include <stdbool.h>
+#  else
 #    undef bool
 typedef unsigned char bool;
+#    define false 0
+#    define true 1
 #  endif
 #endif
 
-- 
2.44.0

From aeb21592ed88cac5072bcd41b2c478766d6e1dee Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 18 Mar 2024 13:54:29 -0700
Subject: [PATCH 02/10] Minor mkseq clarification/tuning
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* braces.c (mkseq): All branches of an if end in ‘result[i++] =
t;’, so hoist that out of the ‘if’.
---
 braces.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/braces.c b/braces.c
index 444807a7..194000c2 100644
--- a/braces.c
+++ b/braces.c
@@ -399,7 +399,7 @@ mkseq (intmax_t start, intmax_t end, intmax_t incr, int type, size_t width)
       QUIT;
 #endif
       if (type == ST_INT)
-	result[i++] = t = itos (n);
+	t = itos (n);
       else if (type == ST_ZINT)
 	{
 	  size_t tlen;
@@ -419,7 +419,6 @@ mkseq (intmax_t start, intmax_t end, intmax_t incr, int type, size_t width)
 		  memset (t + (n < 0), '0', width - tlen);
 		}
 	    }
-	  result[i++] = t;
 	}
       else
 	{
@@ -428,9 +427,10 @@ mkseq (intmax_t start, intmax_t end, intmax_t incr, int type, size_t width)
 	      t[0] = n;
 	      t[1] = '\0';
 	    }
-	  result[i++] = t;
 	}
 
+      result[i++] = t;
+
       /* We failed to allocate memory for this number, so we bail. */
       if (t == 0)
 	{
-- 
2.44.0

From 3c124056fbd07b31b259ec0fd87781d9a3ed4eed Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 18 Mar 2024 13:57:36 -0700
Subject: [PATCH 03/10] Simplify mkseq control
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* braces.c (mkseq): Replace ‘if (!TEST) break; } while (1);’
with ‘} while (TEST);’.
---
 braces.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/braces.c b/braces.c
index 194000c2..099312e4 100644
--- a/braces.c
+++ b/braces.c
@@ -445,11 +445,8 @@ mkseq (intmax_t start, intmax_t end, intmax_t incr, int type, size_t width)
 	}
 
       n += incr;
-
-      if (i == nelem - 1)
-	break;
     }
-  while (1);
+  while (i < nelem - 1);
 
   result[i] = (char *)0;
   return (result);
-- 
2.44.0

From 2563729af1db4a242d4ec5d278a0e1f206af3bb5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 18 Mar 2024 14:23:49 -0700
Subject: [PATCH 04/10] printf '%M.Nq' should respect N even if M overflows
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix bug: ‘printf '%999999999999999999999999999999.5q\n' abcdefghijkl’
was incorrectly printing ‘abcdefghijkl’ instead of ‘abcde’.
* builtins/printf.def (decode_int): Don’t let diagnosis
cause us to forget to return the right value or update *str.
---
 builtins/printf.def | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/builtins/printf.def b/builtins/printf.def
index 2327bd5d..d29f727c 100644
--- a/builtins/printf.def
+++ b/builtins/printf.def
@@ -262,10 +262,7 @@ decodeint (char **str, int diagnose, int overflow_return)
       v |= ckd_add (&pr, pr, *ps - '0');
     }
   if (v && diagnose)
-    {
-      report_erange (*str, ps);
-      return -1;
-    }
+    report_erange (*str, ps);
 
   *str = ps;
   return (v ? overflow_return : pr);
-- 
2.44.0

From 63c2e30bc4819ca08d163e393e44dd04aea1b278 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 18 Mar 2024 23:27:41 -0700
Subject: [PATCH 05/10] Fix bug with printf "%.NQ" when N is large

I reproduced the bug on Fedora 39 by configuring
--with-bash-malloc=no and running the following shell commands:
  x="'"; for i in $(seq 30); do x+=$x; done
  printf -v y '%.2147483647Q' "$x"
  echo ${#y}
This outputs "0" (which is incorrect) with no diagnostic.
With the patch, the printf commands outputs "bash: printf: %Q:
Numerical result out of range" which warns the user of the overflow.
* builtins/printf.def (printf_builtin):
When processing %Q with a precision, report integer overflow if
the string length exceeds INT_MAX.
---
 builtins/printf.def | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/builtins/printf.def b/builtins/printf.def
index d29f727c..023b98ec 100644
--- a/builtins/printf.def
+++ b/builtins/printf.def
@@ -695,7 +695,13 @@ printf_builtin (WORD_LIST *list)
 		    if (convch == 'Q')
 		      {
 			if (slen > precision)
-			  precision = slen;
+			  {
+			    if (ckd_add (&precision, slen, 0))
+			      {
+				builtin_error ("%%Q: %s", strerror(ERANGE));
+				precision = -1;
+			      }
+			  }
 		      }		    
 		    /* Use printstr to get fieldwidth and precision right. */
 		    r = printstr (start, xp, slen, fieldwidth, precision);
-- 
2.44.0

From 7421aa4809e4e9a7894a268adc3faee7660e60ea Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 19 Mar 2024 08:55:04 -0700
Subject: [PATCH 06/10] Omit unnecessary INT_MAX checks in printf_builtin

* printf.def: Remove checks of precision against INT_MAX.
These are no longer needed (and in some odd cases I suppose
are even incorrect) now that the code no longer models
excess precision with INT_MAX.
---
 builtins/printf.def | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtins/printf.def b/builtins/printf.def
index 023b98ec..29553083 100644
--- a/builtins/printf.def
+++ b/builtins/printf.def
@@ -905,7 +905,7 @@ printstr (char *fmt, char *string, int len, int fieldwidth, int precision)
 	  pr = decodeint (&fmt, 1, -1);
 	  /* pr < precision means we adjusted precision in printf_builtin
 	     for the quoted string length (%Q), so we use the adjusted value */
-	  if (pr < precision && precision < INT_MAX)
+	  if (pr < precision)
 	    pr = precision;
 	}
       else
@@ -1003,7 +1003,7 @@ printwidestr (char *fmt, wchar_t *wstring, size_t len, int fieldwidth, int preci
 	  pr = decodeint (&fmt, 1, -1);
 	  /* pr < precision means we adjusted precision in printf_builtin
 	     for the quoted string length (%Q), so we use the adjusted value */
-	  if (pr < precision && precision < INT_MAX)
+	  if (pr < precision)
 	    pr = precision;
 	}
       else
-- 
2.44.0

From 797dd300ab8071c36019bc3479939b3378f38cef Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 19 Mar 2024 09:09:28 -0700
Subject: [PATCH 07/10] Omit unused typemax.h macros

* include/typemax.h (sh_imaxabs, ADDOVERFLOW, SUBOVERFLOW):
Remove these macros, as they are no longer used.
---
 include/typemax.h | 19 -------------------
 1 file changed, 19 deletions(-)

diff --git a/include/typemax.h b/include/typemax.h
index e3b98f47..c93fda73 100644
--- a/include/typemax.h
+++ b/include/typemax.h
@@ -119,23 +119,4 @@ static const unsigned long long int maxquad = ULLONG_MAX;
 #  define SIZE_MAX	((size_t) ~(size_t)0)
 #endif
 
-#ifndef sh_imaxabs
-#  define sh_imaxabs(x)	(((x) >= 0) ? (x) : -(x))
-#endif
-
-/* Handle signed arithmetic overflow and underflow.  Have to do it this way
-   to avoid compilers optimizing out simpler overflow checks. */
-
-/* Make sure that a+b does not exceed MAXV or is smaller than MINV (if b < 0).
-   Assumes that b > 0 if a > 0 and b < 0 if a < 0 */
-#define ADDOVERFLOW(a,b,minv,maxv) \
-	((((a) > 0) && ((b) > ((maxv) - (a)))) || \
-	 (((a) < 0) && ((b) < ((minv) - (a)))))
-
-/* Make sure that a-b is not smaller than MINV or exceeds MAXV (if b < 0).
-   Assumes that b > 0 if a > 0 and b < 0 if a < 0 */
-#define SUBOVERFLOW(a,b,minv,maxv) \
-	((((b) > 0) && ((a) < ((minv) + (b)))) || \
-	 (((b) < 0) && ((a) > ((maxv) + (b)))))
-
 #endif /* _SH_TYPEMAX_H */
-- 
2.44.0

From b797cc056bd35e4cd4e5e18445654fb676f9d85f Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 19 Mar 2024 09:20:13 -0700
Subject: [PATCH 08/10] Omit unused stringlist functions

* lib/sh/stringlist.c (strlist_from_word_list, strlist_to_word_list):
Remove unused functions, so that we need not worry about their
integer overflow bugs.
---
 externs.h           |  1 -
 lib/sh/stringlist.c | 34 ----------------------------------
 2 files changed, 35 deletions(-)

diff --git a/externs.h b/externs.h
index b60fb79e..ee7e4165 100644
--- a/externs.h
+++ b/externs.h
@@ -417,7 +417,6 @@ extern STRINGLIST *strlist_prefix_suffix (STRINGLIST *, const char *, const char
 extern void strlist_print (STRINGLIST *, const char *);
 extern void strlist_walk (STRINGLIST *, sh_strlist_map_func_t *);
 extern void strlist_sort (STRINGLIST *);
-extern WORD_LIST *strlist_to_word_list (STRINGLIST *, int, int);
 
 /* declarations for functions defined in lib/sh/stringvec.c */
 
diff --git a/lib/sh/stringlist.c b/lib/sh/stringlist.c
index 2e61287f..3673ac3e 100644
--- a/lib/sh/stringlist.c
+++ b/lib/sh/stringlist.c
@@ -240,37 +240,3 @@ strlist_sort (STRINGLIST *sl)
     return;
   strvec_sort (sl->list, 0);
 }
-
-STRINGLIST *
-strlist_from_word_list (WORD_LIST *list, int alloc, int starting_index, int *ip)
-{
-  STRINGLIST *ret;
-  int slen, len;
-
-  if (list == 0)
-    {
-      if (ip)
-        *ip = 0;
-      return ((STRINGLIST *)0);
-    }
-  slen = list_length ((GENERIC_LIST *)list);
-  ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
-  ret->list = strvec_from_word_list (list, alloc, starting_index, &len);
-  ret->list_size = slen + starting_index;
-  ret->list_len = len;
-  if (ip)
-    *ip = len;
-  return ret;
-}
-
-WORD_LIST *
-strlist_to_word_list (STRINGLIST *sl, int alloc, int starting_index)
-{
-  WORD_LIST *list;
-
-  if (sl == 0 || sl->list == 0)
-    return ((WORD_LIST *)NULL);
-
-  list = strvec_to_word_list (sl->list, alloc, starting_index);
-  return list;
-}
-- 
2.44.0

From 859b99643232e1f4cf82d76d9a031978866c6955 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 19 Mar 2024 16:24:41 -0700
Subject: [PATCH 09/10] Define NULL, offsetof in bashansi.h

These are central parts of the C standard nowadays, and
defining them in bashansi.h simplifies future improvements.
* bashansi.h: Include stddef.h if available.
(NULL, offsetof): Define substitutes if stddef.h doesn't.
Now-redundant includes of stddef.h (and defines of NULL
and offsetof) removed.
---
 bashansi.h           | 11 +++++++++++
 builtins/help.def    |  1 -
 lib/readline/shell.c |  4 ----
 lib/sh/fmtulong.c    |  3 ---
 lib/sh/getcwd.c      |  4 ----
 lib/sh/makepath.c    |  4 ----
 lib/sh/snprintf.c    |  3 ---
 lib/sh/strtod.c      |  4 ----
 lib/sh/strtol.c      |  4 ----
 unwind_prot.c        |  8 --------
 10 files changed, 11 insertions(+), 35 deletions(-)

diff --git a/bashansi.h b/bashansi.h
index 0bac61b4..079f365c 100644
--- a/bashansi.h
+++ b/bashansi.h
@@ -47,4 +47,15 @@ typedef unsigned char bool;
 #  endif
 #endif
 
+/* Include <stddef.h>, or define substitutes (config.h handles ptrdiff_t). */
+#ifdef HAVE_STDDEF_H
+#  include <stddef.h>
+#endif
+#ifndef NULL
+#  define NULL 0
+#endif
+#ifndef offsetof
+#  define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
 #endif /* !_BASHANSI_H_ */
diff --git a/builtins/help.def b/builtins/help.def
index 6195831b..041f89ca 100644
--- a/builtins/help.def
+++ b/builtins/help.def
@@ -58,7 +58,6 @@ $END
 #include <errno.h>
 
 #include <filecntl.h>
-#include <stddef.h>
 
 #include "../bashintl.h"
 
diff --git a/lib/readline/shell.c b/lib/readline/shell.c
index 36c91484..7c0f9554 100644
--- a/lib/readline/shell.c
+++ b/lib/readline/shell.c
@@ -67,10 +67,6 @@
 extern struct passwd *getpwuid (uid_t);
 #endif /* HAVE_GETPWUID && !HAVE_GETPW_DECLS */
 
-#ifndef NULL
-#  define NULL 0
-#endif
-
 #ifndef CHAR_BIT
 #  define CHAR_BIT 8
 #endif
diff --git a/lib/sh/fmtulong.c b/lib/sh/fmtulong.c
index 329e48c7..379d80a5 100644
--- a/lib/sh/fmtulong.c
+++ b/lib/sh/fmtulong.c
@@ -31,9 +31,6 @@
 #endif
 
 #include <bashansi.h>
-#ifdef HAVE_STDDEF_H
-#  include <stddef.h>
-#endif
 
 #ifdef HAVE_STDINT_H
 #  include <stdint.h>
diff --git a/lib/sh/getcwd.c b/lib/sh/getcwd.c
index 1a39b9f6..b2323cbe 100644
--- a/lib/sh/getcwd.c
+++ b/lib/sh/getcwd.c
@@ -64,10 +64,6 @@ extern int errno;
 #  define lstat stat
 #endif
 
-#if !defined (NULL)
-#  define NULL 0
-#endif
-
 /* If the d_fileno member of a struct dirent doesn't return anything useful,
    we need to check inode number equivalence the hard way.  Return 1 if
    the inode corresponding to PATH/DIR is identical to THISINO. */
diff --git a/lib/sh/makepath.c b/lib/sh/makepath.c
index af3adefa..fef1df0f 100644
--- a/lib/sh/makepath.c
+++ b/lib/sh/makepath.c
@@ -32,10 +32,6 @@
 
 #include <tilde/tilde.h>
 
-#ifndef NULL
-#  define NULL 0
-#endif
-
 /* MAKE SURE THESE AGREE WITH ../../externs.h. */
 
 #ifndef MP_DOTILDE
diff --git a/lib/sh/snprintf.c b/lib/sh/snprintf.c
index d0363dcb..5425f479 100644
--- a/lib/sh/snprintf.c
+++ b/lib/sh/snprintf.c
@@ -94,9 +94,6 @@
 #  include <limits.h>
 #endif
 #include <bashansi.h>
-#ifdef HAVE_STDDEF_H
-#  include <stddef.h>
-#endif
 #include <chartypes.h>
 
 #ifdef HAVE_STDINT_H
diff --git a/lib/sh/strtod.c b/lib/sh/strtod.c
index e6c1c360..2802c66d 100644
--- a/lib/sh/strtod.c
+++ b/lib/sh/strtod.c
@@ -41,10 +41,6 @@ extern int errno;
 
 #include <bashansi.h>
 
-#ifndef NULL
-#  define NULL 0
-#endif
-
 #ifndef HUGE_VAL
 #  define HUGE_VAL HUGE
 #endif
diff --git a/lib/sh/strtol.c b/lib/sh/strtol.c
index ac30dbd9..de917b0f 100644
--- a/lib/sh/strtol.c
+++ b/lib/sh/strtol.c
@@ -42,10 +42,6 @@ extern int errno;
 #include <stdc.h>
 #include <bashansi.h>
 
-#ifndef NULL
-#  define NULL 0
-#endif
-
 /* Nonzero if we are defining `strtoul' or `strtoull', operating on
    unsigned integers.  */
 #ifndef UNSIGNED
diff --git a/unwind_prot.c b/unwind_prot.c
index da9218ba..daa9c59c 100644
--- a/unwind_prot.c
+++ b/unwind_prot.c
@@ -35,14 +35,6 @@
 #  include <unistd.h>
 #endif
 
-#if defined (HAVE_STDDEF_H)
-#  include <stddef.h>
-#endif
-
-#ifndef offsetof
-#  define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-#endif
-
 #include "command.h"
 #include "general.h"
 #include "unwind_prot.h"
-- 
2.44.0

From 2aa97514c5db8c370c9ad0385ea797754ce63e9a Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 19 Mar 2024 17:17:55 -0700
Subject: [PATCH 10/10] Do not crash if NMEMB*SIZE overflows in alloc

Define and use reallocarray-like functions to fix
bugs when NMEMB * SIZE overflows in stringvec.c.
This mimics what glibc and other C libraries do nowadays,
though we do not rely on the system reallocarray even if present.
* lib/malloc/malloc.c, xmalloc.c: Include <stdckdint.h>, for ckd_mul.
(sh_reallocarray, reallocarray): New functions.
* lib/sh/stringvec.c (strvec_create, strvec_resize):
Do not silently return a null pointer if n * sizeof (char *) overflows;
instead, report an error and exit.
This fixes two recently-introduced typos.
(strvec_remove, strvec_search, strvec_copy, strvec_from_word_list)
(strvec_to_word_list):
Do not misbehave if the vector has more than INT_MAX strings.
---
 externs.h             |  2 +-
 lib/malloc/malloc.c   | 23 +++++++++++++++++++++++
 lib/malloc/shmalloc.h |  1 +
 lib/sh/stringvec.c    | 27 ++++++++++++++-------------
 xmalloc.c             | 28 ++++++++++++++++++++++++++++
 xmalloc.h             |  2 ++
 6 files changed, 69 insertions(+), 14 deletions(-)

diff --git a/externs.h b/externs.h
index ee7e4165..944546ea 100644
--- a/externs.h
+++ b/externs.h
@@ -428,7 +428,7 @@ extern void strvec_flush (char **);
 extern void strvec_dispose (char **);
 extern int strvec_remove (char **, const char *);
 extern size_t strvec_len (char * const *);
-extern int strvec_search (char **, const char *);
+extern ptrdiff_t strvec_search (char **, const char *);
 extern char **strvec_copy (char * const *);
 extern int strvec_posixcmp (char **, char **);
 extern int strvec_strcmp (char **, char **);
diff --git a/lib/malloc/malloc.c b/lib/malloc/malloc.c
index 7b2c3f25..a8dd056c 100644
--- a/lib/malloc/malloc.c
+++ b/lib/malloc/malloc.c
@@ -84,6 +84,7 @@
 #  include <strings.h>
 #endif
 #include <errno.h>
+#include <stdckdint.h>
 #include <stdio.h>
 
 #if !defined (botch)
@@ -1401,6 +1402,17 @@ sh_realloc (PTR_T ptr, size_t size, const char *file, int line)
   return internal_realloc (ptr, size, file, line, MALLOC_WRAPPER);
 }
 
+void *
+sh_reallocarray (void *ptr, size_t nmemb, size_t size, const char *file, int line)
+{
+  size_t nbytes;
+
+  if (ckd_mul (&nbytes, nmemb, size))
+    return NULL;
+
+  return internal_realloc (ptr, nbytes, file, line, MALLOC_WRAPPER);
+}
+
 void
 sh_free (PTR_T mem, const char *file, int line)
 {
@@ -1451,6 +1463,17 @@ realloc (PTR_T mem, size_t nbytes)
   return internal_realloc (mem, nbytes, (char *)NULL, 0, 0);
 }
 
+void *
+reallocarray (void *a, size_t nmemb, size_t size)
+{
+  size_t nbytes;
+
+  if (ckd_mul (&nbytes, nmemb, size))
+    return NULL;
+
+  return internal_realloc (a, nbytes, (char *)NULL, 0, 0);
+}
+
 void
 free (PTR_T mem)
 {
diff --git a/lib/malloc/shmalloc.h b/lib/malloc/shmalloc.h
index 5ea2c56e..6c458449 100644
--- a/lib/malloc/shmalloc.h
+++ b/lib/malloc/shmalloc.h
@@ -29,6 +29,7 @@
 
 extern PTR_T sh_malloc (size_t, const char *, int);
 extern PTR_T sh_realloc (PTR_T, size_t, const char *, int);
+extern void *sh_reallocarray (void *, size_t, size_t, const char *, int);
 extern void sh_free (PTR_T, const char *, int);
 
 extern PTR_T sh_memalign (size_t, size_t, const char *, int);
diff --git a/lib/sh/stringvec.c b/lib/sh/stringvec.c
index 10ca9da7..50e83a18 100644
--- a/lib/sh/stringvec.c
+++ b/lib/sh/stringvec.c
@@ -37,9 +37,7 @@
 char **
 strvec_create (size_t n)
 {
-  size_t nbytes;
-
-  return ckd_mul (&nbytes, n, sizeof (char *)) ? NULL : xmalloc (nbytes);
+  return (char **)xreallocarray (NULL, n, sizeof (char *));
 }
 
 /* Allocate an array of strings with room for N members. */
@@ -54,9 +52,7 @@ strvec_mcreate (size_t n)
 char **
 strvec_resize (char **array, size_t nsize)
 {
-  size_t nbytes;
-
-  return ckd_mul (&nbytes, nsize, sizeof (char *)) ? NULL : (char **)xrealloc (array, nbytes);
+  return (char **)xreallocarray (array, nsize, sizeof (char *));
 }
 
 char **
@@ -103,7 +99,7 @@ strvec_dispose (char **array)
 int
 strvec_remove (char **array, const char *name)
 {
-  register int i, j;
+  register size_t i, j;
   char *x;
 
   if (array == 0)
@@ -123,10 +119,10 @@ strvec_remove (char **array, const char *name)
 
 /* Find NAME in ARRAY.  Return the index of NAME, or -1 if not present.
    ARRAY should be NULL terminated. */
-int
+ptrdiff_t
 strvec_search (char **array, const char *name)
 {
-  int i;
+  size_t i;
 
   for (i = 0; array[i]; i++)
     if (STREQ (name, array[i]))
@@ -139,8 +135,7 @@ strvec_search (char **array, const char *name)
 char **
 strvec_copy (char * const *array)
 {
-  int i;
-  size_t len;
+  size_t i, len;
   char **ret;
 
   len = strvec_len (array);
@@ -212,10 +207,16 @@ strvec_sort (char **array, int posix)
 char **
 strvec_from_word_list (WORD_LIST *list, int alloc, int starting_index, int *ip)
 {
-  int count;
+  size_t count;
   char **array;
 
   count = list_length ((GENERIC_LIST *)list);
+  if (ip && count > INT_MAX)
+    {
+      /* Emulate memory_error_and_abort. */
+      fprintf (stderr, "strvec_from_word_list: too many words for argv\n");
+      exit (2);
+    }
   array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *));
 
   for (count = 0; count < starting_index; count++)
@@ -239,7 +240,7 @@ strvec_to_word_list (char **array, int alloc, int starting_index)
 {
   WORD_LIST *list;
   WORD_DESC *w;
-  int i, count;
+  size_t i, count;
 
   if (array == 0 || array[0] == 0)
     return (WORD_LIST *)NULL;
diff --git a/xmalloc.c b/xmalloc.c
index 3777d53d..43523cf6 100644
--- a/xmalloc.c
+++ b/xmalloc.c
@@ -23,6 +23,7 @@
 #endif
 
 #include "bashtypes.h"
+#include <stdckdint.h>
 #include <stdio.h>
 
 #if defined (HAVE_UNISTD_H)
@@ -131,6 +132,22 @@ xrealloc (PTR_T pointer, size_t bytes)
   return (temp);
 }
 
+/* Reallocate the storage addressed by POINTER so that it is of
+   size NMEMB*SIZE, preserving contents like realloc does.
+   If POINTER is null, allocate new storage.  This is like
+   glibc reallocarray except that it never returns a null pointer;
+   if storage is exhausted it reports an error and exits. */
+void *
+xreallocarray (void *pointer, size_t nmemb, size_t size)
+{
+  size_t nbytes;
+
+  if (ckd_mul (&nbytes, nmemb, size))
+    allocerr ("xreallocarray", (size_t)-1);
+
+  return xrealloc (pointer, nbytes);
+}
+
 /* Use this as the function to call when adding unwind protects so we
    don't need to know what free() returns. */
 void
@@ -192,6 +209,17 @@ sh_xrealloc (PTR_T pointer, size_t bytes, char *file, int line)
   return (temp);
 }
 
+void *
+sh_xreallocarray (void *pointer, size_t nmemb, size_t size, char *file, int line)
+{
+  size_t nbytes;
+
+  if (ckd_mul (&nbytes, nmemb, size))
+    return NULL;
+
+  return sh_xrealloc (pointer, nbytes, file, line);
+}
+
 void
 sh_xfree (PTR_T string, char *file, int line)
 {
diff --git a/xmalloc.h b/xmalloc.h
index d8731305..39c6d471 100644
--- a/xmalloc.h
+++ b/xmalloc.h
@@ -32,6 +32,7 @@
 /* Allocation functions in xmalloc.c */
 extern PTR_T xmalloc (size_t);
 extern PTR_T xrealloc (void *, size_t);
+extern void *xreallocarray (void *, size_t, size_t);
 extern void xfree (void *);
 
 #if defined(USING_BASH_MALLOC) && !defined (DISABLE_MALLOC_WRAPPERS)
@@ -41,6 +42,7 @@ extern void sh_xfree (void *, const char *, int);
 
 #define xmalloc(x)	sh_xmalloc((x), __FILE__, __LINE__)
 #define xrealloc(x, n)	sh_xrealloc((x), (n), __FILE__, __LINE__)
+#define xreallocarray(a, n, s) sh_xreallocarray (a, n, s, __FILE__, __LINE__)
 #define xfree(x)	sh_xfree((x), __FILE__, __LINE__)
 
 #ifdef free
-- 
2.44.0

Reply via email to