1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-02 11:44:50 +03:00

Backpatch Zdenek Kotala's fix to prevent pglz_decompress from stomping on

memory if the compressed data is corrupt.

Backpatch as far as 8.2.  The issue exists in older branches too, but given
the lack of field reports, it's not clear it's worth any additional effort
to adapt the patch to the slightly different code in older branches.
This commit is contained in:
Tom Lane 2008-05-28 21:58:03 +00:00
parent 85a24f0dd9
commit f26cbcab75

View File

@ -166,7 +166,7 @@
*
* Copyright (c) 1999-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.29 2008/01/01 19:45:52 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.29.2.1 2008/05/28 21:58:03 tgl Exp $
* ----------
*/
#include "postgres.h"
@ -631,26 +631,26 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
void
pglz_decompress(const PGLZ_Header *source, char *dest)
{
const unsigned char *dp;
const unsigned char *dend;
unsigned char *bp;
unsigned char ctrl;
int32 ctrlc;
int32 len;
int32 off;
int32 destsize;
const unsigned char *sp;
const unsigned char *srcend;
unsigned char *dp;
unsigned char *destend;
dp = ((const unsigned char *) source) + sizeof(PGLZ_Header);
dend = ((const unsigned char *) source) + VARSIZE(source);
bp = (unsigned char *) dest;
sp = ((const unsigned char *) source) + sizeof(PGLZ_Header);
srcend = ((const unsigned char *) source) + VARSIZE(source);
dp = (unsigned char *) dest;
destend = dp + source->rawsize;
while (dp < dend)
while (sp < srcend && dp < destend)
{
/*
* Read one control byte and process the next 8 items.
* Read one control byte and process the next 8 items (or as many
* as remain in the compressed input).
*/
ctrl = *dp++;
for (ctrlc = 0; ctrlc < 8 && dp < dend; ctrlc++)
unsigned char ctrl = *sp++;
int ctrlc;
for (ctrlc = 0; ctrlc < 8 && sp < srcend; ctrlc++)
{
if (ctrl & 1)
{
@ -661,11 +661,27 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
* coded as 18, another extension tag byte tells how much
* longer the match really was (0-255).
*/
len = (dp[0] & 0x0f) + 3;
off = ((dp[0] & 0xf0) << 4) | dp[1];
dp += 2;
int32 len;
int32 off;
len = (sp[0] & 0x0f) + 3;
off = ((sp[0] & 0xf0) << 4) | sp[1];
sp += 2;
if (len == 18)
len += *dp++;
len += *sp++;
/*
* Check for output buffer overrun, to ensure we don't
* clobber memory in case of corrupt input. Note: we must
* advance dp here to ensure the error is detected below
* the loop. We don't simply put the elog inside the loop
* since that will probably interfere with optimization.
*/
if (dp + len > destend)
{
dp += len;
break;
}
/*
* Now we copy the bytes specified by the tag from OUTPUT to
@ -675,8 +691,8 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
*/
while (len--)
{
*bp = bp[-off];
bp++;
*dp = dp[-off];
dp++;
}
}
else
@ -685,7 +701,10 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
* An unset control bit means LITERAL BYTE. So we just copy
* one from INPUT to OUTPUT.
*/
*bp++ = *dp++;
if (dp >= destend) /* check for buffer overrun */
break; /* do not clobber memory */
*dp++ = *sp++;
}
/*
@ -696,14 +715,10 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
}
/*
* Check we decompressed the right amount, else die. This is a FATAL
* condition if we tromped on more memory than expected (we assume we have
* not tromped on shared memory, though, so need not PANIC).
* Check we decompressed the right amount.
*/
destsize = (char *) bp - dest;
if (destsize != source->rawsize)
elog(destsize > source->rawsize ? FATAL : ERROR,
"compressed data is corrupt");
if (dp != destend || sp != srcend)
elog(ERROR, "compressed data is corrupt");
/*
* That's it.