postgres.git / summary / log / commit / refs
commit 2b5ba2a0a141f621f61cb732d776dc78269f879b
Author: Andrew Dunstan <andrew@dunslane.net>
Date: Thu Apr 09 15:48:55 2026 +0000
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.
src/common/pg_lzcompress.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/src/common/pg_lzcompress.c b/src/common/pg_lzcompress.c
index 75d529df5e3..c8e9ef9aa3b 100644
--- a/src/common/pg_lzcompress.c
+++ b/src/common/pg_lzcompress.c
@@ -727,22 +727,33 @@ pglz_decompress(const char *source, int32 slen, char *dest,
int32 len;
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;
off = ((sp[0] & 0xf0) << 4) | sp[1];
sp += 2;
if (len == 18)
+ {
+ if (unlikely(sp >= srcend))
+ return -1;
len += *sp++;
+ }
/*
- * Check for corrupt data: if we fell off the end of the
- * source, or if we obtained off = 0, or if off is more than
- * the distance back to the buffer start, we have problems.
- * (We must check for off = 0, else we risk an infinite loop
- * below in the face of corrupt data. Likewise, the upper
- * limit on off prevents accessing outside the buffer
- * boundaries.)
+ * Check for corrupt data: if we obtained off = 0, or if off
+ * is more than the distance back to the buffer start, we have
+ * problems. (We must check for off = 0, else we risk an
+ * infinite loop below in the face of corrupt data. Likewise,
+ * the upper limit on off prevents accessing outside the
+ * buffer boundaries.)
*/
- if (unlikely(sp > srcend || off == 0 ||
+ if (unlikely(off == 0 ||
off > (dp - (unsigned char *) dest)))
return -1;
[parent: 2478bd5db0aa]