Fix pfree-of-already-freed-tuple when rescanning a GiST index-only scan.
GiST's getNextNearest() function attempts to pfree the previously-returned tuple if any (that is, scan->xs_hitup in HEAD, or scan->xs_itup in older branches). However, if we are rescanning a plan node after ending a previous scan early, those tuple pointers could be pointing to garbage, because they would be pointing into the scan's pageDataCxt or queueCxt which has been reset. In a debug build this reliably results in a crash, although I think it might sometimes accidentally fail to fail in production builds. To fix, clear the pointer field anyplace we reset a context it might be pointing into. This may be overkill --- I think probably only the queueCxt case is involved in this bug, so that resetting in gistrescan() would be sufficient --- but dangling pointers are generally bad news, so let's avoid them. Another plausible answer might be to just not bother with the pfree in getNextNearest(). The reconstructed tuples would go away anyway in the context resets, and I'm far from convinced that freeing them a bit earlier really saves anything meaningful. I'll stick with the original logic in this patch, but if we find more problems in the same area we should consider that approach. Per bug #14641 from Denis Smirnov. Back-patch to 9.5 where this logic was introduced. Discussion: https://postgr.es/m/20170504072034.24366.57688@wrigleys.postgresql.org
This commit is contained in:
parent
20bf7b2b0a
commit
3f074845a8
@ -375,6 +375,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
|
|||||||
}
|
}
|
||||||
|
|
||||||
so->nPageData = so->curPageData = 0;
|
so->nPageData = so->curPageData = 0;
|
||||||
|
scan->xs_hitup = NULL; /* might point into pageDataCxt */
|
||||||
if (so->pageDataCxt)
|
if (so->pageDataCxt)
|
||||||
MemoryContextReset(so->pageDataCxt);
|
MemoryContextReset(so->pageDataCxt);
|
||||||
|
|
||||||
@ -642,6 +643,7 @@ gistgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
|
|
||||||
so->firstCall = false;
|
so->firstCall = false;
|
||||||
so->curPageData = so->nPageData = 0;
|
so->curPageData = so->nPageData = 0;
|
||||||
|
scan->xs_hitup = NULL;
|
||||||
if (so->pageDataCxt)
|
if (so->pageDataCxt)
|
||||||
MemoryContextReset(so->pageDataCxt);
|
MemoryContextReset(so->pageDataCxt);
|
||||||
|
|
||||||
@ -766,6 +768,7 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
|
|||||||
|
|
||||||
/* Begin the scan by processing the root page */
|
/* Begin the scan by processing the root page */
|
||||||
so->curPageData = so->nPageData = 0;
|
so->curPageData = so->nPageData = 0;
|
||||||
|
scan->xs_hitup = NULL;
|
||||||
if (so->pageDataCxt)
|
if (so->pageDataCxt)
|
||||||
MemoryContextReset(so->pageDataCxt);
|
MemoryContextReset(so->pageDataCxt);
|
||||||
|
|
||||||
|
@ -313,6 +313,9 @@ gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
|
|||||||
if (!first_time)
|
if (!first_time)
|
||||||
pfree(fn_extras);
|
pfree(fn_extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* any previous xs_hitup will have been pfree'd in context resets above */
|
||||||
|
scan->xs_hitup = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -114,6 +114,40 @@ order by point(0.101, 0.101) <-> p;
|
|||||||
(0.5,0.5)
|
(0.5,0.5)
|
||||||
(11 rows)
|
(11 rows)
|
||||||
|
|
||||||
|
-- Check case with multiple rescans (bug #14641)
|
||||||
|
explain (costs off)
|
||||||
|
select p from
|
||||||
|
(values (box(point(0,0), point(0.5,0.5))),
|
||||||
|
(box(point(0.5,0.5), point(0.75,0.75))),
|
||||||
|
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
|
||||||
|
cross join lateral
|
||||||
|
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
Nested Loop
|
||||||
|
-> Values Scan on "*VALUES*"
|
||||||
|
-> Limit
|
||||||
|
-> Index Only Scan using gist_tbl_point_index on gist_tbl
|
||||||
|
Index Cond: (p <@ "*VALUES*".column1)
|
||||||
|
Order By: (p <-> ("*VALUES*".column1)[0])
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
select p from
|
||||||
|
(values (box(point(0,0), point(0.5,0.5))),
|
||||||
|
(box(point(0.5,0.5), point(0.75,0.75))),
|
||||||
|
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
|
||||||
|
cross join lateral
|
||||||
|
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
|
||||||
|
p
|
||||||
|
-------------
|
||||||
|
(0.5,0.5)
|
||||||
|
(0.45,0.45)
|
||||||
|
(0.75,0.75)
|
||||||
|
(0.7,0.7)
|
||||||
|
(1,1)
|
||||||
|
(0.95,0.95)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
drop index gist_tbl_point_index;
|
drop index gist_tbl_point_index;
|
||||||
-- Test index-only scan with box opclass
|
-- Test index-only scan with box opclass
|
||||||
create index gist_tbl_box_index on gist_tbl using gist (b);
|
create index gist_tbl_box_index on gist_tbl using gist (b);
|
||||||
|
@ -69,6 +69,22 @@ order by point(0.101, 0.101) <-> p;
|
|||||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||||
order by point(0.101, 0.101) <-> p;
|
order by point(0.101, 0.101) <-> p;
|
||||||
|
|
||||||
|
-- Check case with multiple rescans (bug #14641)
|
||||||
|
explain (costs off)
|
||||||
|
select p from
|
||||||
|
(values (box(point(0,0), point(0.5,0.5))),
|
||||||
|
(box(point(0.5,0.5), point(0.75,0.75))),
|
||||||
|
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
|
||||||
|
cross join lateral
|
||||||
|
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
|
||||||
|
|
||||||
|
select p from
|
||||||
|
(values (box(point(0,0), point(0.5,0.5))),
|
||||||
|
(box(point(0.5,0.5), point(0.75,0.75))),
|
||||||
|
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
|
||||||
|
cross join lateral
|
||||||
|
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
|
||||||
|
|
||||||
drop index gist_tbl_point_index;
|
drop index gist_tbl_point_index;
|
||||||
|
|
||||||
-- Test index-only scan with box opclass
|
-- Test index-only scan with box opclass
|
||||||
|
Loading…
x
Reference in New Issue
Block a user