Skip to content

Commit 83ce20d

Browse files
committed
Fix use-after-free in parallel_vacuum_reset_dead_items
parallel_vacuum_reset_dead_items used a local variable to hold a pointer from the passed vacrel, purely as a shorthand. This pointer was later freed and a new allocation was made and stored to the struct. Then the local pointer was mistakenly referenced again. This apparently happened not to break anything since the freed chunk would have been put on the context's freelist, so it was accidentally the same pointer anyway, in which case the DSA handle was correctly updated. The minimal fix is to change two places so they access dead_items through the vacrel. This coding style is a maintenance hazard, so while at it get rid of most other similar usages, which were inconsistently used anyway. Analysis and patch by Vallimaharajan G, with further defensive coding by me Backpath to v17, when TidStore came in Discussion: https://postgr.es/m/1936493cc38.68cb2ef27266.7456585136086197135@zohocorp.com
1 parent 9abdc18 commit 83ce20d

File tree

2 files changed

+9
-15
lines changed

2 files changed

+9
-15
lines changed

src/backend/access/heap/vacuumlazy.c

+6-11
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,6 @@ lazy_scan_heap(LVRelState *vacrel)
820820
next_fsm_block_to_vacuum = 0;
821821
bool all_visible_according_to_vm;
822822

823-
TidStore *dead_items = vacrel->dead_items;
824-
VacDeadItemsInfo *dead_items_info = vacrel->dead_items_info;
825823
Buffer vmbuffer = InvalidBuffer;
826824
const int initprog_index[] = {
827825
PROGRESS_VACUUM_PHASE,
@@ -833,7 +831,7 @@ lazy_scan_heap(LVRelState *vacrel)
833831
/* Report that we're scanning the heap, advertising total # of blocks */
834832
initprog_val[0] = PROGRESS_VACUUM_PHASE_SCAN_HEAP;
835833
initprog_val[1] = rel_pages;
836-
initprog_val[2] = dead_items_info->max_bytes;
834+
initprog_val[2] = vacrel->dead_items_info->max_bytes;
837835
pgstat_progress_update_multi_param(3, initprog_index, initprog_val);
838836

839837
/* Initialize for the first heap_vac_scan_next_block() call */
@@ -876,7 +874,7 @@ lazy_scan_heap(LVRelState *vacrel)
876874
* dead_items TIDs, pause and do a cycle of vacuuming before we tackle
877875
* this page.
878876
*/
879-
if (TidStoreMemoryUsage(dead_items) > dead_items_info->max_bytes)
877+
if (TidStoreMemoryUsage(vacrel->dead_items) > vacrel->dead_items_info->max_bytes)
880878
{
881879
/*
882880
* Before beginning index vacuuming, we release any pin we may
@@ -1046,7 +1044,7 @@ lazy_scan_heap(LVRelState *vacrel)
10461044
* Do index vacuuming (call each index's ambulkdelete routine), then do
10471045
* related heap vacuuming
10481046
*/
1049-
if (dead_items_info->num_items > 0)
1047+
if (vacrel->dead_items_info->num_items > 0)
10501048
lazy_vacuum(vacrel);
10511049

10521050
/*
@@ -2882,19 +2880,18 @@ static void
28822880
dead_items_add(LVRelState *vacrel, BlockNumber blkno, OffsetNumber *offsets,
28832881
int num_offsets)
28842882
{
2885-
TidStore *dead_items = vacrel->dead_items;
28862883
const int prog_index[2] = {
28872884
PROGRESS_VACUUM_NUM_DEAD_ITEM_IDS,
28882885
PROGRESS_VACUUM_DEAD_TUPLE_BYTES
28892886
};
28902887
int64 prog_val[2];
28912888

2892-
TidStoreSetBlockOffsets(dead_items, blkno, offsets, num_offsets);
2889+
TidStoreSetBlockOffsets(vacrel->dead_items, blkno, offsets, num_offsets);
28932890
vacrel->dead_items_info->num_items += num_offsets;
28942891

28952892
/* update the progress information */
28962893
prog_val[0] = vacrel->dead_items_info->num_items;
2897-
prog_val[1] = TidStoreMemoryUsage(dead_items);
2894+
prog_val[1] = TidStoreMemoryUsage(vacrel->dead_items);
28982895
pgstat_progress_update_multi_param(2, prog_index, prog_val);
28992896
}
29002897

@@ -2904,16 +2901,14 @@ dead_items_add(LVRelState *vacrel, BlockNumber blkno, OffsetNumber *offsets,
29042901
static void
29052902
dead_items_reset(LVRelState *vacrel)
29062903
{
2907-
TidStore *dead_items = vacrel->dead_items;
2908-
29092904
if (ParallelVacuumIsActive(vacrel))
29102905
{
29112906
parallel_vacuum_reset_dead_items(vacrel->pvs);
29122907
return;
29132908
}
29142909

29152910
/* Recreate the tidstore with the same max_bytes limitation */
2916-
TidStoreDestroy(dead_items);
2911+
TidStoreDestroy(vacrel->dead_items);
29172912
vacrel->dead_items = TidStoreCreateLocal(vacrel->dead_items_info->max_bytes, true);
29182913

29192914
/* Reset the counter */

src/backend/commands/vacuumparallel.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -472,21 +472,20 @@ parallel_vacuum_get_dead_items(ParallelVacuumState *pvs, VacDeadItemsInfo **dead
472472
void
473473
parallel_vacuum_reset_dead_items(ParallelVacuumState *pvs)
474474
{
475-
TidStore *dead_items = pvs->dead_items;
476475
VacDeadItemsInfo *dead_items_info = &(pvs->shared->dead_items_info);
477476

478477
/*
479478
* Free the current tidstore and return allocated DSA segments to the
480479
* operating system. Then we recreate the tidstore with the same max_bytes
481480
* limitation we just used.
482481
*/
483-
TidStoreDestroy(dead_items);
482+
TidStoreDestroy(pvs->dead_items);
484483
pvs->dead_items = TidStoreCreateShared(dead_items_info->max_bytes,
485484
LWTRANCHE_PARALLEL_VACUUM_DSA);
486485

487486
/* Update the DSA pointer for dead_items to the new one */
488-
pvs->shared->dead_items_dsa_handle = dsa_get_handle(TidStoreGetDSA(dead_items));
489-
pvs->shared->dead_items_handle = TidStoreGetHandle(dead_items);
487+
pvs->shared->dead_items_dsa_handle = dsa_get_handle(TidStoreGetDSA(pvs->dead_items));
488+
pvs->shared->dead_items_handle = TidStoreGetHandle(pvs->dead_items);
490489

491490
/* Reset the counter */
492491
dead_items_info->num_items = 0;

0 commit comments

Comments
 (0)