Skip to content

Commit 3f07484

Browse files
committed
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
1 parent 20bf7b2 commit 3f07484

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

src/backend/access/gist/gistget.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
375375
}
376376

377377
so->nPageData = so->curPageData = 0;
378+
scan->xs_hitup = NULL; /* might point into pageDataCxt */
378379
if (so->pageDataCxt)
379380
MemoryContextReset(so->pageDataCxt);
380381

@@ -642,6 +643,7 @@ gistgettuple(IndexScanDesc scan, ScanDirection dir)
642643

643644
so->firstCall = false;
644645
so->curPageData = so->nPageData = 0;
646+
scan->xs_hitup = NULL;
645647
if (so->pageDataCxt)
646648
MemoryContextReset(so->pageDataCxt);
647649

@@ -766,6 +768,7 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
766768

767769
/* Begin the scan by processing the root page */
768770
so->curPageData = so->nPageData = 0;
771+
scan->xs_hitup = NULL;
769772
if (so->pageDataCxt)
770773
MemoryContextReset(so->pageDataCxt);
771774

src/backend/access/gist/gistscan.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
313313
if (!first_time)
314314
pfree(fn_extras);
315315
}
316+
317+
/* any previous xs_hitup will have been pfree'd in context resets above */
318+
scan->xs_hitup = NULL;
316319
}
317320

318321
void

src/test/regress/expected/gist.out

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,40 @@ order by point(0.101, 0.101) <-> p;
114114
(0.5,0.5)
115115
(11 rows)
116116

117+
-- Check case with multiple rescans (bug #14641)
118+
explain (costs off)
119+
select p from
120+
(values (box(point(0,0), point(0.5,0.5))),
121+
(box(point(0.5,0.5), point(0.75,0.75))),
122+
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
123+
cross join lateral
124+
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
125+
QUERY PLAN
126+
--------------------------------------------------------------------
127+
Nested Loop
128+
-> Values Scan on "*VALUES*"
129+
-> Limit
130+
-> Index Only Scan using gist_tbl_point_index on gist_tbl
131+
Index Cond: (p <@ "*VALUES*".column1)
132+
Order By: (p <-> ("*VALUES*".column1)[0])
133+
(6 rows)
134+
135+
select p from
136+
(values (box(point(0,0), point(0.5,0.5))),
137+
(box(point(0.5,0.5), point(0.75,0.75))),
138+
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
139+
cross join lateral
140+
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
141+
p
142+
-------------
143+
(0.5,0.5)
144+
(0.45,0.45)
145+
(0.75,0.75)
146+
(0.7,0.7)
147+
(1,1)
148+
(0.95,0.95)
149+
(6 rows)
150+
117151
drop index gist_tbl_point_index;
118152
-- Test index-only scan with box opclass
119153
create index gist_tbl_box_index on gist_tbl using gist (b);

src/test/regress/sql/gist.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ order by point(0.101, 0.101) <-> p;
6969
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
7070
order by point(0.101, 0.101) <-> p;
7171

72+
-- Check case with multiple rescans (bug #14641)
73+
explain (costs off)
74+
select p from
75+
(values (box(point(0,0), point(0.5,0.5))),
76+
(box(point(0.5,0.5), point(0.75,0.75))),
77+
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
78+
cross join lateral
79+
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
80+
81+
select p from
82+
(values (box(point(0,0), point(0.5,0.5))),
83+
(box(point(0.5,0.5), point(0.75,0.75))),
84+
(box(point(0.8,0.8), point(1.0,1.0)))) as v(bb)
85+
cross join lateral
86+
(select p from gist_tbl where p <@ bb order by p <-> bb[0] limit 2) ss;
87+
7288
drop index gist_tbl_point_index;
7389

7490
-- Test index-only scan with box opclass

0 commit comments

Comments
 (0)