Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4731d84

Browse files
committed
pg_controldata: Prevent division-by-zero errors
If the control file is corrupted and specifies the WAL segment size to be 0 bytes, calculating the latest checkpoint's REDO WAL file will fail with a division-by-zero error. Show it as "???" instead. Also reword the warning message a bit and send it to stdout, like the other pre-existing warning messages. Add some tests for dealing with a corrupted pg_control file. Author: Nathan Bossart <bossartn@amazon.com>, tests by me
1 parent 5616300 commit 4731d84

File tree

2 files changed

+38
-9
lines changed

2 files changed

+38
-9
lines changed

src/bin/pg_controldata/pg_controldata.c

+18-8
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ main(int argc, char *argv[])
9595
char mock_auth_nonce_str[MOCK_AUTH_NONCE_LEN * 2 + 1];
9696
const char *strftime_fmt = "%c";
9797
const char *progname;
98-
XLogSegNo segno;
9998
char xlogfilename[MAXFNAMELEN];
10099
int c;
101100
int i;
@@ -169,10 +168,11 @@ main(int argc, char *argv[])
169168
WalSegSz = ControlFile->xlog_seg_size;
170169

171170
if (!IsValidWalSegSize(WalSegSz))
172-
fprintf(stderr,
173-
_("WARNING: WAL segment size specified, %d bytes, is not a power of two between 1MB and 1GB.\n"
174-
"The file is corrupt and the results below are untrustworthy.\n"),
175-
WalSegSz);
171+
printf(_("WARNING: invalid WAL segment size\n"
172+
"The WAL segment size stored in the file, %d bytes, is not a power of two\n"
173+
"between 1 MB and 1 GB. The file is corrupt and the results below are\n"
174+
"untrustworthy.\n\n"),
175+
WalSegSz);
176176

177177
/*
178178
* This slightly-chintzy coding will work as long as the control file
@@ -193,10 +193,20 @@ main(int argc, char *argv[])
193193
/*
194194
* Calculate name of the WAL file containing the latest checkpoint's REDO
195195
* start point.
196+
*
197+
* A corrupted control file could report a WAL segment size of 0, and to
198+
* guard against division by zero, we need to treat that specially.
196199
*/
197-
XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz);
198-
XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
199-
segno, WalSegSz);
200+
if (WalSegSz != 0)
201+
{
202+
XLogSegNo segno;
203+
204+
XLByteToSeg(ControlFile->checkPointCopy.redo, segno, WalSegSz);
205+
XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID,
206+
segno, WalSegSz);
207+
}
208+
else
209+
strcpy(xlogfilename, _("???"));
200210

201211
/*
202212
* Format system_identifier and mock_authentication_nonce separately to

src/bin/pg_controldata/t/001_pg_controldata.pl

+20-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use warnings;
33
use PostgresNode;
44
use TestLib;
5-
use Test::More tests => 13;
5+
use Test::More tests => 17;
66

77
program_help_ok('pg_controldata');
88
program_version_ok('pg_controldata');
@@ -16,3 +16,22 @@
1616

1717
command_like([ 'pg_controldata', $node->data_dir ],
1818
qr/checkpoint/, 'pg_controldata produces output');
19+
20+
21+
# check with a corrupted pg_control
22+
23+
my $pg_control = $node->data_dir . '/global/pg_control';
24+
my $size = (stat($pg_control))[7];
25+
26+
open my $fh, '>', $pg_control or BAIL_OUT($!);
27+
binmode $fh;
28+
# fill file with zeros
29+
print $fh pack("x[$size]");
30+
close $fh;
31+
32+
command_checks_all([ 'pg_controldata', $node->data_dir ],
33+
0,
34+
[ qr/WARNING: Calculated CRC checksum does not match value stored in file/,
35+
qr/WARNING: invalid WAL segment size/ ],
36+
[ qr/^$/ ],
37+
'pg_controldata with corrupted pg_control');

0 commit comments

Comments
 (0)