Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 9c767ad

Browse files
committed
Improve pglz_decompress() so that it cannot clobber memory beyond the
available output buffer when presented with corrupt input. Some testing suggests that this slows the decompression loop about 1%, which seems an acceptable price to pay for more robustness. (Curiously, the penalty seems to be *less* on not-very-compressible data, which I didn't expect since the overhead per output byte ought to be more in the literal-bytes path.) Patch from Zdenek Kotala. I fixed a corner case and did some renaming of variables to make the routine more readable.
1 parent ad43447 commit 9c767ad

File tree

1 file changed

+46
-31
lines changed

1 file changed

+46
-31
lines changed

src/backend/utils/adt/pg_lzcompress.c

+46-31
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
*
167167
* Copyright (c) 1999-2008, PostgreSQL Global Development Group
168168
*
169-
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.30 2008/03/07 23:20:21 tgl Exp $
169+
* $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.31 2008/03/08 01:09:36 tgl Exp $
170170
* ----------
171171
*/
172172
#include "postgres.h"
@@ -641,26 +641,26 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
641641
void
642642
pglz_decompress(const PGLZ_Header *source, char *dest)
643643
{
644-
const unsigned char *dp;
645-
const unsigned char *dend;
646-
unsigned char *bp;
647-
unsigned char ctrl;
648-
int32 ctrlc;
649-
int32 len;
650-
int32 off;
651-
int32 destsize;
652-
653-
dp = ((const unsigned char *) source) + sizeof(PGLZ_Header);
654-
dend = ((const unsigned char *) source) + VARSIZE(source);
655-
bp = (unsigned char *) dest;
644+
const unsigned char *sp;
645+
const unsigned char *srcend;
646+
unsigned char *dp;
647+
unsigned char *destend;
656648

657-
while (dp < dend)
649+
sp = ((const unsigned char *) source) + sizeof(PGLZ_Header);
650+
srcend = ((const unsigned char *) source) + VARSIZE(source);
651+
dp = (unsigned char *) dest;
652+
destend = dp + source->rawsize;
653+
654+
while (sp < srcend && dp < destend)
658655
{
659656
/*
660-
* Read one control byte and process the next 8 items.
657+
* Read one control byte and process the next 8 items (or as many
658+
* as remain in the compressed input).
661659
*/
662-
ctrl = *dp++;
663-
for (ctrlc = 0; ctrlc < 8 && dp < dend; ctrlc++)
660+
unsigned char ctrl = *sp++;
661+
int ctrlc;
662+
663+
for (ctrlc = 0; ctrlc < 8 && sp < srcend; ctrlc++)
664664
{
665665
if (ctrl & 1)
666666
{
@@ -671,11 +671,27 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
671671
* coded as 18, another extension tag byte tells how much
672672
* longer the match really was (0-255).
673673
*/
674-
len = (dp[0] & 0x0f) + 3;
675-
off = ((dp[0] & 0xf0) << 4) | dp[1];
676-
dp += 2;
674+
int32 len;
675+
int32 off;
676+
677+
len = (sp[0] & 0x0f) + 3;
678+
off = ((sp[0] & 0xf0) << 4) | sp[1];
679+
sp += 2;
677680
if (len == 18)
678-
len += *dp++;
681+
len += *sp++;
682+
683+
/*
684+
* Check for output buffer overrun, to ensure we don't
685+
* clobber memory in case of corrupt input. Note: we must
686+
* advance dp here to ensure the error is detected below
687+
* the loop. We don't simply put the elog inside the loop
688+
* since that will probably interfere with optimization.
689+
*/
690+
if (dp + len > destend)
691+
{
692+
dp += len;
693+
break;
694+
}
679695

680696
/*
681697
* Now we copy the bytes specified by the tag from OUTPUT to
@@ -685,8 +701,8 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
685701
*/
686702
while (len--)
687703
{
688-
*bp = bp[-off];
689-
bp++;
704+
*dp = dp[-off];
705+
dp++;
690706
}
691707
}
692708
else
@@ -695,7 +711,10 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
695711
* An unset control bit means LITERAL BYTE. So we just copy
696712
* one from INPUT to OUTPUT.
697713
*/
698-
*bp++ = *dp++;
714+
if (dp >= destend) /* check for buffer overrun */
715+
break; /* do not clobber memory */
716+
717+
*dp++ = *sp++;
699718
}
700719

701720
/*
@@ -706,14 +725,10 @@ pglz_decompress(const PGLZ_Header *source, char *dest)
706725
}
707726

708727
/*
709-
* Check we decompressed the right amount, else die. This is a FATAL
710-
* condition if we tromped on more memory than expected (we assume we have
711-
* not tromped on shared memory, though, so need not PANIC).
728+
* Check we decompressed the right amount.
712729
*/
713-
destsize = (char *) bp - dest;
714-
if (destsize != source->rawsize)
715-
elog(destsize > source->rawsize ? FATAL : ERROR,
716-
"compressed data is corrupt");
730+
if (dp != destend || sp != srcend)
731+
elog(ERROR, "compressed data is corrupt");
717732

718733
/*
719734
* That's it.

0 commit comments

Comments
 (0)