]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix over-allocation of space for array_out()'s result string.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 24 Sep 2018 15:30:51 +0000 (11:30 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 24 Sep 2018 15:30:51 +0000 (11:30 -0400)
array_out overestimated the space needed for its output, possibly by
a very substantial amount if the array is multi-dimensional, because
of wrong order of operations in the loop that counts the number of
curly-brace pairs needed.  While the output string is normally
short-lived, this could still cause problems in extreme cases.

An additional minor error was that it counted one more delimiter than
is actually needed.

Repair those errors, add an Assert that the space is now correctly
calculated, and make some minor improvements in the comments.

I also failed to resist the temptation to get rid of an integer
modulus operation per array element; a simple comparison is sufficient.

This bug dates clear back to Berkeley days, so back-patch to all
supported versions.

Keiichi Hirobe, minor additional work by me

Discussion: https://postgr.es/m/CAH=EFxE9W0tRvQkixR2XJRRCToUYUEDkJZk6tnADXugPBRdcdg@mail.gmail.com

src/backend/utils/adt/arrayfuncs.c

index 9154ffd7bede1f9f2891a31bd49703930b45426f..d5008eff8c64899c798354f4364e4fc40ba59aa7 100644 (file)
@@ -976,8 +976,8 @@ array_out(PG_FUNCTION_ARGS)
        int                     bitmask;
        bool       *needquotes,
                                needdims = false;
+       size_t          overall_length;
        int                     nitems,
-                               overall_length,
                                i,
                                j,
                                k,
@@ -1050,7 +1050,7 @@ array_out(PG_FUNCTION_ARGS)
         */
        values = (char **) palloc(nitems * sizeof(char *));
        needquotes = (bool *) palloc(nitems * sizeof(bool));
-       overall_length = 1;                     /* don't forget to count \0 at end. */
+       overall_length = 0;
 
        p = ARR_DATA_PTR(v);
        bitmap = ARR_NULLBITMAP(v);
@@ -1105,7 +1105,7 @@ array_out(PG_FUNCTION_ARGS)
                /* Count the pair of double quotes, if needed */
                if (needquote)
                        overall_length += 2;
-               /* and the comma */
+               /* and the comma (or other typdelim delimiter) */
                overall_length += 1;
 
                /* advance bitmap pointer if any */
@@ -1121,14 +1121,19 @@ array_out(PG_FUNCTION_ARGS)
        }
 
        /*
-        * count total number of curly braces in output string
+        * The very last array element doesn't have a typdelim delimiter after it,
+        * but that's OK; that space is needed for the trailing '\0'.
+        *
+        * Now count total number of curly brace pairs in output string.
         */
        for (i = j = 0, k = 1; i < ndim; i++)
-               k *= dims[i], j += k;
+       {
+               j += k, k *= dims[i];
+       }
+       overall_length += 2 * j;
 
+       /* Format explicit dimensions if required */
        dims_str[0] = '\0';
-
-       /* add explicit dimensions if required */
        if (needdims)
        {
                char       *ptr = dims_str;
@@ -1140,9 +1145,11 @@ array_out(PG_FUNCTION_ARGS)
                }
                *ptr++ = *ASSGN;
                *ptr = '\0';
+               overall_length += ptr - dims_str;
        }
 
-       retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
+       /* Now construct the output string */
+       retval = (char *) palloc(overall_length);
        p = retval;
 
 #define APPENDSTR(str) (strcpy(p, (str)), p += strlen(p))
@@ -1180,14 +1187,16 @@ array_out(PG_FUNCTION_ARGS)
 
                for (i = ndim - 1; i >= 0; i--)
                {
-                       indx[i] = (indx[i] + 1) % dims[i];
-                       if (indx[i])
+                       if (++(indx[i]) < dims[i])
                        {
                                APPENDCHAR(typdelim);
                                break;
                        }
                        else
+                       {
+                               indx[i] = 0;
                                APPENDCHAR('}');
+                       }
                }
                j = i;
        } while (j != -1);
@@ -1195,6 +1204,9 @@ array_out(PG_FUNCTION_ARGS)
 #undef APPENDSTR
 #undef APPENDCHAR
 
+       /* Assert that we calculated the string length accurately */
+       Assert(overall_length == (p - retval + 1));
+
        pfree(values);
        pfree(needquotes);