Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit324281f

Browse files
committed
Fix corruption due to vacuum_defer_cleanup_age underflowing 64bit xids
When vacuum_defer_cleanup_age is bigger than the current xid, including theepoch, the subtraction of vacuum_defer_cleanup_age would lead to a wrappedaround xid. While that normally is not a problem, the subsequent conversion toa 64bit xid results in a 64bit-xid very far into the future. As that xid isused as a horizon to detect whether rows versions are old enough to beremoved, that allows removal of rows that are still visible (i.e. corruption).If vacuum_defer_cleanup_age was never changed from the default, there is nochance of this bug occurring.This bug was introduced indc7420c. A lesser version of it exists in12-13, introduced byfb5344c, affecting only GiST.The 12-13 version of the issue can, in rare cases, lead to pages in a gistindex getting recycled too early, potentially causing index entries to befound multiple times.The fix is fairly simple - don't allow vacuum_defer_cleanup_age to retreatfurther than FirstNormalTransactionId.Patches to make similar bugs easier to find, by adding asserts to the 64bitxid infrastructure, have been proposed, but are not suitable for backpatching.Currently there are no tests for vacuum_defer_cleanup_age. A patch introducinginfrastructure to make writing a test easier has been posted to the list.Reported-by: Michail Nikolaev <michail.nikolaev@gmail.com>Reviewed-by: Matthias van de Meent <boekewurm+postgres@gmail.com>Author: Andres Freund <andres@anarazel.de>Discussion:https://postgr.es/m/20230108002923.cyoser3ttmt63bfn@awork3.anarazel.deBackpatch: 12-, but impact/fix is smaller for 12-13
1 parent9f1e51b commit324281f

File tree

1 file changed

+73
-12
lines changed

1 file changed

+73
-12
lines changed

‎src/backend/storage/ipc/procarray.c

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ static inline void ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId l
372372
staticvoidProcArrayGroupClearXid(PGPROC*proc,TransactionIdlatestXid);
373373
staticvoidMaintainLatestCompletedXid(TransactionIdlatestXid);
374374
staticvoidMaintainLatestCompletedXidRecovery(TransactionIdlatestXid);
375+
staticvoidTransactionIdRetreatSafely(TransactionId*xid,
376+
intretreat_by,
377+
FullTransactionIdrel);
375378

376379
staticinlineFullTransactionIdFullXidRelativeTo(FullTransactionIdrel,
377380
TransactionIdxid);
@@ -1893,17 +1896,35 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
18931896
* so guc.c should limit it to no more than the xidStopLimit threshold
18941897
* in varsup.c. Also note that we intentionally don't apply
18951898
* vacuum_defer_cleanup_age on standby servers.
1899+
*
1900+
* Need to use TransactionIdRetreatSafely() instead of open-coding the
1901+
* subtraction, to prevent creating an xid before
1902+
* FirstNormalTransactionId.
18961903
*/
1897-
h->oldest_considered_running=
1898-
TransactionIdRetreatedBy(h->oldest_considered_running,
1899-
vacuum_defer_cleanup_age);
1900-
h->shared_oldest_nonremovable=
1901-
TransactionIdRetreatedBy(h->shared_oldest_nonremovable,
1902-
vacuum_defer_cleanup_age);
1903-
h->data_oldest_nonremovable=
1904-
TransactionIdRetreatedBy(h->data_oldest_nonremovable,
1905-
vacuum_defer_cleanup_age);
1906-
/* defer doesn't apply to temp relations */
1904+
Assert(TransactionIdPrecedesOrEquals(h->oldest_considered_running,
1905+
h->shared_oldest_nonremovable));
1906+
Assert(TransactionIdPrecedesOrEquals(h->shared_oldest_nonremovable,
1907+
h->data_oldest_nonremovable));
1908+
1909+
if (vacuum_defer_cleanup_age>0)
1910+
{
1911+
TransactionIdRetreatSafely(&h->oldest_considered_running,
1912+
vacuum_defer_cleanup_age,
1913+
h->latest_completed);
1914+
TransactionIdRetreatSafely(&h->shared_oldest_nonremovable,
1915+
vacuum_defer_cleanup_age,
1916+
h->latest_completed);
1917+
TransactionIdRetreatSafely(&h->data_oldest_nonremovable,
1918+
vacuum_defer_cleanup_age,
1919+
h->latest_completed);
1920+
/* defer doesn't apply to temp relations */
1921+
1922+
1923+
Assert(TransactionIdPrecedesOrEquals(h->oldest_considered_running,
1924+
h->shared_oldest_nonremovable));
1925+
Assert(TransactionIdPrecedesOrEquals(h->shared_oldest_nonremovable,
1926+
h->data_oldest_nonremovable));
1927+
}
19071928
}
19081929

19091930
/*
@@ -2474,8 +2495,10 @@ GetSnapshotData(Snapshot snapshot)
24742495
oldestfxid=FullXidRelativeTo(latest_completed,oldestxid);
24752496

24762497
/* apply vacuum_defer_cleanup_age */
2477-
def_vis_xid_data=
2478-
TransactionIdRetreatedBy(xmin,vacuum_defer_cleanup_age);
2498+
def_vis_xid_data=xmin;
2499+
TransactionIdRetreatSafely(&def_vis_xid_data,
2500+
vacuum_defer_cleanup_age,
2501+
oldestfxid);
24792502

24802503
/* Check whether there's a replication slot requiring an older xmin. */
24812504
def_vis_xid_data=
@@ -4340,6 +4363,44 @@ GlobalVisCheckRemovableXid(Relation rel, TransactionId xid)
43404363
returnGlobalVisTestIsRemovableXid(state,xid);
43414364
}
43424365

4366+
/*
4367+
* Safely retract *xid by retreat_by, store the result in *xid.
4368+
*
4369+
* Need to be careful to prevent *xid from retreating below
4370+
* FirstNormalTransactionId during epoch 0. This is important to prevent
4371+
* generating xids that cannot be converted to a FullTransactionId without
4372+
* wrapping around.
4373+
*
4374+
* If retreat_by would lead to a too old xid, FirstNormalTransactionId is
4375+
* returned instead.
4376+
*/
4377+
staticvoid
4378+
TransactionIdRetreatSafely(TransactionId*xid,intretreat_by,FullTransactionIdrel)
4379+
{
4380+
TransactionIdoriginal_xid=*xid;
4381+
FullTransactionIdfxid;
4382+
uint64fxid_i;
4383+
4384+
Assert(TransactionIdIsNormal(original_xid));
4385+
Assert(retreat_by >=0);/* relevant GUCs are stored as ints */
4386+
AssertTransactionIdInAllowableRange(original_xid);
4387+
4388+
if (retreat_by==0)
4389+
return;
4390+
4391+
fxid=FullXidRelativeTo(rel,original_xid);
4392+
fxid_i=U64FromFullTransactionId(fxid);
4393+
4394+
if ((fxid_i-FirstNormalTransactionId) <=retreat_by)
4395+
*xid=FirstNormalTransactionId;
4396+
else
4397+
{
4398+
*xid=TransactionIdRetreatedBy(original_xid,retreat_by);
4399+
Assert(TransactionIdIsNormal(*xid));
4400+
Assert(NormalTransactionIdPrecedes(*xid,original_xid));
4401+
}
4402+
}
4403+
43434404
/*
43444405
* Convert a 32 bit transaction id into 64 bit transaction id, by assuming it
43454406
* is within MaxTransactionId / 2 of XidFromFullTransactionId(rel).

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp