Check for arithmetic overflow when calculating sizes, to prevent
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 2 Sep 2005 22:54:59 +0000 (22:54 +0000)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 2 Sep 2005 22:54:59 +0000 (22:54 +0000)
some buffer-overflow issues.  These patches are conservative, in the
sense that when I couldn't determine whether an overflow was possible,
I inserted a run-time check.
* regex_internal.h (re_xmalloc, re_xrealloc, re_x2realloc): New macros.
(SIZE_MAX) [!defined SIZE_MAX]: New macro.
(re_alloc_oversized, re_x2alloc_oversized, re_xnmalloc):
(re_xnrealloc, re_x2nrealloc): New inline functions.
* lib/regcomp.c (init_dfa, analyze, build_range_exp, parse_bracket_exp):
(build_equiv_class, build_charclass): Check for arithmetic overflow
in size expression calculations.
* lib/regex_internal.c (re_string_realloc_buffers):
(build_wcs_upper_buffer, re_node_set_add_intersect):
(re_node_set_init_union, re_node_set_insert, re_node_set_insert_last):
(re_dfa_add_node, register_state): Likewise.
* lib/regexec.c (re_search_stub, re_copy_regs, re_search_internal):
(prune_impossible_nodes, push_fail_stack, set_regs, check_arrival):
(build_trtable, extend_buffers, match_ctx_init, match_ctx_add_entry):
(match_ctx_add_subtop, match_ctx_add_sublast): Likewise.

config/srclist.txt
lib/ChangeLog
lib/regcomp.c
lib/regex_internal.c
lib/regex_internal.h
lib/regexec.c

index 59f7ddb..c1f77a1 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: srclist.txt,v 1.100 2005-09-02 12:27:28 karl Exp $
+# $Id: srclist.txt,v 1.101 2005-09-02 22:54:59 eggert Exp $
 # Files for which we are not the source.  See ./srclistvars.sh for the
 # variable definitions.
 
@@ -109,6 +109,7 @@ $LIBCSRC/stdlib/getsubopt.c         lib gpl
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1281
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1282
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1285
+# http://sources.redhat.com/bugzilla/show_bug.cgi?id=1291
 #$LIBCSRC/posix/regcomp.c              lib gpl
 #
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1238
@@ -138,6 +139,7 @@ $LIBCSRC/stdlib/getsubopt.c         lib gpl
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1285
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1286
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1287
+# http://sources.redhat.com/bugzilla/show_bug.cgi?id=1291
 #$LIBCSRC/posix/regex_internal.c               lib gpl
 #
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1054
@@ -150,6 +152,7 @@ $LIBCSRC/stdlib/getsubopt.c         lib gpl
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1278
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1281
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1285
+# http://sources.redhat.com/bugzilla/show_bug.cgi?id=1291
 #$LIBCSRC/posix/regex_internal.h               lib gpl
 #
 # http://sources.redhat.com/bugzilla/show_bug.cgi?id=1216
index 091cdc7..7ebaab3 100644 (file)
@@ -1,3 +1,25 @@
+2005-09-02  Paul Eggert  <eggert@cs.ucla.edu>
+
+       Check for arithmetic overflow when calculating sizes, to prevent
+       some buffer-overflow issues.  These patches are conservative, in the
+       sense that when I couldn't determine whether an overflow was possible,
+       I inserted a run-time check.
+       * regex_internal.h (re_xmalloc, re_xrealloc, re_x2realloc): New macros.
+       (SIZE_MAX) [!defined SIZE_MAX]: New macro.
+       (re_alloc_oversized, re_x2alloc_oversized, re_xnmalloc):
+       (re_xnrealloc, re_x2nrealloc): New inline functions.
+       * lib/regcomp.c (init_dfa, analyze, build_range_exp, parse_bracket_exp):
+       (build_equiv_class, build_charclass): Check for arithmetic overflow
+       in size expression calculations.
+       * lib/regex_internal.c (re_string_realloc_buffers):
+       (build_wcs_upper_buffer, re_node_set_add_intersect):
+       (re_node_set_init_union, re_node_set_insert, re_node_set_insert_last):
+       (re_dfa_add_node, register_state): Likewise.
+       * lib/regexec.c (re_search_stub, re_copy_regs, re_search_internal):
+       (prune_impossible_nodes, push_fail_stack, set_regs, check_arrival):
+       (build_trtable, extend_buffers, match_ctx_init, match_ctx_add_entry):
+       (match_ctx_add_subtop, match_ctx_add_sublast): Likewise.
+
 2005-09-01  Paul Eggert  <eggert@cs.ucla.edu>
 
        * glob.c (GET_LOGIN_NAME_MAX): Renamed from LOGIN_NAME_MAX,
index dc9bbc1..5dc3488 100644 (file)
@@ -808,7 +808,7 @@ init_dfa (re_dfa_t *dfa, Idx pat_len)
   dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
 
   dfa->nodes_alloc = pat_len + 1;
-  dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+  dfa->nodes = re_xmalloc (re_token_t, dfa->nodes_alloc);
 
   /*  table_size = 2 ^ ceil(log pat_len) */
   for (table_size = 1; table_size <= pat_len; table_size <<= 1)
@@ -1083,13 +1083,13 @@ analyze (regex_t *preg)
   /* Allocate arrays.  */
   dfa->nexts = re_malloc (Idx, dfa->nodes_alloc);
   dfa->org_indices = re_malloc (Idx, dfa->nodes_alloc);
-  dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+  dfa->edests = re_xmalloc (re_node_set, dfa->nodes_alloc);
   dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
   if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
          || dfa->eclosures == NULL, 0))
     return REG_ESPACE;
 
-  dfa->subexp_map = re_malloc (Idx, preg->re_nsub);
+  dfa->subexp_map = re_xmalloc (Idx, preg->re_nsub);
   if (dfa->subexp_map != NULL)
     {
       Idx i;
@@ -1125,7 +1125,7 @@ analyze (regex_t *preg)
   if ((!preg->re_no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
       || dfa->nbackref)
     {
-      dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+      dfa->inveclosures = re_xmalloc (re_node_set, dfa->nodes_len);
       if (BE (dfa->inveclosures == NULL, 0))
         return REG_ESPACE;
       ret = calc_inveclosure (dfa);
@@ -2608,12 +2608,11 @@ build_range_exp (re_bitset_ptr_t sbcset,
            wchar_t *new_array_start, *new_array_end;
            Idx new_nranges;
 
-           /* +1 in case of mbcset->nranges is 0.  */
-           new_nranges = 2 * mbcset->nranges + 1;
+           new_nranges = mbcset->nranges;
            /* Use realloc since mbcset->range_starts and mbcset->range_ends
               are NULL if *range_alloc == 0.  */
-           new_array_start = re_realloc (mbcset->range_starts, wchar_t,
-                                         new_nranges);
+           new_array_start = re_x2realloc (mbcset->range_starts, wchar_t,
+                                           &new_nranges);
            new_array_end = re_realloc (mbcset->range_ends, wchar_t,
                                        new_nranges);
 
@@ -2840,10 +2839,9 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
              uint32_t *new_array_end;
              Idx new_nranges;
 
-             /* +1 in case of mbcset->nranges is 0.  */
-             new_nranges = 2 * mbcset->nranges + 1;
-             new_array_start = re_realloc (mbcset->range_starts, uint32_t,
-                                           new_nranges);
+             new_nranges = mbcset->nranges;
+             new_array_start = re_x2realloc (mbcset->range_starts, uint32_t,
+                                             &new_nranges);
              new_array_end = re_realloc (mbcset->range_ends, uint32_t,
                                          new_nranges);
 
@@ -2914,12 +2912,11 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
          if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
            {
              /* Not enough, realloc it.  */
-             /* +1 in case of mbcset->ncoll_syms is 0.  */
-             Idx new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+             Idx new_coll_sym_alloc = mbcset->ncoll_syms;
              /* Use realloc since mbcset->coll_syms is NULL
                 if *alloc == 0.  */
-             int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
-                                                  new_coll_sym_alloc);
+             int32_t *new_coll_syms = re_x2realloc (mbcset->coll_syms, int32_t,
+                                                    &new_coll_sym_alloc);
              if (BE (new_coll_syms == NULL, 0))
                return REG_ESPACE;
              mbcset->coll_syms = new_coll_syms;
@@ -3103,11 +3100,10 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
                {
                  wchar_t *new_mbchars;
                  /* Not enough, realloc it.  */
-                 /* +1 in case of mbcset->nmbchars is 0.  */
-                 mbchar_alloc = 2 * mbcset->nmbchars + 1;
+                 mbchar_alloc = mbcset->nmbchars;
                  /* Use realloc since array is NULL if *alloc == 0.  */
-                 new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
-                                           mbchar_alloc);
+                 new_mbchars = re_x2realloc (mbcset->mbchars, wchar_t,
+                                             &mbchar_alloc);
                  if (BE (new_mbchars == NULL, 0))
                    goto parse_bracket_exp_espace;
                  mbcset->mbchars = new_mbchars;
@@ -3381,12 +3377,11 @@ build_equiv_class (re_bitset_ptr_t sbcset,
       if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
        {
          /* Not enough, realloc it.  */
-         /* +1 in case of mbcset->nequiv_classes is 0.  */
-         Idx new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+         Idx new_equiv_class_alloc = mbcset->nequiv_classes;
          /* Use realloc since the array is NULL if *alloc == 0.  */
-         int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
-                                                  int32_t,
-                                                  new_equiv_class_alloc);
+         int32_t *new_equiv_classes = re_x2realloc (mbcset->equiv_classes,
+                                                    int32_t,
+                                                    &new_equiv_class_alloc);
          if (BE (new_equiv_classes == NULL, 0))
            return REG_ESPACE;
          mbcset->equiv_classes = new_equiv_classes;
@@ -3431,11 +3426,10 @@ build_charclass (unsigned REG_TRANSLATE_TYPE trans, re_bitset_ptr_t sbcset,
   if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
     {
       /* Not enough, realloc it.  */
-      /* +1 in case of mbcset->nchar_classes is 0.  */
-      Idx new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      Idx new_char_class_alloc = mbcset->nchar_classes;
       /* Use realloc since array is NULL if *alloc == 0.  */
-      wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
-                                              new_char_class_alloc);
+      wctype_t *new_char_classes = re_x2realloc (mbcset->char_classes, wctype_t,
+                                                &new_char_class_alloc);
       if (BE (new_char_classes == NULL, 0))
        return REG_ESPACE;
       mbcset->char_classes = new_char_classes;
index 57ba44d..864b1a7 100644 (file)
@@ -132,13 +132,13 @@ re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len)
 #ifdef RE_ENABLE_I18N
   if (pstr->mb_cur_max > 1)
     {
-      wint_t *new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+      wint_t *new_wcs = re_xrealloc (pstr->wcs, wint_t, new_buf_len);
       if (BE (new_wcs == NULL, 0))
        return REG_ESPACE;
       pstr->wcs = new_wcs;
       if (pstr->offsets != NULL)
        {
-         Idx *new_offsets = re_realloc (pstr->offsets, Idx, new_buf_len);
+         Idx *new_offsets = re_xrealloc (pstr->offsets, Idx, new_buf_len);
          if (BE (new_offsets == NULL, 0))
            return REG_ESPACE;
          pstr->offsets = new_offsets;
@@ -392,7 +392,7 @@ build_wcs_upper_buffer (re_string_t *pstr)
 
                    if (pstr->offsets == NULL)
                      {
-                       pstr->offsets = re_malloc (Idx, pstr->bufs_len);
+                       pstr->offsets = re_xmalloc (Idx, pstr->bufs_len);
 
                        if (pstr->offsets == NULL)
                          return REG_ESPACE;
@@ -873,7 +873,7 @@ re_node_set_alloc (re_node_set *set, Idx size)
 {
   set->alloc = size;
   set->nelem = 0;
-  set->elems = re_malloc (Idx, size);
+  set->elems = re_xmalloc (Idx, size);
   if (BE (set->elems == NULL, 0))
     return REG_ESPACE;
   return REG_NOERROR;
@@ -964,7 +964,12 @@ re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
   if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
     {
       Idx new_alloc = src1->nelem + src2->nelem + dest->alloc;
-      Idx *new_elems = re_realloc (dest->elems, Idx, new_alloc);
+      Idx *new_elems;
+      if (sizeof (Idx) < 3
+         && (new_alloc < dest->alloc
+             || ((Idx) (src1->nelem + src2->nelem) < src1->nelem)))
+       return REG_ESPACE;
+      new_elems = re_xrealloc (dest->elems, Idx, new_alloc);
       if (BE (new_elems == NULL, 0))
         return REG_ESPACE;
       dest->elems = new_elems;
@@ -1050,7 +1055,9 @@ re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
   if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
     {
       dest->alloc = src1->nelem + src2->nelem;
-      dest->elems = re_malloc (Idx, dest->alloc);
+      if (sizeof (Idx) < 2 && dest->alloc < src1->nelem)
+       return REG_ESPACE;
+      dest->elems = re_xmalloc (Idx, dest->alloc);
       if (BE (dest->elems == NULL, 0))
        return REG_ESPACE;
     }
@@ -1101,10 +1108,17 @@ re_node_set_merge (re_node_set *dest, const re_node_set *src)
   Idx is, id, sbase, delta;
   if (src == NULL || src->nelem == 0)
     return REG_NOERROR;
+  if (sizeof (Idx) < 3
+      && ((Idx) (2 * src->nelem) < src->nelem
+         || (Idx) (2 * src->nelem + dest->nelem) < dest->nelem))
+    return REG_ESPACE;
   if (dest->alloc < 2 * src->nelem + dest->nelem)
     {
-      Idx new_alloc = 2 * (src->nelem + dest->alloc);
-      Idx *new_buffer = re_realloc (dest->elems, Idx, new_alloc);
+      Idx new_alloc = src->nelem + dest->alloc;
+      Idx *new_buffer;
+      if (sizeof (Idx) < 4 && new_alloc < dest->alloc)
+       return REG_ESPACE;
+      new_buffer = re_x2realloc (dest->elems, Idx, &new_alloc);
       if (BE (new_buffer == NULL, 0))
        return REG_ESPACE;
       dest->elems = new_buffer;
@@ -1199,9 +1213,7 @@ re_node_set_insert (re_node_set *set, Idx elem)
   /* Realloc if we need.  */
   if (set->alloc == set->nelem)
     {
-      Idx *new_elems;
-      set->alloc = set->alloc * 2;
-      new_elems = re_realloc (set->elems, Idx, set->alloc);
+      Idx *new_elems = re_x2realloc (set->elems, Idx, &set->alloc);
       if (BE (new_elems == NULL, 0))
        return false;
       set->elems = new_elems;
@@ -1239,8 +1251,7 @@ re_node_set_insert_last (re_node_set *set, Idx elem)
   if (set->alloc == set->nelem)
     {
       Idx *new_elems;
-      set->alloc = (set->alloc + 1) * 2;
-      new_elems = re_realloc (set->elems, Idx, set->alloc);
+      new_elems = re_x2realloc (set->elems, Idx, &set->alloc);
       if (BE (new_elems == NULL, 0))
        return false;
       set->elems = new_elems;
@@ -1313,18 +1324,18 @@ re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
   int type = token.type;
   if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
     {
-      Idx new_nodes_alloc = dfa->nodes_alloc * 2;
+      Idx new_nodes_alloc = dfa->nodes_alloc;
       Idx *new_nexts, *new_indices;
       re_node_set *new_edests, *new_eclosures;
 
-      re_token_t *new_nodes = re_realloc (dfa->nodes, re_token_t,
-                                         new_nodes_alloc);
+      re_token_t *new_nodes = re_x2realloc (dfa->nodes, re_token_t,
+                                           &new_nodes_alloc);
       if (BE (new_nodes == NULL, 0))
        return REG_MISSING;
       dfa->nodes = new_nodes;
       new_nexts = re_realloc (dfa->nexts, Idx, new_nodes_alloc);
       new_indices = re_realloc (dfa->org_indices, Idx, new_nodes_alloc);
-      new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+      new_edests = re_xrealloc (dfa->edests, re_node_set, new_nodes_alloc);
       new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
       if (BE (new_nexts == NULL || new_indices == NULL
              || new_edests == NULL || new_eclosures == NULL, 0))
@@ -1487,9 +1498,9 @@ register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, re_hashval_t hash)
   spot = dfa->state_table + (hash & dfa->state_hash_mask);
   if (BE (spot->alloc <= spot->num, 0))
     {
-      Idx new_alloc = 2 * spot->num + 2;
-      re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
-                                             new_alloc);
+      Idx new_alloc = spot->num;
+      re_dfastate_t **new_array = re_x2realloc (spot->array, re_dfastate_t *,
+                                               &new_alloc);
       if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       spot->array = new_array;
index 3c17fc3..b1d79ef 100644 (file)
@@ -442,10 +442,70 @@ static unsigned int re_string_context_at (const re_string_t *input,
 #endif
 
 #define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_xmalloc(t,n) ((t *) re_xnmalloc (n, sizeof (t)))
 #define re_calloc(t,n) ((t *) calloc (n, sizeof (t)))
 #define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_xrealloc(p,t,n) ((t *) re_xnrealloc (p, n, sizeof (t)))
+#define re_x2realloc(p,t,pn) ((t *) re_x2nrealloc (p, pn, sizeof (t)))
 #define re_free(p) free (p)
 
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+/* Return true if an array of N objects, each of size S, cannot exist
+   due to size arithmetic overflow.  S must be nonzero.  */
+static inline bool
+re_alloc_oversized (size_t n, size_t s)
+{
+  return BE (SIZE_MAX / s < n, 0);
+}
+
+/* Return true if an array of (2 * N + 1) objects, each of size S,
+   cannot exist due to size arithmetic overflow.  S must be nonzero.  */
+static inline bool
+re_x2alloc_oversized (size_t n, size_t s)
+{
+  return BE ((SIZE_MAX / s - 1) / 2 < n, 0);
+}
+
+/* Allocate an array of N objects, each with S bytes of memory,
+   dynamically, with error checking.  S must be nonzero.  */
+static inline void *
+re_xnmalloc (size_t n, size_t s)
+{
+  return re_alloc_oversized (n, s) ? NULL : malloc (n * s);
+}
+
+/* Change the size of an allocated block of memory P to an array of N
+   objects each of S bytes, with error checking.  S must be nonzero.  */
+static inline void *
+re_xnrealloc (void *p, size_t n, size_t s)
+{
+  return re_alloc_oversized (n, s) ? NULL : realloc (p, n * s);
+}
+
+/* Reallocate a block of memory P to an array of (2 * (*PN) + 1)
+   objects each of S bytes, with error checking.  S must be nonzero.
+   If the allocation is successful, set *PN to the new allocation
+   count and return the resulting pointer.  Otherwise, return
+   NULL.  */
+static inline void *
+re_x2nrealloc (void *p, size_t *pn, size_t s)
+{
+  if (re_x2alloc_oversized (*pn, s))
+    return NULL;
+  else
+    {
+      /* Add 1 in case *PN is zero.  */
+      size_t n1 = 2 * *pn + 1;
+      p = realloc (p, n1 * s);
+      if (BE (p != NULL, 1))
+       *pn = n1;
+      return p;
+    }
+}
+
 struct bin_tree_t
 {
   struct bin_tree_t *parent;
index 257c647..4de1f7b 100644 (file)
@@ -434,7 +434,7 @@ re_search_stub (struct re_pattern_buffer *bufp,
   if (regs == NULL)
     nregs = 1;
   else if (BE (bufp->re_regs_allocated == REG_FIXED
-              && regs->rm_num_regs < bufp->re_nsub + 1, 0))
+              && regs->rm_num_regs <= bufp->re_nsub, 0))
     {
       nregs = regs->rm_num_regs;
       if (BE (nregs < 1, 0))
@@ -446,7 +446,7 @@ re_search_stub (struct re_pattern_buffer *bufp,
     }
   else
     nregs = bufp->re_nsub + 1;
-  pmatch = re_malloc (regmatch_t, nregs);
+  pmatch = re_xmalloc (regmatch_t, nregs);
   if (BE (pmatch == NULL, 0))
     {
       rval = -2;
@@ -500,7 +500,7 @@ re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs,
   /* Have the register data arrays been allocated?  */
   if (regs_allocated == REG_UNALLOCATED)
     { /* No.  So allocate them with malloc.  */
-      regs->rm_start = re_malloc (regoff_t, need_regs);
+      regs->rm_start = re_xmalloc (regoff_t, need_regs);
       regs->rm_end = re_malloc (regoff_t, need_regs);
       if (BE (regs->rm_start == NULL, 0) || BE (regs->rm_end == NULL, 0))
        return REG_UNALLOCATED;
@@ -513,7 +513,7 @@ re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs,
       if (BE (need_regs > regs->rm_num_regs, 0))
        {
          regoff_t *new_start =
-           re_realloc (regs->rm_start, regoff_t, need_regs);
+           re_xrealloc (regs->rm_start, regoff_t, need_regs);
          regoff_t *new_end = re_realloc (regs->rm_end, regoff_t, need_regs);
          if (BE (new_start == NULL, 0) || BE (new_end == NULL, 0))
            return REG_UNALLOCATED;
@@ -685,7 +685,7 @@ re_search_internal (const regex_t *preg,
      multi character collating element.  */
   if (nmatch > 1 || dfa->has_mb_node)
     {
-      mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+      mctx.state_log = re_xmalloc (re_dfastate_t *, mctx.input.bufs_len + 1);
       if (BE (mctx.state_log == NULL, 0))
        {
          err = REG_ESPACE;
@@ -945,7 +945,7 @@ prune_impossible_nodes (re_match_context_t *mctx)
 #endif
   match_last = mctx->match_last;
   halt_node = mctx->last_node;
-  sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+  sifted_states = re_xmalloc (re_dfastate_t *, match_last + 1);
   if (BE (sifted_states == NULL, 0))
     {
       ret = REG_ESPACE;
@@ -953,7 +953,7 @@ prune_impossible_nodes (re_match_context_t *mctx)
     }
   if (dfa->nbackref)
     {
-      lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+      lim_states = re_xmalloc (re_dfastate_t *, match_last + 1);
       if (BE (lim_states == NULL, 0))
        {
          ret = REG_ESPACE;
@@ -1343,15 +1343,14 @@ push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node,
   if (fs->num == fs->alloc)
     {
       struct re_fail_stack_ent_t *new_array =
-       re_realloc (fs->stack, struct re_fail_stack_ent_t, fs->alloc * 2);
+       re_x2realloc (fs->stack, struct re_fail_stack_ent_t, &fs->alloc);
       if (new_array == NULL)
        return REG_ESPACE;
-      fs->alloc *= 2;
       fs->stack = new_array;
     }
   fs->stack[num].idx = str_idx;
   fs->stack[num].node = dest_node;
-  fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+  fs->stack[num].regs = re_xmalloc (regmatch_t, nregs);
   if (fs->stack[num].regs == NULL)
     return REG_ESPACE;
   memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
@@ -1399,7 +1398,7 @@ set_regs (const regex_t *preg, const re_match_context_t *mctx,
   if (fl_backtrack)
     {
       fs = &fs_body;
-      fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+      fs->stack = re_xmalloc (struct re_fail_stack_ent_t, fs->alloc);
       if (fs->stack == NULL)
        return REG_ESPACE;
     }
@@ -1409,6 +1408,11 @@ set_regs (const regex_t *preg, const re_match_context_t *mctx,
   cur_node = dfa->init_node;
   re_node_set_init_empty (&eps_via_nodes);
 
+  if (re_alloc_oversized (nmatch, sizeof (regmatch_t)))
+    {
+      free_fail_stack_return (fs);
+      return REG_ESPACE;
+    }
   if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
     prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
   else
@@ -2872,16 +2876,16 @@ check_arrival (re_match_context_t *mctx, state_array_t *path,
     {
       re_dfastate_t **new_array;
       Idx old_alloc = path->alloc;
-      path->alloc += last_str + mctx->max_mb_elem_len + 1;
-      new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
-      if (new_array == NULL)
-       {
-         path->alloc = old_alloc;
-         return REG_ESPACE;
-       }
+      Idx new_alloc = old_alloc + last_str + mctx->max_mb_elem_len + 1;
+      if (BE (new_alloc < old_alloc, 0))
+       return REG_ESPACE;
+      new_array = re_xrealloc (path->array, re_dfastate_t *, new_alloc);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
       path->array = new_array;
+      path->alloc = new_alloc;
       memset (new_array + old_alloc, '\0',
-             sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+             sizeof (re_dfastate_t *) * (new_alloc - old_alloc));
     }
 
   str_idx = path->next_idx == 0 ? top_str : path->next_idx;
@@ -3336,6 +3340,12 @@ build_trtable (re_dfa_t *dfa, re_dfastate_t *state)
   if (BE (err != REG_NOERROR, 0))
     goto out_free;
 
+  /* Avoid arithmetic overflow in size calculation.  */
+  if (BE (((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX)
+          / (3 * sizeof (re_dfastate_t *)))
+         < ndests, 0))
+    goto out_free;
+
   if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX
                         + ndests * 3 * sizeof (re_dfastate_t *)))
     dest_states = (re_dfastate_t **)
@@ -4057,8 +4067,8 @@ extend_buffers (re_match_context_t *mctx)
       /* XXX We have no indication of the size of this buffer.  If this
         allocation fail we have no indication that the state_log array
         does not have the right size.  */
-      re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
-                                             pstr->bufs_len + 1);
+      re_dfastate_t **new_array = re_xrealloc (mctx->state_log, re_dfastate_t *,
+                                              pstr->bufs_len + 1);
       if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       mctx->state_log = new_array;
@@ -4106,8 +4116,8 @@ match_ctx_init (re_match_context_t *mctx, int eflags, Idx n)
   mctx->match_last = REG_MISSING;
   if (n > 0)
     {
-      mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
-      mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+      mctx->bkref_ents = re_xmalloc (struct re_backref_cache_entry, n);
+      mctx->sub_tops = re_xmalloc (re_sub_match_top_t *, n);
       if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
        return REG_ESPACE;
     }
@@ -4179,8 +4189,8 @@ match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx,
   if (mctx->nbkref_ents >= mctx->abkref_ents)
     {
       struct re_backref_cache_entry* new_entry;
-      new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
-                             mctx->abkref_ents * 2);
+      new_entry = re_x2realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+                               &mctx->abkref_ents);
       if (BE (new_entry == NULL, 0))
        {
          re_free (mctx->bkref_ents);
@@ -4188,8 +4198,8 @@ match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx,
        }
       mctx->bkref_ents = new_entry;
       memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
-             sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
-      mctx->abkref_ents *= 2;
+             (sizeof (struct re_backref_cache_entry)
+              * (mctx->abkref_ents - mctx->nbkref_ents)));
     }
   if (mctx->nbkref_ents > 0
       && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
@@ -4253,10 +4263,10 @@ match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx)
 #endif
   if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
     {
-      Idx new_asub_tops = mctx->asub_tops * 2;
-      re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
-                                                  re_sub_match_top_t *,
-                                                  new_asub_tops);
+      Idx new_asub_tops = mctx->asub_tops;
+      re_sub_match_top_t **new_array = re_x2realloc (mctx->sub_tops,
+                                                    re_sub_match_top_t *,
+                                                    &new_asub_tops);
       if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       mctx->sub_tops = new_array;
@@ -4280,10 +4290,10 @@ match_ctx_add_sublast (re_sub_match_top_t *subtop, Idx node, Idx str_idx)
   re_sub_match_last_t *new_entry;
   if (BE (subtop->nlasts == subtop->alasts, 0))
     {
-      Idx new_alasts = 2 * subtop->alasts + 1;
-      re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
-                                                   re_sub_match_last_t *,
-                                                   new_alasts);
+      Idx new_alasts = subtop->alasts;
+      re_sub_match_last_t **new_array = re_x2realloc (subtop->lasts,
+                                                     re_sub_match_last_t *,
+                                                     &new_alasts);
       if (BE (new_array == NULL, 0))
        return NULL;
       subtop->lasts = new_array;