Hi!

We can overflow buffer in eval_data_member_spec on some initializers.
The code has 2 loops, one to figure out the needed length
of the buffer and diagnose errors
          unsigned HOST_WIDE_INT l = 0;
          bool ntmbs = false;
          FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (f), k, field, value)
            if (!tree_fits_shwi_p (value))
              goto fail;
            else if (field == NULL_TREE)
              {
                if (integer_zerop (value))
                  {
                    ntmbs = true;
                    break;
                  }
                ++l;
              }
            else if (TREE_CODE (field) == RANGE_EXPR)
              {
                tree lo = TREE_OPERAND (field, 0);
                tree hi = TREE_OPERAND (field, 1);
                if (!tree_fits_uhwi_p (lo) || !tree_fits_uhwi_p (hi))
                  goto fail;
                if (integer_zerop (value))
                  {
                    l = tree_to_uhwi (lo);
                    ntmbs = true;
                    break;
                  }
                l = tree_to_uhwi (hi) + 1;
              }
            else if (tree_fits_uhwi_p (field))
              {
                l = tree_to_uhwi (field);
                if (integer_zerop (value))
                  {
                    ntmbs = true;
                    break;
                  }
                ++l;
              }
            else
              goto fail;
          if (!ntmbs || l > INT_MAX - 1)
            goto fail;
which assumes that if there are designators, they need to be ascending,
so no { [10] = 1, [2] = 3, [6] = 4, [4] = 5 } like in C11 and stops on the
first '\0' seen (but remember in l the index of that.  Here we need the
3 integer_zerop calls because the exact setting of l depends on if it is
an elt without index, or with RANGE_EXPR index, or with normal INTEGER_CST
index.  And then there is a second loop where it just stores the values
into the allocated buffer and does rely on the checking the first loop
did.  Now, for CONSTRUCTORs without indexes or with RANGE_EXPR only it
also correctly stops at '\0', but I forgot to check that in the last
case where index is INTEGER_CST:
          char *namep;
          unsigned len = l;
          if (l < 64)
            namep = XALLOCAVEC (char, l + 1);
          else
            namep = XNEWVEC (char, l + 1);
          memset (namep, 0, l + 1);
          l = 0;
          FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (f), k, field, value)
            if (field == NULL_TREE)
              {
                if (integer_zerop (value))
                  break;
                namep[l] = tree_to_shwi (value);
                ++l;
              }
            else if (TREE_CODE (field) == RANGE_EXPR)
              {
                tree lo = TREE_OPERAND (field, 0);
                tree hi = TREE_OPERAND (field, 1);
                if (integer_zerop (value))
                  break;
                unsigned HOST_WIDE_INT m = tree_to_uhwi (hi);
                for (l = tree_to_uhwi (lo); l <= m; ++l)
                  namep[l] = tree_to_shwi (value);
              }
            else
              {
                l = tree_to_uhwi (field);
                namep[l++] = tree_to_shwi (value);
              }
          namep[len] = '\0';
So, I could add if (integer_zerop (value) break; into the else
block, but then, in this loop there is no reason not to check integer_zerop
(value) first because it is handled in all 3 cases the same.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-02-03  Jakub Jelinek  <[email protected]>

        PR c++/123920
        * reflect.cc (eval_data_member_spec): Break out of the loop
        if value is integer_zerop even in the field
        && TREE_CODE (field) != RANGE_EXPR case and use a single
        test for integer_zerop (value) in the whole loop.

--- gcc/cp/reflect.cc.jj        2026-01-30 17:44:17.045951022 +0100
+++ gcc/cp/reflect.cc   2026-02-02 11:48:19.364444672 +0100
@@ -5703,10 +5703,10 @@ eval_data_member_spec (location_t loc, c
          memset (namep, 0, l + 1);
          l = 0;
          FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (f), k, field, value)
-           if (field == NULL_TREE)
+           if (integer_zerop (value))
+             break;
+           else if (field == NULL_TREE)
              {
-               if (integer_zerop (value))
-                 break;
                namep[l] = tree_to_shwi (value);
                ++l;
              }
@@ -5714,8 +5714,6 @@ eval_data_member_spec (location_t loc, c
              {
                tree lo = TREE_OPERAND (field, 0);
                tree hi = TREE_OPERAND (field, 1);
-               if (integer_zerop (value))
-                 break;
                unsigned HOST_WIDE_INT m = tree_to_uhwi (hi);
                for (l = tree_to_uhwi (lo); l <= m; ++l)
                  namep[l] = tree_to_shwi (value);

        Jakub

Reply via email to