@@ -105,6 +105,7 @@ typedef struct HeapCheckContext
105105 OffsetNumber offnum ;
106106 ItemId itemid ;
107107 uint16 lp_len ;
108+ uint16 lp_off ;
108109 HeapTupleHeader tuphdr ;
109110 int natts ;
110111
@@ -247,6 +248,13 @@ verify_heapam(PG_FUNCTION_ARGS)
247248 memset (& ctx , 0 , sizeof (HeapCheckContext ));
248249 ctx .cached_xid = InvalidTransactionId ;
249250
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+
250258 /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
251259 old_context = MemoryContextSwitchTo (rsinfo -> econtext -> ecxt_per_query_memory );
252260 random_access = (rsinfo -> allowedModes & SFRM_Materialize_Random ) != 0 ;
@@ -378,14 +386,22 @@ verify_heapam(PG_FUNCTION_ARGS)
378386
379387 /*
380388 * 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
382390 */
383391 if (ItemIdIsRedirected (ctx .itemid ))
384392 {
385393 OffsetNumber rdoffnum = ItemIdGetRedirect (ctx .itemid );
386394 ItemId rditem ;
387395
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 )
389405 {
390406 report_corruption (& ctx ,
391407 psprintf ("line pointer redirection to item at offset %u exceeds maximum offset %u" ,
@@ -401,8 +417,36 @@ verify_heapam(PG_FUNCTION_ARGS)
401417 continue ;
402418 }
403419
404- /* Set up context information about this next tuple */
420+ /* Sanity-check the line pointer's offset and length values */
405421 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 */
406450 ctx .tuphdr = (HeapTupleHeader ) PageGetItem (ctx .page , ctx .itemid );
407451 ctx .natts = HeapTupleHeaderGetNatts (ctx .tuphdr );
408452
@@ -1088,25 +1132,6 @@ check_tuple(HeapCheckContext *ctx)
10881132 bool fatal = false;
10891133 uint16 infomask = ctx -> tuphdr -> t_infomask ;
10901134
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-
11101135 /* If xmin is normal, it should be within valid range */
11111136 xmin = HeapTupleHeaderGetXmin (ctx -> tuphdr );
11121137 switch (get_xid_status (xmin , ctx , NULL ))
@@ -1256,6 +1281,9 @@ check_tuple(HeapCheckContext *ctx)
12561281 for (ctx -> attnum = 0 ; ctx -> attnum < ctx -> natts ; ctx -> attnum ++ )
12571282 if (!check_tuple_attribute (ctx ))
12581283 break ; /* cannot continue */
1284+
1285+ /* revert attnum to -1 until we again examine individual attributes */
1286+ ctx -> attnum = -1 ;
12591287}
12601288
12611289/*
@@ -1393,9 +1421,9 @@ get_xid_status(TransactionId xid, HeapCheckContext *ctx,
13931421 if (!fxid_in_cached_range (fxid , ctx ))
13941422 {
13951423 /*
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.
13991427 */
14001428 update_cached_xid_range (ctx );
14011429 fxid = FullTransactionIdFromXidAndCtx (xid , ctx );
0 commit comments