amcheck: Fix a few bugs in new update chain validation.
authorRobert Haas <[email protected]>
Thu, 23 Mar 2023 16:52:33 +0000 (12:52 -0400)
committerRobert Haas <[email protected]>
Thu, 23 Mar 2023 17:03:03 +0000 (13:03 -0400)
We shouldn't set successor[whatever] to an offset number that is less
than FirstOffsetNumber or more than maxoff. We already avoided that
for redirects, but not for CTID links. Allowing bad offset numbers
into the successor[] array causes core dumps.

We shouldn't use HeapTupleHeaderIsHotUpdated() because it checks
stuff other than the status of the infomask2 bit HEAP_HOT_UPDATED.
We only care about the status of that bit, not the other stuff
that HeapTupleHeaderIsHotUpdated() checks. This mistake can cause
verify_heapam() to report corruption when none is present.

The first hunk of this patch was written by me. The other two were
written by Andres Freund. This could probably do with more review
before commit, but I'd like to try to get the buildfarm green again
sooner rather than later.

Discussion: http://postgr.es/m/20230322204552[email protected]

contrib/amcheck/verify_heapam.c

index 663fb65dee6fb372bb7deb50eda103cc9be85fa7..1b8607c6cc8925ea65fdc1c2bd975bc7c74c7933 100644 (file)
@@ -543,7 +543,8 @@ verify_heapam(PG_FUNCTION_ARGS)
                         */
                        nextblkno = ItemPointerGetBlockNumber(&(ctx.tuphdr)->t_ctid);
                        nextoffnum = ItemPointerGetOffsetNumber(&(ctx.tuphdr)->t_ctid);
-                       if (nextblkno == ctx.blkno && nextoffnum != ctx.offnum)
+                       if (nextblkno == ctx.blkno && nextoffnum != ctx.offnum &&
+                               nextoffnum >= FirstOffsetNumber && nextoffnum <= maxoff)
                                successor[ctx.offnum] = nextoffnum;
                }
 
@@ -665,15 +666,18 @@ verify_heapam(PG_FUNCTION_ARGS)
                         * tuple should be marked as a heap-only tuple. Conversely, if the
                         * current tuple isn't marked as HOT-updated, then the next tuple
                         * shouldn't be marked as a heap-only tuple.
+                        *
+                        * NB: Can't use HeapTupleHeaderIsHotUpdated() as it checks if
+                        * hint bits indicate xmin/xmax aborted.
                         */
-                       if (!HeapTupleHeaderIsHotUpdated(curr_htup) &&
+                       if (!(curr_htup->t_infomask2 & HEAP_HOT_UPDATED) &&
                                HeapTupleHeaderIsHeapOnly(next_htup))
                        {
                                report_corruption(&ctx,
                                                                  psprintf("non-heap-only update produced a heap-only tuple at offset %u",
                                                                                   (unsigned) nextoffnum));
                        }
-                       if (HeapTupleHeaderIsHotUpdated(curr_htup) &&
+                       if ((curr_htup->t_infomask2 & HEAP_HOT_UPDATED) &&
                                !HeapTupleHeaderIsHeapOnly(next_htup))
                        {
                                report_corruption(&ctx,