Fix heap-buffer-overflow in pglz_decompress() on corrupt input.

When decoding a match tag, pglz_decompress() reads 2 bytes (or 3
for extended-length matches) from the source buffer before checking
whether enough data remains.  The existing bounds check (sp > srcend)
occurs after the reads, so truncated compressed data that ends
mid-tag causes a read past the allocated buffer.

Fix by validating that sufficient source bytes are available before
reading each part of the match tag.  The post-read sp > srcend
check is no longer needed and is removed.

Found by fuzz testing with libFuzzer and AddressSanitizer.
This commit is contained in:
Andrew Dunstan 2026-04-09 11:48:55 -04:00
parent 2478bd5db0
commit 2b5ba2a0a1

View file

@ -727,22 +727,33 @@ pglz_decompress(const char *source, int32 slen, char *dest,
int32 len; int32 len;
int32 off; int32 off;
/*
* A match tag is at least 2 bytes; if the length nibble is
* 0x0f the tag is 3 bytes (extended length). Verify we have
* enough source data before reading them.
*/
if (unlikely(sp + 2 > srcend))
return -1;
len = (sp[0] & 0x0f) + 3; len = (sp[0] & 0x0f) + 3;
off = ((sp[0] & 0xf0) << 4) | sp[1]; off = ((sp[0] & 0xf0) << 4) | sp[1];
sp += 2; sp += 2;
if (len == 18) if (len == 18)
{
if (unlikely(sp >= srcend))
return -1;
len += *sp++; len += *sp++;
}
/* /*
* Check for corrupt data: if we fell off the end of the * Check for corrupt data: if we obtained off = 0, or if off
* source, or if we obtained off = 0, or if off is more than * is more than the distance back to the buffer start, we have
* the distance back to the buffer start, we have problems. * problems. (We must check for off = 0, else we risk an
* (We must check for off = 0, else we risk an infinite loop * infinite loop below in the face of corrupt data. Likewise,
* below in the face of corrupt data. Likewise, the upper * the upper limit on off prevents accessing outside the
* limit on off prevents accessing outside the buffer * buffer boundaries.)
* boundaries.)
*/ */
if (unlikely(sp > srcend || off == 0 || if (unlikely(off == 0 ||
off > (dp - (unsigned char *) dest))) off > (dp - (unsigned char *) dest)))
return -1; return -1;