diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c index 4f53d30..cdbf594 100644 --- a/src/backend/access/rmgrdesc/xactdesc.c +++ b/src/backend/access/rmgrdesc/xactdesc.c @@ -16,6 +16,9 @@ #include "access/transam.h" #include "access/xact.h" +#ifdef FRONTEND +#include "common/logging.h" +#endif #include "replication/origin.h" #include "storage/sinval.h" #include "storage/standbydefs.h" @@ -256,6 +259,20 @@ ParsePrepareRecord(uint8 info, xl_xact_prepare *xlrec, xl_xact_parsed_prepare *p parsed->nabortstats = xlrec->nabortstats; parsed->nmsgs = xlrec->ninvalmsgs; + /* parsed->twophase_gid is only GIDSIZE bytes long */ + if (xlrec->gidlen >= GIDSIZE) + { +#ifdef FRONTEND + pg_fatal("invalid two-phase GID length %u in WAL record", + xlrec->gidlen); +#else + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid two-phase GID length %u in WAL record", + xlrec->gidlen))); +#endif + } + strncpy(parsed->twophase_gid, bufptr, xlrec->gidlen); bufptr += MAXALIGN(xlrec->gidlen); diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 439e28c..a94d10e 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -492,7 +492,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, FullTransactionId fxid, gxact->locking_backend = MyProcNumber; gxact->valid = false; gxact->inredo = false; - strcpy(gxact->gid, gid); + strlcpy(gxact->gid, gid, GIDSIZE); /* * Remember that we have this GlobalTransaction entry locked for us. If we @@ -2126,6 +2126,12 @@ RecoverPreparedTransactions(void) hdr = (TwoPhaseFileHeader *) buf; Assert(TransactionIdEquals(hdr->xid, XidFromFullTransactionId(gxact->fxid))); + if (hdr->gidlen == 0 || hdr->gidlen >= GIDSIZE) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid two-phase GID length %u in state file", + hdr->gidlen))); + bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); gid = (const char *) bufptr; bufptr += MAXALIGN(hdr->gidlen); @@ -2534,6 +2540,18 @@ PrepareRedoAdd(FullTransactionId fxid, char *buf, bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); gid = (const char *) bufptr; + /* + * The write-time path (PrepareTransaction) rejects oversized GIDs, + * but a replayed record must not be trusted: a crafted or corrupted + * XLOG_XACT_PREPARE record can carry any gidlen, and gxact->gid is + * only GIDSIZE bytes long. + */ + if (hdr->gidlen == 0 || hdr->gidlen >= GIDSIZE) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid two-phase GID length %u in WAL record", + hdr->gidlen))); + /* * Reserve the GID for the given transaction in the redo code path. * @@ -2597,7 +2615,7 @@ PrepareRedoAdd(FullTransactionId fxid, char *buf, gxact->valid = false; gxact->ondisk = !XLogRecPtrIsValid(start_lsn); gxact->inredo = true; /* yes, added in redo */ - strcpy(gxact->gid, gid); + strlcpy(gxact->gid, gid, GIDSIZE); /* And insert it into the active array */ Assert(TwoPhaseState->numPrepXacts < max_prepared_xacts);