Avoid access to uninitialized memory in shared tidbitmap iteration.
authorRobert Haas <rhaas@postgresql.org>
Thu, 16 Mar 2017 19:07:26 +0000 (15:07 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 16 Mar 2017 19:09:46 +0000 (15:09 -0400)
Primarily, this didn't work correctly when the tidbitmap ended up
empty.

Dilip Kumar, per a report from Emre Hasegeli

Discussion: http://postgr.es/m/CAFiTN-ujHFKb8WSLhK54rfqQT3r2yiPQOyeBrCDsA4p9Fwp_jw@mail.gmail.com

src/backend/nodes/tidbitmap.c

index 8e915646d93a1a6a06875835033c121572de2424..ae7a913c12c77c0e6ca1b13c10011fe32572c45c 100644 (file)
@@ -302,6 +302,10 @@ tbm_create(long maxbytes, dsa_area *dsa)
        tbm->maxentries = (int) nbuckets;
        tbm->lossify_start = 0;
        tbm->dsa = dsa;
+       tbm->dsapagetable = InvalidDsaPointer;
+       tbm->dsapagetableold = InvalidDsaPointer;
+       tbm->ptpages = InvalidDsaPointer;
+       tbm->ptchunks = InvalidDsaPointer;
 
        return tbm;
 }
@@ -363,20 +367,23 @@ void
 tbm_free_shared_area(dsa_area *dsa, dsa_pointer dp)
 {
        TBMSharedIteratorState *istate = dsa_get_address(dsa, dp);
-       PTEntryArray *ptbase = dsa_get_address(dsa, istate->pagetable);
+       PTEntryArray *ptbase;
        PTIterationArray *ptpages;
        PTIterationArray *ptchunks;
 
-       if (pg_atomic_sub_fetch_u32(&ptbase->refcount, 1) == 0)
-               dsa_free(dsa, istate->pagetable);
-
-       if (istate->spages)
+       if (DsaPointerIsValid(istate->pagetable))
+       {
+               ptbase = dsa_get_address(dsa, istate->pagetable);
+               if (pg_atomic_sub_fetch_u32(&ptbase->refcount, 1) == 0)
+                       dsa_free(dsa, istate->pagetable);
+       }
+       if (DsaPointerIsValid(istate->spages))
        {
                ptpages = dsa_get_address(dsa, istate->spages);
                if (pg_atomic_sub_fetch_u32(&ptpages->refcount, 1) == 0)
                        dsa_free(dsa, istate->spages);
        }
-       if (istate->schunks)
+       if (DsaPointerIsValid(istate->schunks))
        {
                ptchunks = dsa_get_address(dsa, istate->schunks);
                if (pg_atomic_sub_fetch_u32(&ptchunks->refcount, 1) == 0)
@@ -786,7 +793,7 @@ tbm_prepare_shared_iterate(TIDBitmap *tbm)
 {
        dsa_pointer dp;
        TBMSharedIteratorState *istate;
-       PTEntryArray *ptbase;
+       PTEntryArray *ptbase = NULL;
        PTIterationArray *ptpages = NULL;
        PTIterationArray *ptchunks = NULL;
 
@@ -797,7 +804,7 @@ tbm_prepare_shared_iterate(TIDBitmap *tbm)
         * Allocate TBMSharedIteratorState from DSA to hold the shared members and
         * lock, this will also be used by multiple worker for shared iterate.
         */
-       dp = dsa_allocate(tbm->dsa, sizeof(TBMSharedIteratorState));
+       dp = dsa_allocate0(tbm->dsa, sizeof(TBMSharedIteratorState));
        istate = dsa_get_address(tbm->dsa, dp);
 
        /*
@@ -856,7 +863,7 @@ tbm_prepare_shared_iterate(TIDBitmap *tbm)
                        Assert(npages == tbm->npages);
                        Assert(nchunks == tbm->nchunks);
                }
-               else
+               else if (tbm->status == TBM_ONE_PAGE)
                {
                        /*
                         * In one page mode allocate the space for one pagetable entry and
@@ -868,8 +875,8 @@ tbm_prepare_shared_iterate(TIDBitmap *tbm)
                        ptpages->index[0] = 0;
                }
 
-               pg_atomic_init_u32(&ptbase->refcount, 0);
-
+               if (ptbase != NULL)
+                       pg_atomic_init_u32(&ptbase->refcount, 0);
                if (npages > 1)
                        qsort_arg((void *) (ptpages->index), npages, sizeof(int),
                                          tbm_shared_comparator, (void *) ptbase->ptentry);
@@ -899,10 +906,11 @@ tbm_prepare_shared_iterate(TIDBitmap *tbm)
         * increase the refcount by 1 so that while freeing the shared iterator
         * we don't free pagetable and iterator array until its refcount becomes 0.
         */
-       pg_atomic_add_fetch_u32(&ptbase->refcount, 1);
-       if (ptpages)
+       if (ptbase != NULL)
+               pg_atomic_add_fetch_u32(&ptbase->refcount, 1);
+       if (ptpages != NULL)
                pg_atomic_add_fetch_u32(&ptpages->refcount, 1);
-       if (ptchunks)
+       if (ptchunks != NULL)
                pg_atomic_add_fetch_u32(&ptchunks->refcount, 1);
 
        /* Initialize the iterator lock */
@@ -1069,9 +1077,16 @@ tbm_shared_iterate(TBMSharedIterator *iterator)
 {
        TBMIterateResult *output = &iterator->output;
        TBMSharedIteratorState *istate = iterator->state;
-       PagetableEntry *ptbase = iterator->ptbase->ptentry;
-       int                *idxpages = iterator->ptpages->index;
-       int                *idxchunks = iterator->ptchunks->index;
+       PagetableEntry *ptbase = NULL;
+       int                *idxpages = NULL;
+       int                *idxchunks = NULL;
+
+       if (iterator->ptbase != NULL)
+               ptbase = iterator->ptbase->ptentry;
+       if (iterator->ptpages != NULL)
+               idxpages = iterator->ptpages->index;
+       if (iterator->ptchunks != NULL)
+               idxchunks = iterator->ptchunks->index;
 
        /* Acquire the LWLock before accessing the shared members */
        LWLockAcquire(&istate->lock, LW_EXCLUSIVE);
@@ -1480,7 +1495,7 @@ tbm_attach_shared_iterate(dsa_area *dsa, dsa_pointer dp)
         * Create the TBMSharedIterator struct, with enough trailing space to
         * serve the needs of the TBMIterateResult sub-struct.
         */
-       iterator = (TBMSharedIterator *) palloc(sizeof(TBMSharedIterator) +
+       iterator = (TBMSharedIterator *) palloc0(sizeof(TBMSharedIterator) +
                                                                 MAX_TUPLES_PER_PAGE * sizeof(OffsetNumber));
 
        istate = (TBMSharedIteratorState *) dsa_get_address(dsa, dp);