pg_resetwal: Prevent division-by-zero errors
authorPeter Eisentraut <peter_e@gmx.net>
Fri, 23 Mar 2018 14:10:49 +0000 (10:10 -0400)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 23 Mar 2018 14:14:25 +0000 (10:14 -0400)
Handle the case where the pg_control file specifies a WAL segment size
of 0 bytes.  This would previously have led to a division by zero error.
Change this to assume the whole file is corrupt and go to guess
everything.

Discussion: https://www.postgresql.org/message-id/a6163ad7-cc99-fdd1-dfad-25df73032ab8%402ndquadrant.com

src/bin/pg_resetwal/pg_resetwal.c
src/bin/pg_resetwal/t/002_corrupted.pl [new file with mode: 0644]

index a132cf2e9fd64aba2c5e59568096e213c7ffcd29..c99e7a8db1e21343ed46f62e29dbabd393cd4fed 100644 (file)
@@ -601,7 +601,7 @@ ReadControlFile(void)
            fprintf(stderr,
                    _("%s: pg_control specifies invalid WAL segment size (%d bytes); proceed with caution \n"),
                    progname, WalSegSz);
-           guessed = true;
+           return false;
        }
 
        return true;
@@ -678,7 +678,7 @@ GuessControlValues(void)
    ControlFile.floatFormat = FLOATFORMAT_VALUE;
    ControlFile.blcksz = BLCKSZ;
    ControlFile.relseg_size = RELSEG_SIZE;
-   ControlFile.xlog_blcksz = XLOG_BLCKSZ;
+   WalSegSz = ControlFile.xlog_blcksz = XLOG_BLCKSZ;
    ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
    ControlFile.nameDataLen = NAMEDATALEN;
    ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
diff --git a/src/bin/pg_resetwal/t/002_corrupted.pl b/src/bin/pg_resetwal/t/002_corrupted.pl
new file mode 100644 (file)
index 0000000..9df5574
--- /dev/null
@@ -0,0 +1,47 @@
+# Tests for handling a corrupted pg_control
+
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 6;
+
+my $node = get_new_node('main');
+$node->init;
+
+my $pg_control = $node->data_dir . '/global/pg_control';
+my $size = (stat($pg_control))[7];
+
+# Read out the head of the file to get PG_CONTROL_VERSION in
+# particular.
+my $data;
+open my $fh, '<', $pg_control or BAIL_OUT($!);
+binmode $fh;
+read $fh, $data, 16;
+close $fh;
+
+# Fill pg_control with zeros
+open $fh, '>', $pg_control or BAIL_OUT($!);
+binmode $fh;
+print $fh pack("x[$size]");
+close $fh;
+
+command_checks_all([ 'pg_resetwal', '-n', $node->data_dir ],
+                   0,
+                   [ qr/pg_control version number/ ],
+                   [ qr/pg_resetwal: pg_control exists but is broken or wrong version; ignoring it/ ],
+                   'processes corrupted pg_control all zeroes');
+
+# Put in the previously saved header data.  This uses a different code
+# path internally, allowing us to process a zero WAL segment size.
+open $fh, '>', $pg_control or BAIL_OUT($!);
+binmode $fh;
+print $fh $data, pack("x[" . ($size - 16) . "]");
+close $fh;
+
+command_checks_all([ 'pg_resetwal', '-n', $node->data_dir ],
+                   0,
+                   [ qr/pg_control version number/ ],
+                   [ qr/\Qpg_resetwal: pg_control specifies invalid WAL segment size (0 bytes); proceed with caution\E/ ],
+                   'processes zero WAL segment size');