Fix out-of-memory handling in ecpglib.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 20 Jan 2020 00:15:15 +0000 (19:15 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 20 Jan 2020 00:15:15 +0000 (19:15 -0500)
ecpg_build_params() would crash on a null pointer dereference if
realloc() failed, due to updating the persistent "stmt" struct
too aggressively.  (Even without the crash, this would've leaked
the old storage that we were trying to realloc.)

Per Coverity.  This seems to have been broken in commit 0cc050794,
so back-patch into v12.

src/interfaces/ecpg/ecpglib/execute.c

index 0912d87c77f9fbf3a46cafa9c5529093b8cd6101..606aff63735028b47236ac6328a03bf0722a87b6 100644 (file)
@@ -1502,26 +1502,37 @@ ecpg_build_params(struct statement *stmt)
        }
        else
        {
-           if (!(stmt->paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
+           bool        realloc_failed = false;
+           char      **newparamvalues;
+           int        *newparamlengths;
+           int        *newparamformats;
+
+           /* enlarge all the param arrays */
+           if ((newparamvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
+               stmt->paramvalues = newparamvalues;
+           else
+               realloc_failed = true;
+
+           if ((newparamlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+               stmt->paramlengths = newparamlengths;
+           else
+               realloc_failed = true;
+
+           if ((newparamformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+               stmt->paramformats = newparamformats;
+           else
+               realloc_failed = true;
+
+           if (realloc_failed)
            {
                ecpg_free_params(stmt, false);
                ecpg_free(tobeinserted);
                return false;
            }
-           stmt->paramvalues[stmt->nparams] = tobeinserted;
 
-           if (!(stmt->paramlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
-           {
-               ecpg_free_params(stmt, false);
-               return false;
-           }
+           /* only now can we assign ownership of "tobeinserted" to stmt */
+           stmt->paramvalues[stmt->nparams] = tobeinserted;
            stmt->paramlengths[stmt->nparams] = binary_length;
-
-           if (!(stmt->paramformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
-           {
-               ecpg_free_params(stmt, false);
-               return false;
-           }
            stmt->paramformats[stmt->nparams] = (binary_format ? 1 : 0);
            stmt->nparams++;