v9_diffs

application/octet-stream

Filename: v9_diffs
Type: application/octet-stream
Part: 2
Message: Re: new heapcheck contrib module
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f5e68906b2..b7ea745964 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -113,6 +113,7 @@ verify_heapam(PG_FUNCTION_ARGS)
 	FullTransactionId nextFullXid;
 	Buffer		vmbuffer = InvalidBuffer;
 	Oid			relid;
+	bool		fatal = false;
 	bool		on_error_stop;
 	bool		skip_all_frozen = false;
 	bool		skip_all_visible = false;
@@ -208,6 +209,7 @@ verify_heapam(PG_FUNCTION_ARGS)
 	else
 	{
 		/* Main relation has no associated toast relation */
+		ctx.toastrel = NULL;
 		ctx.toast_indexes = NULL;
 		ctx.num_toast_indexes = 0;
 	}
@@ -234,15 +236,25 @@ verify_heapam(PG_FUNCTION_ARGS)
 		confess(&ctx, psprintf("relfrozenxid %u precedes global "
 							   "oldest valid xid %u ",
 							   ctx.relfrozenxid, ctx.oldestValidXid));
-		PG_RETURN_NULL();
+		fatal = true;
 	}
-
-	if (TransactionIdIsNormal(ctx.relminmxid) &&
+	else if (TransactionIdIsNormal(ctx.relminmxid) &&
 		TransactionIdPrecedes(ctx.relminmxid, ctx.oldestValidXid))
 	{
 		confess(&ctx, psprintf("relfrozenxid %u precedes global "
 							   "oldest valid xid %u ",
 							   ctx.relfrozenxid, ctx.oldestValidXid));
+		fatal = true;
+	}
+
+	if (fatal)
+	{
+		if (ctx.toast_indexes)
+			toast_close_indexes(ctx.toast_indexes, ctx.num_toast_indexes,
+								ShareUpdateExclusiveLock);
+		if (ctx.toastrel)
+			table_close(ctx.toastrel, ShareUpdateExclusiveLock);
+		relation_close(ctx.rel, ShareUpdateExclusiveLock);
 		PG_RETURN_NULL();
 	}
 
@@ -264,6 +276,7 @@ verify_heapam(PG_FUNCTION_ARGS)
 	{
 		int32		mapbits;
 		OffsetNumber maxoff;
+		PageHeader	ph;
 
 		/* Optionally skip over all-frozen or all-visible blocks */
 		if (skip_all_frozen || skip_all_visible)
@@ -281,6 +294,7 @@ verify_heapam(PG_FUNCTION_ARGS)
 										RBM_NORMAL, ctx.bstrategy);
 		LockBuffer(ctx.buffer, BUFFER_LOCK_SHARE);
 		ctx.page = BufferGetPage(ctx.buffer);
+		ph = (PageHeader) ctx.page;
 
 		/* We must unlock the page from the prior iteration, if any */
 		Assert(ctx.blkno == InvalidBlockNumber || ctx.buffer != InvalidBuffer);
@@ -302,12 +316,33 @@ verify_heapam(PG_FUNCTION_ARGS)
 		{
 			ctx.itemid = PageGetItemId(ctx.page, ctx.offnum);
 
-			/* Skip over unused/dead/redirected line pointers */
-			if (!ItemIdIsUsed(ctx.itemid) ||
-				ItemIdIsDead(ctx.itemid) ||
-				ItemIdIsRedirected(ctx.itemid))
+			/* Skip over unused/dead line pointers */
+			if (!ItemIdIsUsed(ctx.itemid) || ItemIdIsDead(ctx.itemid))
 				continue;
 
+			/*
+			 * If this line pointer has been redirected, check that it redirects
+			 * to a valid offset within the line pointer array.
+			 */
+			if (ItemIdIsRedirected(ctx.itemid))
+			{
+				uint16 redirect = ItemIdGetRedirect(ctx.itemid);
+				if (redirect <= SizeOfPageHeaderData || redirect >= ph->pd_lower)
+				{
+					confess(&ctx, psprintf(
+						"Invalid redirect line pointer offset %u out of bounds",
+							(unsigned) redirect));
+					continue;
+				}
+				if ((redirect - SizeOfPageHeaderData) % sizeof(uint16))
+				{
+					confess(&ctx, psprintf(
+						"Invalid redirect line pointer offset %u bad alignment",
+							(unsigned) redirect));
+				}
+				continue;
+			}
+
 			/* Set up context information about this next tuple */
 			ctx.lp_len = ItemIdGetLength(ctx.itemid);
 			ctx.tuphdr = (HeapTupleHeader) PageGetItem(ctx.page, ctx.itemid);
@@ -637,7 +672,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext * ctx)
 	{
 		/* should never happen */
 		confess(ctx,
-				pstrdup("toast chunk is neither short nor extended"));
+				pstrdup("corrupt toast chunk va_header"));
 		return;
 	}
 
@@ -972,16 +1007,29 @@ check_tuple(HeapCheckContext * ctx)
 	 * length nulls bitmap field t_bits does not overflow the allowed space.
 	 * We don't know if the corruption is in the natts field or the infomask
 	 * bit HEAP_HASNULL.
+	 *
+	 * If the tuple does not have nulls, check that no space has been
+	 * reserved for the null bitmap.
 	 */
-	if (infomask & HEAP_HASNULL &&
-		SizeofHeapTupleHeader + BITMAPLEN(ctx->natts) > ctx->tuphdr->t_hoff)
+	if (infomask & HEAP_HASNULL)
 	{
-		confess(ctx, psprintf("SizeofHeapTupleHeader + "
-							  "BITMAPLEN(natts) > t_hoff "
-							  "(%u + %u > %u)",
-							  (unsigned) SizeofHeapTupleHeader,
-							  BITMAPLEN(ctx->natts),
-							  ctx->tuphdr->t_hoff));
+		if (SizeofHeapTupleHeader + BITMAPLEN(ctx->natts) > ctx->tuphdr->t_hoff)
+		{
+			confess(ctx, psprintf("SizeofHeapTupleHeader + "
+								  "BITMAPLEN(natts) > t_hoff "
+								  "(%u + %u > %u)",
+								  (unsigned) SizeofHeapTupleHeader,
+								  BITMAPLEN(ctx->natts),
+								  ctx->tuphdr->t_hoff));
+			fatal = true;
+		}
+	}
+	else if (MAXALIGN(ctx->tuphdr->t_hoff) != MAXALIGN(SizeofHeapTupleHeader))
+	{
+		confess(ctx,
+			psprintf("t_hoff = %u in tuple without nulls (expected %u)",
+						(unsigned) MAXALIGN(ctx->tuphdr->t_hoff),
+						(unsigned) MAXALIGN(SizeofHeapTupleHeader)));
 		fatal = true;
 	}
 
@@ -1031,6 +1079,4 @@ check_tuple(HeapCheckContext * ctx)
 		if (!check_tuple_attribute(ctx))
 			break;
 	}
-	ctx->offset = -1;
-	ctx->attnum = -1;
 }
diff --git a/contrib/pg_amcheck/t/004_verify_heapam.pl b/contrib/pg_amcheck/t/004_verify_heapam.pl
index b2c1f36928..08bce6e68e 100644
--- a/contrib/pg_amcheck/t/004_verify_heapam.pl
+++ b/contrib/pg_amcheck/t/004_verify_heapam.pl
@@ -381,8 +381,11 @@ is ($result,
 0|2|8064|1|58|||tuple xmin = 4026531839 precedes relation relfrozenxid = $relfrozenxid
 0|3|8000|1|58|||tuple xmax = 4026531839 precedes relation relfrozenxid = $relfrozenxid
 0|4|7936|1|58|||t_hoff > lp_len (152 > 58)
+0|4|7936|1|58|||t_hoff = 152 in tuple without nulls (expected 24)
 0|5|7872|1|58|||t_hoff not max-aligned (27)
+0|5|7872|1|58|||t_hoff = 32 in tuple without nulls (expected 24)
 0|6|7808|1|58|||t_hoff < SizeofHeapTupleHeader (16 < 23)
+0|6|7808|1|58|||t_hoff = 16 in tuple without nulls (expected 24)
 0|7|7744|1|58|||t_hoff < SizeofHeapTupleHeader (21 < 23)
 0|7|7744|1|58|||t_hoff not max-aligned (21)
 0|8|7680|1|58|||relation natts < tuple natts (3 < 2047)