1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-05 09:19:17 +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:08 +00:00
parent a943df0ba5
commit d015675da6

View File

@ -164,7 +164,9 @@
* *
* Jan Wieck * Jan Wieck
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.23 2006/10/05 23:33:33 tgl Exp $ * Copyright (c) 1999-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.23.2.1 2008/05/28 21:58:08 tgl Exp $
* ---------- * ----------
*/ */
#include "postgres.h" #include "postgres.h"
@ -209,29 +211,27 @@ typedef struct PGLZ_HistEntry
* ---------- * ----------
*/ */
static const PGLZ_Strategy strategy_default_data = { static const PGLZ_Strategy strategy_default_data = {
256, /* Data chunks smaller 256 bytes are not 256, /* Data chunks less than 256 bytes are not
* compressed */ * compressed */
6144, /* Data chunks greater equal 6K force 6144, /* Data chunks >= 6K force compression, unless
* compression */ * compressed output is larger than input */
/* except compressed result is greater uncompressed data */ 20, /* Below 6K, compression rates below 20% mean
20, /* Compression rates below 20% mean fallback * fallback to uncompressed */
* to uncompressed */
/* storage except compression is forced by previous parameter */
128, /* Stop history lookup if a match of 128 bytes 128, /* Stop history lookup if a match of 128 bytes
* is found */ * is found */
10 /* Lower good match size by 10% at every 10 /* Lower good match size by 10% at every
* lookup loop iteration. */ * lookup loop iteration */
}; };
const PGLZ_Strategy *const PGLZ_strategy_default = &strategy_default_data; const PGLZ_Strategy *const PGLZ_strategy_default = &strategy_default_data;
static const PGLZ_Strategy strategy_always_data = { static const PGLZ_Strategy strategy_always_data = {
0, /* Chunks of any size are compressed */ 0, /* Chunks of any size are compressed */
0, /* */ 0,
0, /* We want to save at least one single byte */ 0, /* It's enough to save one single byte */
128, /* Stop history lookup if a match of 128 bytes 128, /* Stop history lookup if a match of 128 bytes
* is found */ * is found */
6 /* Look harder for a good match. */ 6 /* Look harder for a good match */
}; };
const PGLZ_Strategy *const PGLZ_strategy_always = &strategy_always_data; const PGLZ_Strategy *const PGLZ_strategy_always = &strategy_always_data;
@ -509,7 +509,7 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
* If the strategy forbids compression (at all or if source chunk too * If the strategy forbids compression (at all or if source chunk too
* small), fail. * small), fail.
*/ */
if (strategy->match_size_good == 0 || if (strategy->match_size_good <= 0 ||
slen < strategy->min_input_size) slen < strategy->min_input_size)
return false; return false;
@ -605,8 +605,8 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
} }
/* /*
* Write out the last control byte and check that we haven't overrun * Write out the last control byte and check that we haven't overrun the
* the output size allowed by the strategy. * output size allowed by the strategy.
*/ */
*ctrlp = ctrlb; *ctrlp = ctrlb;
result_size = bp - bstart; result_size = bp - bstart;
@ -631,26 +631,26 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
void void
pglz_decompress(const PGLZ_Header *source, char *dest) pglz_decompress(const PGLZ_Header *source, char *dest)
{ {
const unsigned char *dp; const unsigned char *sp;
const unsigned char *dend; const unsigned char *srcend;
unsigned char *bp; unsigned char *dp;
unsigned char ctrl; unsigned char *destend;
int32 ctrlc;
int32 len;
int32 off;
int32 destsize;
dp = ((const unsigned char *) source) + sizeof(PGLZ_Header); sp = ((const unsigned char *) source) + sizeof(PGLZ_Header);
dend = ((const unsigned char *) source) + VARATT_SIZE(source); srcend = ((const unsigned char *) source) + VARSIZE(source);
bp = (unsigned char *) dest; 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++; unsigned char ctrl = *sp++;
for (ctrlc = 0; ctrlc < 8 && dp < dend; ctrlc++) int ctrlc;
for (ctrlc = 0; ctrlc < 8 && sp < srcend; ctrlc++)
{ {
if (ctrl & 1) 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 * coded as 18, another extension tag byte tells how much
* longer the match really was (0-255). * longer the match really was (0-255).
*/ */
len = (dp[0] & 0x0f) + 3; int32 len;
off = ((dp[0] & 0xf0) << 4) | dp[1]; int32 off;
dp += 2;
len = (sp[0] & 0x0f) + 3;
off = ((sp[0] & 0xf0) << 4) | sp[1];
sp += 2;
if (len == 18) 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 * 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--) while (len--)
{ {
*bp = bp[-off]; *dp = dp[-off];
bp++; dp++;
} }
} }
else else
@ -685,7 +701,10 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
* An unset control bit means LITERAL BYTE. So we just copy * An unset control bit means LITERAL BYTE. So we just copy
* one from INPUT to OUTPUT. * 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 * Check we decompressed the right amount.
* condition if we tromped on more memory than expected (we assume we
* have not tromped on shared memory, though, so need not PANIC).
*/ */
destsize = (char *) bp - dest; if (dp != destend || sp != srcend)
if (destsize != source->rawsize) elog(ERROR, "compressed data is corrupt");
elog(destsize > source->rawsize ? FATAL : ERROR,
"compressed data is corrupt");
/* /*
* That's it. * That's it.