Fix locking bugs that could corrupt pg_control.
authorThomas Munro <[email protected]>
Mon, 8 Jun 2020 01:57:24 +0000 (13:57 +1200)
committerThomas Munro <[email protected]>
Mon, 8 Jun 2020 02:02:20 +0000 (14:02 +1200)
The redo routines for XLOG_CHECKPOINT_{ONLINE,SHUTDOWN} must acquire
ControlFileLock before modifying ControlFile->checkPointCopy, or the
checkpointer could write out a control file with a bad checksum.

Likewise, XLogReportParameters() must acquire ControlFileLock before
modifying ControlFile and calling UpdateControlFile().

Back-patch to all supported releases.

Author: Nathan Bossart <[email protected]>
Author: Fujii Masao <[email protected]>
Reviewed-by: Fujii Masao <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Thomas Munro <[email protected]>
Discussion: https://postgr.es/m/70BF24D6-DC51-443F-B55A-95735803842A%40amazon.com

src/backend/access/transam/xlog.c

index b3270c70e2f714d49af5af8ce5ab93f4bb113160..f6261eb0e1da91dd56050c2bab15ab4d7753f693 100644 (file)
@@ -9265,6 +9265,8 @@ XLogReportParameters(void)
            XLogFlush(recptr);
        }
 
+       LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
+
        ControlFile->MaxConnections = MaxConnections;
        ControlFile->max_worker_processes = max_worker_processes;
        ControlFile->max_prepared_xacts = max_prepared_xacts;
@@ -9273,6 +9275,8 @@ XLogReportParameters(void)
        ControlFile->wal_log_hints = wal_log_hints;
        ControlFile->track_commit_timestamp = track_commit_timestamp;
        UpdateControlFile();
+
+       LWLockRelease(ControlFileLock);
    }
 }
 
@@ -9492,8 +9496,10 @@ xlog_redo(XLogReaderState *record)
        }
 
        /* ControlFile->checkPointCopy always tracks the latest ckpt XID */
+       LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
        ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch;
        ControlFile->checkPointCopy.nextXid = checkPoint.nextXid;
+       LWLockRelease(ControlFileLock);
 
        /* Update shared-memory copy of checkpoint XID/epoch */
        SpinLockAcquire(&XLogCtl->info_lck);
@@ -9551,8 +9557,10 @@ xlog_redo(XLogReaderState *record)
            SetTransactionIdLimit(checkPoint.oldestXid,
                                  checkPoint.oldestXidDB);
        /* ControlFile->checkPointCopy always tracks the latest ckpt XID */
+       LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
        ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch;
        ControlFile->checkPointCopy.nextXid = checkPoint.nextXid;
+       LWLockRelease(ControlFileLock);
 
        /* Update shared-memory copy of checkpoint XID/epoch */
        SpinLockAcquire(&XLogCtl->info_lck);