@@ -105,6 +105,7 @@ typedef struct HeapCheckContext
105
105
OffsetNumber offnum ;
106
106
ItemId itemid ;
107
107
uint16 lp_len ;
108
+ uint16 lp_off ;
108
109
HeapTupleHeader tuphdr ;
109
110
int natts ;
110
111
@@ -247,6 +248,13 @@ verify_heapam(PG_FUNCTION_ARGS)
247
248
memset (& ctx , 0 , sizeof (HeapCheckContext ));
248
249
ctx .cached_xid = InvalidTransactionId ;
249
250
251
+ /*
252
+ * If we report corruption when not examining some individual attribute,
253
+ * we need attnum to be reported as NULL. Set that up before any
254
+ * corruption reporting might happen.
255
+ */
256
+ ctx .attnum = -1 ;
257
+
250
258
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
251
259
old_context = MemoryContextSwitchTo (rsinfo -> econtext -> ecxt_per_query_memory );
252
260
random_access = (rsinfo -> allowedModes & SFRM_Materialize_Random ) != 0 ;
@@ -378,14 +386,22 @@ verify_heapam(PG_FUNCTION_ARGS)
378
386
379
387
/*
380
388
* If this line pointer has been redirected, check that it
381
- * redirects to a valid offset within the line pointer array.
389
+ * redirects to a valid offset within the line pointer array
382
390
*/
383
391
if (ItemIdIsRedirected (ctx .itemid ))
384
392
{
385
393
OffsetNumber rdoffnum = ItemIdGetRedirect (ctx .itemid );
386
394
ItemId rditem ;
387
395
388
- if (rdoffnum < FirstOffsetNumber || rdoffnum > maxoff )
396
+ if (rdoffnum < FirstOffsetNumber )
397
+ {
398
+ report_corruption (& ctx ,
399
+ psprintf ("line pointer redirection to item at offset %u precedes minimum offset %u" ,
400
+ (unsigned ) rdoffnum ,
401
+ (unsigned ) FirstOffsetNumber ));
402
+ continue ;
403
+ }
404
+ if (rdoffnum > maxoff )
389
405
{
390
406
report_corruption (& ctx ,
391
407
psprintf ("line pointer redirection to item at offset %u exceeds maximum offset %u" ,
@@ -401,8 +417,36 @@ verify_heapam(PG_FUNCTION_ARGS)
401
417
continue ;
402
418
}
403
419
404
- /* Set up context information about this next tuple */
420
+ /* Sanity-check the line pointer's offset and length values */
405
421
ctx .lp_len = ItemIdGetLength (ctx .itemid );
422
+ ctx .lp_off = ItemIdGetOffset (ctx .itemid );
423
+
424
+ if (ctx .lp_off != MAXALIGN (ctx .lp_off ))
425
+ {
426
+ report_corruption (& ctx ,
427
+ psprintf ("line pointer to page offset %u is not maximally aligned" ,
428
+ ctx .lp_off ));
429
+ continue ;
430
+ }
431
+ if (ctx .lp_len < MAXALIGN (SizeofHeapTupleHeader ))
432
+ {
433
+ report_corruption (& ctx ,
434
+ psprintf ("line pointer length %u is less than the minimum tuple header size %u" ,
435
+ ctx .lp_len ,
436
+ (unsigned ) MAXALIGN (SizeofHeapTupleHeader )));
437
+ continue ;
438
+ }
439
+ if (ctx .lp_off + ctx .lp_len > BLCKSZ )
440
+ {
441
+ report_corruption (& ctx ,
442
+ psprintf ("line pointer to page offset %u with length %u ends beyond maximum page offset %u" ,
443
+ ctx .lp_off ,
444
+ ctx .lp_len ,
445
+ (unsigned ) BLCKSZ ));
446
+ continue ;
447
+ }
448
+
449
+ /* It should be safe to examine the tuple's header, at least */
406
450
ctx .tuphdr = (HeapTupleHeader ) PageGetItem (ctx .page , ctx .itemid );
407
451
ctx .natts = HeapTupleHeaderGetNatts (ctx .tuphdr );
408
452
@@ -1088,25 +1132,6 @@ check_tuple(HeapCheckContext *ctx)
1088
1132
bool fatal = false;
1089
1133
uint16 infomask = ctx -> tuphdr -> t_infomask ;
1090
1134
1091
- /*
1092
- * If we report corruption before iterating over individual attributes, we
1093
- * need attnum to be reported as NULL. Set that up before any corruption
1094
- * reporting might happen.
1095
- */
1096
- ctx -> attnum = -1 ;
1097
-
1098
- /*
1099
- * If the line pointer for this tuple does not reserve enough space for a
1100
- * complete tuple header, we dare not read the tuple header.
1101
- */
1102
- if (ctx -> lp_len < MAXALIGN (SizeofHeapTupleHeader ))
1103
- {
1104
- report_corruption (ctx ,
1105
- psprintf ("line pointer length %u is less than the minimum tuple header size %u" ,
1106
- ctx -> lp_len , (uint32 ) MAXALIGN (SizeofHeapTupleHeader )));
1107
- return ;
1108
- }
1109
-
1110
1135
/* If xmin is normal, it should be within valid range */
1111
1136
xmin = HeapTupleHeaderGetXmin (ctx -> tuphdr );
1112
1137
switch (get_xid_status (xmin , ctx , NULL ))
@@ -1256,6 +1281,9 @@ check_tuple(HeapCheckContext *ctx)
1256
1281
for (ctx -> attnum = 0 ; ctx -> attnum < ctx -> natts ; ctx -> attnum ++ )
1257
1282
if (!check_tuple_attribute (ctx ))
1258
1283
break ; /* cannot continue */
1284
+
1285
+ /* revert attnum to -1 until we again examine individual attributes */
1286
+ ctx -> attnum = -1 ;
1259
1287
}
1260
1288
1261
1289
/*
@@ -1393,9 +1421,9 @@ get_xid_status(TransactionId xid, HeapCheckContext *ctx,
1393
1421
if (!fxid_in_cached_range (fxid , ctx ))
1394
1422
{
1395
1423
/*
1396
- * We may have been checking against stale values. Update the
1397
- * cached range to be sure, and since we relied on the cached
1398
- * range when we performed the full xid conversion, reconvert.
1424
+ * We may have been checking against stale values. Update the cached
1425
+ * range to be sure, and since we relied on the cached range when we
1426
+ * performed the full xid conversion, reconvert.
1399
1427
*/
1400
1428
update_cached_xid_range (ctx );
1401
1429
fxid = FullTransactionIdFromXidAndCtx (xid , ctx );
0 commit comments