Guard against possible memory allocation botch in batchmemtuples().
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 6 Sep 2016 19:50:31 +0000 (15:50 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 6 Sep 2016 19:50:40 +0000 (15:50 -0400)
Negative availMemLessRefund would be problematic.  It's not entirely
clear whether the case can be hit in the code as it stands, but this
seems like good future-proofing in any case.  While we're at it,
insist that the value be not merely positive but not tiny, so as to
avoid doing a lot of repalloc work for little gain.

Peter Geoghegan

Discussion: <CAM3SWZRVkuUB68DbAkgw=532gW0f+fofKueAMsY7hVYi68MuYQ@mail.gmail.com>

src/backend/utils/sort/tuplesort.c

index c8fbcf8fcc944ec06d8d745eced4ec4468e9f604..aa8e0e42fc38e448f260dacaed75b14346d7e50d 100644 (file)
@@ -2866,6 +2866,9 @@ batchmemtuples(Tuplesortstate *state)
    int64       availMemLessRefund;
    int         memtupsize = state->memtupsize;
 
+   /* Caller error if we have no tapes */
+   Assert(state->activeTapes > 0);
+
    /* For simplicity, assume no memtuples are actually currently counted */
    Assert(state->memtupcount == 0);
 
@@ -2879,6 +2882,20 @@ batchmemtuples(Tuplesortstate *state)
    refund = memtupsize * STANDARDCHUNKHEADERSIZE;
    availMemLessRefund = state->availMem - refund;
 
+   /*
+    * We need to be sure that we do not cause LACKMEM to become true, else
+    * the batch allocation size could be calculated as negative, causing
+    * havoc.  Hence, if availMemLessRefund is negative at this point, we must
+    * do nothing.  Moreover, if it's positive but rather small, there's
+    * little point in proceeding because we could only increase memtuples by
+    * a small amount, not worth the cost of the repalloc's.  We somewhat
+    * arbitrarily set the threshold at ALLOCSET_DEFAULT_INITSIZE per tape.
+    * (Note that this does not represent any assumption about tuple sizes.)
+    */
+   if (availMemLessRefund <=
+       (int64) state->activeTapes * ALLOCSET_DEFAULT_INITSIZE)
+       return;
+
    /*
     * To establish balanced memory use after refunding palloc overhead,
     * temporarily have our accounting indicate that we've allocated all
@@ -2888,9 +2905,11 @@ batchmemtuples(Tuplesortstate *state)
    state->growmemtuples = true;
    USEMEM(state, availMemLessRefund);
    (void) grow_memtuples(state);
-   /* Should not matter, but be tidy */
-   FREEMEM(state, availMemLessRefund);
    state->growmemtuples = false;
+   /* availMem must stay accurate for spacePerTape calculation */
+   FREEMEM(state, availMemLessRefund);
+   if (LACKMEM(state))
+       elog(ERROR, "unexpected out-of-memory situation in tuplesort");
 
 #ifdef TRACE_SORT
    if (trace_sort)