]> granicus.if.org Git - postgresql/commitdiff
Add static assertions about pg_control fitting into one disk sector.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Jul 2017 20:16:57 +0000 (16:16 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Jul 2017 20:16:57 +0000 (16:16 -0400)
When pg_control was first designed, sizeof(ControlFileData) was small
enough that a comment seemed like plenty to document the assumption that
it'd fit into one disk sector.  Now it's nearly 300 bytes, raising the
possibility that somebody would carelessly add enough stuff to create
a problem.  Let's add a StaticAssertStmt() to ensure that the situation
doesn't pass unnoticed if it ever occurs.

While at it, rename PG_CONTROL_SIZE to PG_CONTROL_FILE_SIZE to make it
clearer what that symbol means, and convert the existing runtime
comparisons of sizeof(ControlFileData) vs. PG_CONTROL_FILE_SIZE to be
static asserts --- we didn't have that technology when this code was
first written.

Discussion: https://postgr.es/m/9192.1500490591@sss.pgh.pa.us

src/backend/access/transam/xlog.c
src/bin/pg_resetwal/pg_resetwal.c
src/bin/pg_rewind/pg_rewind.c
src/include/catalog/pg_control.h

index 5b6cec8deed3c6b3e5f821c62a402c04cc89e097..3654543919f906e540c144c4fa6017542cead2b6 100644 (file)
@@ -4376,7 +4376,16 @@ static void
 WriteControlFile(void)
 {
        int                     fd;
-       char            buffer[PG_CONTROL_SIZE];        /* need not be aligned */
+       char            buffer[PG_CONTROL_FILE_SIZE];   /* need not be aligned */
+
+       /*
+        * Ensure that the size of the pg_control data structure is sane.  See the
+        * comments for these symbols in pg_control.h.
+        */
+       StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE,
+                                        "pg_control is too large for atomic disk writes");
+       StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE,
+                                        "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE");
 
        /*
         * Initialize version and compatibility-check fields
@@ -4409,16 +4418,13 @@ WriteControlFile(void)
        FIN_CRC32C(ControlFile->crc);
 
        /*
-        * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
-        * excess over sizeof(ControlFileData).  This reduces the odds of
+        * We write out PG_CONTROL_FILE_SIZE bytes into pg_control, zero-padding
+        * the excess over sizeof(ControlFileData).  This reduces the odds of
         * premature-EOF errors when reading pg_control.  We'll still fail when we
         * check the contents of the file, but hopefully with a more specific
         * error than "couldn't read pg_control".
         */
-       if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
-               elog(PANIC, "sizeof(ControlFileData) is larger than PG_CONTROL_SIZE; fix either one");
-
-       memset(buffer, 0, PG_CONTROL_SIZE);
+       memset(buffer, 0, PG_CONTROL_FILE_SIZE);
        memcpy(buffer, ControlFile, sizeof(ControlFileData));
 
        fd = BasicOpenFile(XLOG_CONTROL_FILE,
@@ -4432,7 +4438,7 @@ WriteControlFile(void)
 
        errno = 0;
        pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE);
-       if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
+       if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
        {
                /* if write didn't set errno, assume problem is no disk space */
                if (errno == 0)
index d4dd1d49ca59c87490615304f0f0ee5155c46789..ac678317795e82aea2ef783f02209d39588f6083 100644 (file)
@@ -552,9 +552,9 @@ ReadControlFile(void)
        }
 
        /* Use malloc to ensure we have a maxaligned buffer */
-       buffer = (char *) pg_malloc(PG_CONTROL_SIZE);
+       buffer = (char *) pg_malloc(PG_CONTROL_FILE_SIZE);
 
-       len = read(fd, buffer, PG_CONTROL_SIZE);
+       len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
        if (len < 0)
        {
                fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
@@ -834,7 +834,16 @@ static void
 RewriteControlFile(void)
 {
        int                     fd;
-       char            buffer[PG_CONTROL_SIZE];        /* need not be aligned */
+       char            buffer[PG_CONTROL_FILE_SIZE];   /* need not be aligned */
+
+       /*
+        * For good luck, apply the same static assertions as in backend's
+        * WriteControlFile().
+        */
+       StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE,
+                                        "pg_control is too large for atomic disk writes");
+       StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE,
+                                        "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE");
 
        /*
         * Adjust fields as needed to force an empty XLOG starting at
@@ -878,21 +887,13 @@ RewriteControlFile(void)
        FIN_CRC32C(ControlFile.crc);
 
        /*
-        * We write out PG_CONTROL_SIZE bytes into pg_control, zero-padding the
-        * excess over sizeof(ControlFileData).  This reduces the odds of
+        * We write out PG_CONTROL_FILE_SIZE bytes into pg_control, zero-padding
+        * the excess over sizeof(ControlFileData).  This reduces the odds of
         * premature-EOF errors when reading pg_control.  We'll still fail when we
         * check the contents of the file, but hopefully with a more specific
         * error than "couldn't read pg_control".
         */
-       if (sizeof(ControlFileData) > PG_CONTROL_SIZE)
-       {
-               fprintf(stderr,
-                               _("%s: internal error -- sizeof(ControlFileData) is too large ... fix PG_CONTROL_SIZE\n"),
-                               progname);
-               exit(1);
-       }
-
-       memset(buffer, 0, PG_CONTROL_SIZE);
+       memset(buffer, 0, PG_CONTROL_FILE_SIZE);
        memcpy(buffer, &ControlFile, sizeof(ControlFileData));
 
        unlink(XLOG_CONTROL_FILE);
@@ -908,7 +909,7 @@ RewriteControlFile(void)
        }
 
        errno = 0;
-       if (write(fd, buffer, PG_CONTROL_SIZE) != PG_CONTROL_SIZE)
+       if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
        {
                /* if write didn't set errno, assume problem is no disk space */
                if (errno == 0)
index 6c75b56992af343dfe98980300c497e93936e8b1..4bd1a759734958f3c2136ef68bb107653abb351d 100644 (file)
@@ -625,9 +625,9 @@ checkControlFile(ControlFileData *ControlFile)
 static void
 digestControlFile(ControlFileData *ControlFile, char *src, size_t size)
 {
-       if (size != PG_CONTROL_SIZE)
+       if (size != PG_CONTROL_FILE_SIZE)
                pg_fatal("unexpected control file size %d, expected %d\n",
-                                (int) size, PG_CONTROL_SIZE);
+                                (int) size, PG_CONTROL_FILE_SIZE);
 
        memcpy(ControlFile, src, sizeof(ControlFileData));
 
@@ -641,7 +641,16 @@ digestControlFile(ControlFileData *ControlFile, char *src, size_t size)
 static void
 updateControlFile(ControlFileData *ControlFile)
 {
-       char            buffer[PG_CONTROL_SIZE];
+       char            buffer[PG_CONTROL_FILE_SIZE];
+
+       /*
+        * For good luck, apply the same static assertions as in backend's
+        * WriteControlFile().
+        */
+       StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE,
+                                        "pg_control is too large for atomic disk writes");
+       StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE,
+                                        "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE");
 
        /* Recalculate CRC of control file */
        INIT_CRC32C(ControlFile->crc);
@@ -651,16 +660,16 @@ updateControlFile(ControlFileData *ControlFile)
        FIN_CRC32C(ControlFile->crc);
 
        /*
-        * Write out PG_CONTROL_SIZE bytes into pg_control by zero-padding the
-        * excess over sizeof(ControlFileData) to avoid premature EOF related
+        * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding
+        * the excess over sizeof(ControlFileData), to avoid premature EOF related
         * errors when reading it.
         */
-       memset(buffer, 0, PG_CONTROL_SIZE);
+       memset(buffer, 0, PG_CONTROL_FILE_SIZE);
        memcpy(buffer, ControlFile, sizeof(ControlFileData));
 
        open_target_file("global/pg_control", false);
 
-       write_target_range(buffer, 0, PG_CONTROL_SIZE);
+       write_target_range(buffer, 0, PG_CONTROL_FILE_SIZE);
 
        close_target_file();
 }
index 84327c9da6b0fe224125480d84fa90521287e277..1ec03caf5fbb8a47bba0ef0e70c96f5aa4387309 100644 (file)
 #include "port/pg_crc32c.h"
 
 
-#define MOCK_AUTH_NONCE_LEN            32
-
 /* Version identifier for this pg_control format */
 #define PG_CONTROL_VERSION     1002
 
+/* Nonce key length, see below */
+#define MOCK_AUTH_NONCE_LEN            32
+
 /*
  * Body of CheckPoint XLOG records.  This is declared here because we keep
  * a copy of the latest one in pg_control for possible disaster recovery.
@@ -94,10 +95,6 @@ typedef enum DBState
 
 /*
  * Contents of pg_control.
- *
- * NOTE: try to keep this under 512 bytes so that it will fit on one physical
- * sector of typical disk drives.  This reduces the odds of corruption due to
- * power failure midway through a write.
  */
 
 typedef struct ControlFileData
@@ -235,6 +232,14 @@ typedef struct ControlFileData
        pg_crc32c       crc;
 } ControlFileData;
 
+/*
+ * Maximum safe value of sizeof(ControlFileData).  For reliability's sake,
+ * it's critical that pg_control updates be atomic writes.  That generally
+ * means the active data can't be more than one disk sector, which is 512
+ * bytes on common hardware.  Be very careful about raising this limit.
+ */
+#define PG_CONTROL_MAX_SAFE_SIZE       512
+
 /*
  * Physical size of the pg_control file.  Note that this is considerably
  * bigger than the actually used size (ie, sizeof(ControlFileData)).
@@ -242,6 +247,6 @@ typedef struct ControlFileData
  * changes, so that ReadControlFile will deliver a suitable wrong-version
  * message instead of a read error if it's looking at an incompatible file.
  */
-#define PG_CONTROL_SIZE                8192
+#define PG_CONTROL_FILE_SIZE           8192
 
 #endif                                                 /* PG_CONTROL_H */