While playing with xlogreader, I was lucky enough to see one of the many
record validations to fail. After having some fun with gdb, I found out that
in some cases the reader does not enforce enough data to be in state-readBuf
before copying into state-readRecordBuf starts. This should not happen if the
callback always reads XLOG_BLCKSZ bytes, but in fact only *reqLen* is the
mandatory size of the chunk delivered.
There are probably various ways to fix this problem. Attached is what I did in
my environment. I hit the problem on 9.4.1, but the patch seems to apply to
master too.
--
Antonin Houska
Cybertec Schönig Schönig GmbH
Gröhrmühlgasse 26
A-2700 Wiener Neustadt
Web: http://www.postgresql-support.de, http://www.cybertec.at
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 474137a..e6ebd9d 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -313,7 +313,21 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
goto err;
}
- len = XLOG_BLCKSZ - RecPtr % XLOG_BLCKSZ;
+ /* Bytes of the current record residing on the current page. */
+ len = Min(XLOG_BLCKSZ - targetRecOff, total_len);
+
+ /*
+ * Nothing beyond the record header is guaranteed to be in state-readBuf
+ * so far.
+ */
+ if (readOff targetRecOff + len)
+ {
+ readOff = ReadPageInternal(state, targetPagePtr, targetRecOff + len);
+
+ if (readOff 0)
+ goto err;
+ }
+
if (total_len len)
{
/* Need to reassemble record */
@@ -322,9 +336,11 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
char *buffer;
uint32 gotlen;
+ Assert(readOff == targetRecOff + len);
+ Assert(readOff == XLOG_BLCKSZ);
+
/* Copy the first fragment of the record from the first page. */
- memcpy(state-readRecordBuf,
- state-readBuf + RecPtr % XLOG_BLCKSZ, len);
+ memcpy(state-readRecordBuf, state-readBuf + targetRecOff, len);
buffer = state-readRecordBuf + len;
gotlen = len;
@@ -413,20 +429,16 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
}
else
{
- /* Wait for the record data to become available */
- readOff = ReadPageInternal(state, targetPagePtr,
- Min(targetRecOff + total_len, XLOG_BLCKSZ));
- if (readOff 0)
- goto err;
+ Assert(readOff = targetRecOff + len);
/* Record does not cross a page boundary */
if (!ValidXLogRecord(state, record, RecPtr))
goto err;
- state-EndRecPtr = RecPtr + MAXALIGN(total_len);
+ state-EndRecPtr = RecPtr + MAXALIGN(len);
state-ReadRecPtr = RecPtr;
- memcpy(state-readRecordBuf, record, total_len);
+ memcpy(state-readRecordBuf, record, len);
}
/*
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers