Skip to content

Commit 321633e

Browse files
committed
Fix more portability issues in new amcheck code.
verify_heapam() wasn't being careful to sanity-check tuple line pointers before using them, resulting in SIGBUS on alignment-picky architectures. Fix that, add some more test coverage. Mark Dilger, some tweaking by me Discussion: https://postgr.es/m/30B8E99A-2D9C-48D4-A55C-741C9D5F1563@enterprisedb.com
1 parent 1b62d0f commit 321633e

File tree

2 files changed

+73
-35
lines changed

2 files changed

+73
-35
lines changed

contrib/amcheck/t/001_verify_heapam.pl

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use PostgresNode;
55
use TestLib;
66

7-
use Test::More tests => 55;
7+
use Test::More tests => 79;
88

99
my ($node, $result);
1010

@@ -109,13 +109,17 @@ sub corrupt_first_page
109109
or BAIL_OUT("open failed: $!");
110110
binmode $fh;
111111

112-
# Corrupt two line pointers. To be stable across platforms, we use
113-
# 0x55555555 and 0xAAAAAAAA for the two, which are bitwise reverses of each
114-
# other.
112+
# Corrupt some line pointers. The values are chosen to hit the
113+
# various line-pointer-corruption checks in verify_heapam.c
114+
# on both little-endian and big-endian architectures.
115115
seek($fh, 32, 0)
116116
or BAIL_OUT("seek failed: $!");
117-
syswrite($fh, pack("L*", 0x55555555, 0xAAAAAAAA))
118-
or BAIL_OUT("syswrite failed: $!");
117+
syswrite(
118+
$fh,
119+
pack("L*",
120+
0xAAA15550, 0xAAA0D550, 0x00010000,
121+
0x00008000, 0x0000800F, 0x001e8000)
122+
) or BAIL_OUT("syswrite failed: $!");
119123
close($fh)
120124
or BAIL_OUT("close failed: $!");
121125

@@ -126,17 +130,23 @@ sub detects_heap_corruption
126130
{
127131
my ($function, $testname) = @_;
128132

129-
detects_corruption($function, $testname,
130-
qr/line pointer redirection to item at offset \d+ exceeds maximum offset \d+/
133+
detects_corruption(
134+
$function,
135+
$testname,
136+
qr/line pointer redirection to item at offset \d+ precedes minimum offset \d+/,
137+
qr/line pointer redirection to item at offset \d+ exceeds maximum offset \d+/,
138+
qr/line pointer to page offset \d+ is not maximally aligned/,
139+
qr/line pointer length \d+ is less than the minimum tuple header size \d+/,
140+
qr/line pointer to page offset \d+ with length \d+ ends beyond maximum page offset \d+/,
131141
);
132142
}
133143

134144
sub detects_corruption
135145
{
136-
my ($function, $testname, $re) = @_;
146+
my ($function, $testname, @re) = @_;
137147

138148
my $result = $node->safe_psql('postgres', qq(SELECT * FROM $function));
139-
like($result, $re, $testname);
149+
like($result, $_, $testname) for (@re);
140150
}
141151

142152
sub detects_no_corruption

contrib/amcheck/verify_heapam.c

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy