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

Commita9ce839

Browse files
Sanitize line pointers within contrib/amcheck.
Adopt a more defensive approach to accessing index tuples incontrib/amcheck: verify that each line pointer looks sane beforeaccessing associated tuple using pointer arithmetic based on linepointer's offset. This avoids undefined behavior and assertion failuresin cases where line pointers are corrupt.Issue spotted following a complaint about an assertion failure byGrigory Smolkin, which involved a test harness that deliberatelycorrupts indexes.This is arguably a bugfix, but no backpatch given the lack of fieldreports beyond Grigory's.Discussion:https://postgr.es/m/CAH2-WzmkurhCqnyLHxk0VkOZqd49+ZZsp1xAJOg7j2x7dmp_XQ@mail.gmail.com
1 parent05b38c7 commita9ce839

File tree

1 file changed

+109
-32
lines changed

1 file changed

+109
-32
lines changed

‎contrib/amcheck/verify_nbtree.c

Lines changed: 109 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,14 @@ static inline bool invariant_g_offset(BtreeCheckState *state, BTScanInsert key,
155155
OffsetNumberlowerbound);
156156
staticinlineboolinvariant_l_nontarget_offset(BtreeCheckState*state,
157157
BTScanInsertkey,
158+
BlockNumbernontargetblock,
158159
Pagenontarget,
159160
OffsetNumberupperbound);
160161
staticPagepalloc_btree_page(BtreeCheckState*state,BlockNumberblocknum);
161162
staticinlineBTScanInsertbt_mkscankey_pivotsearch(Relationrel,
162163
IndexTupleitup);
164+
staticItemIdPageGetItemIdCareful(BtreeCheckState*state,BlockNumberblock,
165+
Pagepage,OffsetNumberoffset);
163166
staticinlineItemPointerBTreeTupleGetHeapTIDCareful(BtreeCheckState*state,
164167
IndexTupleitup,boolnonpivot);
165168

@@ -710,7 +713,9 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level)
710713
ItemIditemid;
711714

712715
/* Internal page -- downlink gets leftmost on next level */
713-
itemid=PageGetItemId(state->target,P_FIRSTDATAKEY(opaque));
716+
itemid=PageGetItemIdCareful(state,state->targetblock,
717+
state->target,
718+
P_FIRSTDATAKEY(opaque));
714719
itup= (IndexTuple)PageGetItem(state->target,itemid);
715720
nextleveldown.leftmost=BTreeInnerTupleGetDownLink(itup);
716721
nextleveldown.level=opaque->btpo.level-1;
@@ -837,26 +842,29 @@ bt_target_page_check(BtreeCheckState *state)
837842
* Check the number of attributes in high key. Note, rightmost page
838843
* doesn't contain a high key, so nothing to check
839844
*/
840-
if (!P_RIGHTMOST(topaque)&&
841-
!_bt_check_natts(state->rel,state->heapkeyspace,state->target,
842-
P_HIKEY))
845+
if (!P_RIGHTMOST(topaque))
843846
{
844847
ItemIditemid;
845848
IndexTupleitup;
846849

847-
itemid=PageGetItemId(state->target,P_HIKEY);
848-
itup= (IndexTuple)PageGetItem(state->target,itemid);
849-
850-
ereport(ERROR,
851-
(errcode(ERRCODE_INDEX_CORRUPTED),
852-
errmsg("wrong number of high key index tuple attributes in index \"%s\"",
853-
RelationGetRelationName(state->rel)),
854-
errdetail_internal("Index block=%u natts=%u block type=%s page lsn=%X/%X.",
855-
state->targetblock,
856-
BTreeTupleGetNAtts(itup,state->rel),
857-
P_ISLEAF(topaque) ?"heap" :"index",
858-
(uint32) (state->targetlsn >>32),
859-
(uint32)state->targetlsn)));
850+
/* Verify line pointer before checking tuple */
851+
itemid=PageGetItemIdCareful(state,state->targetblock,
852+
state->target,P_HIKEY);
853+
if (!_bt_check_natts(state->rel,state->heapkeyspace,state->target,
854+
P_HIKEY))
855+
{
856+
itup= (IndexTuple)PageGetItem(state->target,itemid);
857+
ereport(ERROR,
858+
(errcode(ERRCODE_INDEX_CORRUPTED),
859+
errmsg("wrong number of high key index tuple attributes in index \"%s\"",
860+
RelationGetRelationName(state->rel)),
861+
errdetail_internal("Index block=%u natts=%u block type=%s page lsn=%X/%X.",
862+
state->targetblock,
863+
BTreeTupleGetNAtts(itup,state->rel),
864+
P_ISLEAF(topaque) ?"heap" :"index",
865+
(uint32) (state->targetlsn >>32),
866+
(uint32)state->targetlsn)));
867+
}
860868
}
861869

862870
/*
@@ -876,7 +884,8 @@ bt_target_page_check(BtreeCheckState *state)
876884

877885
CHECK_FOR_INTERRUPTS();
878886

879-
itemid=PageGetItemId(state->target,offset);
887+
itemid=PageGetItemIdCareful(state,state->targetblock,
888+
state->target,offset);
880889
itup= (IndexTuple)PageGetItem(state->target,itemid);
881890
tupsize=IndexTupleSize(itup);
882891

@@ -1121,7 +1130,9 @@ bt_target_page_check(BtreeCheckState *state)
11211130
OffsetNumberNext(offset));
11221131

11231132
/* Reuse itup to get pointed-to heap location of second item */
1124-
itemid=PageGetItemId(state->target,OffsetNumberNext(offset));
1133+
itemid=PageGetItemIdCareful(state,state->targetblock,
1134+
state->target,
1135+
OffsetNumberNext(offset));
11251136
itup= (IndexTuple)PageGetItem(state->target,itemid);
11261137
nhtid=psprintf("(%u,%u)",
11271138
ItemPointerGetBlockNumberNoCheck(&(itup->t_tid)),
@@ -1406,7 +1417,8 @@ bt_right_page_check_scankey(BtreeCheckState *state)
14061417
if (P_ISLEAF(opaque)&&nline >=P_FIRSTDATAKEY(opaque))
14071418
{
14081419
/* Return first data item (if any) */
1409-
rightitem=PageGetItemId(rightpage,P_FIRSTDATAKEY(opaque));
1420+
rightitem=PageGetItemIdCareful(state,targetnext,rightpage,
1421+
P_FIRSTDATAKEY(opaque));
14101422
}
14111423
elseif (!P_ISLEAF(opaque)&&
14121424
nline >=OffsetNumberNext(P_FIRSTDATAKEY(opaque)))
@@ -1415,8 +1427,8 @@ bt_right_page_check_scankey(BtreeCheckState *state)
14151427
* Return first item after the internal page's "negative infinity"
14161428
* item
14171429
*/
1418-
rightitem=PageGetItemId(rightpage,
1419-
OffsetNumberNext(P_FIRSTDATAKEY(opaque)));
1430+
rightitem=PageGetItemIdCareful(state,targetnext,rightpage,
1431+
OffsetNumberNext(P_FIRSTDATAKEY(opaque)));
14201432
}
14211433
else
14221434
{
@@ -1576,7 +1588,8 @@ bt_downlink_check(BtreeCheckState *state, BTScanInsert targetkey,
15761588
if (offset_is_negative_infinity(copaque,offset))
15771589
continue;
15781590

1579-
if (!invariant_l_nontarget_offset(state,targetkey,child,offset))
1591+
if (!invariant_l_nontarget_offset(state,targetkey,childblock,child,
1592+
offset))
15801593
ereport(ERROR,
15811594
(errcode(ERRCODE_INDEX_CORRUPTED),
15821595
errmsg("down-link lower bound invariant violated for index \"%s\"",
@@ -1687,7 +1700,8 @@ bt_downlink_missing_check(BtreeCheckState *state)
16871700
RelationGetRelationName(state->rel));
16881701

16891702
level=topaque->btpo.level;
1690-
itemid=PageGetItemId(state->target,P_FIRSTDATAKEY(topaque));
1703+
itemid=PageGetItemIdCareful(state,state->targetblock,state->target,
1704+
P_FIRSTDATAKEY(topaque));
16911705
itup= (IndexTuple)PageGetItem(state->target,itemid);
16921706
childblk=BTreeInnerTupleGetDownLink(itup);
16931707
for (;;)
@@ -1711,7 +1725,8 @@ bt_downlink_missing_check(BtreeCheckState *state)
17111725
level-1,copaque->btpo.level)));
17121726

17131727
level=copaque->btpo.level;
1714-
itemid=PageGetItemId(child,P_FIRSTDATAKEY(copaque));
1728+
itemid=PageGetItemIdCareful(state,childblk,child,
1729+
P_FIRSTDATAKEY(copaque));
17151730
itup= (IndexTuple)PageGetItem(child,itemid);
17161731
childblk=BTreeInnerTupleGetDownLink(itup);
17171732
/* Be slightly more pro-active in freeing this memory, just in case */
@@ -1760,7 +1775,7 @@ bt_downlink_missing_check(BtreeCheckState *state)
17601775
*/
17611776
if (P_ISHALFDEAD(copaque)&& !P_RIGHTMOST(copaque))
17621777
{
1763-
itemid=PageGetItemId(child,P_HIKEY);
1778+
itemid=PageGetItemIdCareful(state,childblk,child,P_HIKEY);
17641779
itup= (IndexTuple)PageGetItem(child,itemid);
17651780
if (BTreeTupleGetTopParent(itup)==state->targetblock)
17661781
return;
@@ -2087,17 +2102,23 @@ offset_is_negative_infinity(BTPageOpaque opaque, OffsetNumber offset)
20872102
* Does the invariant hold that the key is strictly less than a given upper
20882103
* bound offset item?
20892104
*
2105+
* Verifies line pointer on behalf of caller.
2106+
*
20902107
* If this function returns false, convention is that caller throws error due
20912108
* to corruption.
20922109
*/
20932110
staticinlinebool
20942111
invariant_l_offset(BtreeCheckState*state,BTScanInsertkey,
20952112
OffsetNumberupperbound)
20962113
{
2114+
ItemIditemid;
20972115
int32cmp;
20982116

20992117
Assert(key->pivotsearch);
21002118

2119+
/* Verify line pointer before checking tuple */
2120+
itemid=PageGetItemIdCareful(state,state->targetblock,state->target,
2121+
upperbound);
21012122
/* pg_upgrade'd indexes may legally have equal sibling tuples */
21022123
if (!key->heapkeyspace)
21032124
returninvariant_leq_offset(state,key,upperbound);
@@ -2116,13 +2137,11 @@ invariant_l_offset(BtreeCheckState *state, BTScanInsert key,
21162137
if (cmp==0)
21172138
{
21182139
BTPageOpaquetopaque;
2119-
ItemIditemid;
21202140
IndexTupleritup;
21212141
intuppnkeyatts;
21222142
ItemPointerrheaptid;
21232143
boolnonpivot;
21242144

2125-
itemid=PageGetItemId(state->target,upperbound);
21262145
ritup= (IndexTuple)PageGetItem(state->target,itemid);
21272146
topaque= (BTPageOpaque)PageGetSpecialPointer(state->target);
21282147
nonpivot=P_ISLEAF(topaque)&&upperbound >=P_FIRSTDATAKEY(topaque);
@@ -2145,6 +2164,9 @@ invariant_l_offset(BtreeCheckState *state, BTScanInsert key,
21452164
* Does the invariant hold that the key is less than or equal to a given upper
21462165
* bound offset item?
21472166
*
2167+
* Caller should have verified that upperbound's item pointer is consistent
2168+
* using PageGetItemIdCareful() call.
2169+
*
21482170
* If this function returns false, convention is that caller throws error due
21492171
* to corruption.
21502172
*/
@@ -2165,6 +2187,9 @@ invariant_leq_offset(BtreeCheckState *state, BTScanInsert key,
21652187
* Does the invariant hold that the key is strictly greater than a given lower
21662188
* bound offset item?
21672189
*
2190+
* Caller should have verified that lowerbound's item pointer is consistent
2191+
* using PageGetItemIdCareful() call.
2192+
*
21682193
* If this function returns false, convention is that caller throws error due
21692194
* to corruption.
21702195
*/
@@ -2199,19 +2224,24 @@ invariant_g_offset(BtreeCheckState *state, BTScanInsert key,
21992224
*
22002225
* Caller's non-target page is a child page of the target, checked as part of
22012226
* checking a property of the target page (i.e. the key comes from the
2202-
* target).
2227+
* target). Verifies line pointer on behalf of caller.
22032228
*
22042229
* If this function returns false, convention is that caller throws error due
22052230
* to corruption.
22062231
*/
22072232
staticinlinebool
22082233
invariant_l_nontarget_offset(BtreeCheckState*state,BTScanInsertkey,
2209-
Pagenontarget,OffsetNumberupperbound)
2234+
BlockNumbernontargetblock,Pagenontarget,
2235+
OffsetNumberupperbound)
22102236
{
2237+
ItemIditemid;
22112238
int32cmp;
22122239

22132240
Assert(key->pivotsearch);
22142241

2242+
/* Verify line pointer before checking tuple */
2243+
itemid=PageGetItemIdCareful(state,nontargetblock,nontarget,
2244+
upperbound);
22152245
cmp=_bt_compare(state->rel,key,nontarget,upperbound);
22162246

22172247
/* pg_upgrade'd indexes may legally have equal sibling tuples */
@@ -2221,14 +2251,12 @@ invariant_l_nontarget_offset(BtreeCheckState *state, BTScanInsert key,
22212251
/* See invariant_l_offset() for an explanation of this extra step */
22222252
if (cmp==0)
22232253
{
2224-
ItemIditemid;
22252254
IndexTuplechild;
22262255
intuppnkeyatts;
22272256
ItemPointerchildheaptid;
22282257
BTPageOpaquecopaque;
22292258
boolnonpivot;
22302259

2231-
itemid=PageGetItemId(nontarget,upperbound);
22322260
child= (IndexTuple)PageGetItem(nontarget,itemid);
22332261
copaque= (BTPageOpaque)PageGetSpecialPointer(nontarget);
22342262
nonpivot=P_ISLEAF(copaque)&&upperbound >=P_FIRSTDATAKEY(copaque);
@@ -2426,6 +2454,55 @@ bt_mkscankey_pivotsearch(Relation rel, IndexTuple itup)
24262454
returnskey;
24272455
}
24282456

2457+
/*
2458+
* PageGetItemId() wrapper that validates returned line pointer.
2459+
*
2460+
* Buffer page/page item access macros generally trust that line pointers are
2461+
* not corrupt, which might cause problems for verification itself. For
2462+
* example, there is no bounds checking in PageGetItem(). Passing it a
2463+
* corrupt line pointer can cause it to return a tuple/pointer that is unsafe
2464+
* to dereference.
2465+
*
2466+
* Validating line pointers before tuples avoids undefined behavior and
2467+
* assertion failures with corrupt indexes, making the verification process
2468+
* more robust and predictable.
2469+
*/
2470+
staticItemId
2471+
PageGetItemIdCareful(BtreeCheckState*state,BlockNumberblock,Pagepage,
2472+
OffsetNumberoffset)
2473+
{
2474+
ItemIditemid=PageGetItemId(page,offset);
2475+
2476+
if (ItemIdGetOffset(itemid)+ItemIdGetLength(itemid)>
2477+
BLCKSZ-sizeof(BTPageOpaqueData))
2478+
ereport(ERROR,
2479+
(errcode(ERRCODE_INDEX_CORRUPTED),
2480+
errmsg("line pointer points past end of tuple space in index \"%s\"",
2481+
RelationGetRelationName(state->rel)),
2482+
errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
2483+
block,offset,ItemIdGetOffset(itemid),
2484+
ItemIdGetLength(itemid),
2485+
ItemIdGetFlags(itemid))));
2486+
2487+
/*
2488+
* Verify that line pointer isn't LP_REDIRECT or LP_UNUSED, since nbtree
2489+
* never uses either. Verify that line pointer has storage, too, since
2490+
* even LP_DEAD items should within nbtree.
2491+
*/
2492+
if (ItemIdIsRedirected(itemid)|| !ItemIdIsUsed(itemid)||
2493+
ItemIdGetLength(itemid)==0)
2494+
ereport(ERROR,
2495+
(errcode(ERRCODE_INDEX_CORRUPTED),
2496+
errmsg("invalid line pointer storage in index \"%s\"",
2497+
RelationGetRelationName(state->rel)),
2498+
errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
2499+
block,offset,ItemIdGetOffset(itemid),
2500+
ItemIdGetLength(itemid),
2501+
ItemIdGetFlags(itemid))));
2502+
2503+
returnitemid;
2504+
}
2505+
24292506
/*
24302507
* BTreeTupleGetHeapTID() wrapper that lets caller enforce that a heap TID must
24312508
* be present in cases where that is mandatory.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp