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);
