aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/amcheck/verify_heapam.c46
-rw-r--r--src/bin/pg_amcheck/t/004_verify_heapam.pl24
2 files changed, 67 insertions, 3 deletions
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index e84ecd1c981..774a70f63df 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -30,6 +30,9 @@ PG_FUNCTION_INFO_V1(verify_heapam);
/* The number of columns in tuples returned by verify_heapam */
#define HEAPCHECK_RELATION_COLS 4
+/* The largest valid toast va_rawsize */
+#define VARLENA_SIZE_LIMIT 0x3FFFFFFF
+
/*
* Despite the name, we use this for reporting problems with both XIDs and
* MXIDs.
@@ -1414,6 +1417,49 @@ check_tuple_attribute(HeapCheckContext *ctx)
*/
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
+ /* Toasted attributes too large to be untoasted should never be stored */
+ if (toast_pointer.va_rawsize > VARLENA_SIZE_LIMIT)
+ report_corruption(ctx,
+ psprintf("toast value %u rawsize %u exceeds limit %u",
+ toast_pointer.va_valueid,
+ toast_pointer.va_rawsize,
+ VARLENA_SIZE_LIMIT));
+
+ if (VARATT_IS_COMPRESSED(&toast_pointer))
+ {
+ ToastCompressionId cmid;
+ bool valid = false;
+
+ /* Compression should never expand the attribute */
+ if (VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) > toast_pointer.va_rawsize - VARHDRSZ)
+ report_corruption(ctx,
+ psprintf("toast value %u external size %u exceeds maximum expected for rawsize %u",
+ toast_pointer.va_valueid,
+ VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer),
+ toast_pointer.va_rawsize));
+
+ /* Compressed attributes should have a valid compression method */
+ cmid = TOAST_COMPRESS_METHOD(&toast_pointer);
+ switch (cmid)
+ {
+ /* List of all valid compression method IDs */
+ case TOAST_PGLZ_COMPRESSION_ID:
+ case TOAST_LZ4_COMPRESSION_ID:
+ valid = true;
+ break;
+
+ /* Recognized but invalid compression method ID */
+ case TOAST_INVALID_COMPRESSION_ID:
+ break;
+
+ /* Intentionally no default here */
+ }
+ if (!valid)
+ report_corruption(ctx,
+ psprintf("toast value %u has invalid compression method id %d",
+ toast_pointer.va_valueid, cmid));
+ }
+
/* The tuple header better claim to contain toasted values */
if (!(infomask & HEAP_HASEXTERNAL))
{
diff --git a/src/bin/pg_amcheck/t/004_verify_heapam.pl b/src/bin/pg_amcheck/t/004_verify_heapam.pl
index 4ca7ed297c0..ae729336d26 100644
--- a/src/bin/pg_amcheck/t/004_verify_heapam.pl
+++ b/src/bin/pg_amcheck/t/004_verify_heapam.pl
@@ -218,7 +218,7 @@ my $rel = $node->safe_psql('postgres',
my $relpath = "$pgdata/$rel";
# Insert data and freeze public.test
-use constant ROWCOUNT => 16;
+use constant ROWCOUNT => 18;
$node->safe_psql(
'postgres', qq(
INSERT INTO public.test (a, b, c)
@@ -297,7 +297,7 @@ close($file)
$node->start;
# Ok, Xids and page layout look ok. We can run corruption tests.
-plan tests => 19;
+plan tests => 21;
# Check that pg_amcheck runs against the uncorrupted table without error.
$node->command_ok(
@@ -504,7 +504,7 @@ for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
push @expected,
qr/${header}multitransaction ID 4 equals or exceeds next valid multitransaction ID 1/;
}
- elsif ($offnum == 15) # Last offnum must equal ROWCOUNT
+ elsif ($offnum == 15)
{
# Set both HEAP_XMAX_COMMITTED and HEAP_XMAX_IS_MULTI
$tup->{t_infomask} |= HEAP_XMAX_COMMITTED;
@@ -514,6 +514,24 @@ for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
push @expected,
qr/${header}multitransaction ID 4000000000 precedes relation minimum multitransaction ID threshold 1/;
}
+ elsif ($offnum == 16)
+ {
+ # Set raw size too large
+ $tup->{c_va_rawsize} = 1073741824;
+
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value \d+ rawsize 1073741824 exceeds limit 1073741823/;
+ }
+ elsif ($offnum == 17) # Last offnum should equal ROWCOUNT-1
+ {
+ # Set raw size too small.
+ $tup->{c_va_rawsize} = 9998;
+
+ $header = header(0, $offnum, 2);
+ push @expected,
+ qr/${header}toast value \d+ external size 10000 exceeds maximum expected for rawsize 9998/;
+ }
write_tuple($file, $offset, $tup);
}
close($file)