Fix multiple bugs in astreamer pipeline code.

astreamer_tar_parser_content() sent the wrong data pointer when
forwarding MEMBER_TRAILER padding to the next streamer.  After
astreamer_buffer_until() buffers the padding bytes, the 'data'
pointer has been advanced past them, but the code passed 'data'
instead of bbs_buffer.data.  This caused the downstream consumer
to receive bytes from after the padding rather than the padding
itself, and could read past the end of the input buffer.

astreamer_gzip_decompressor_content() only checked for
Z_STREAM_ERROR from inflate(), silently ignoring Z_DATA_ERROR
(corrupted data) and Z_MEM_ERROR (out of memory).  Fix by
treating any return other than Z_OK, Z_STREAM_END, and
Z_BUF_ERROR as fatal.

astreamer_gzip_decompressor_free() missed calling inflateEnd() to
release zlib's internal decompression state.

astreamer_tar_parser_free() neglected to pfree() the streamer
struct itself, leaking it.

astreamer_extractor_content() did not check the return value of
fclose() when closing an extracted file.  A deferred write error
(e.g., disk full on buffered I/O) would be silently lost.

Discussion: https://postgr.es/m/results/98c6b630-acbb-44a7-97fa-1692ce2b827c@dunslane.net

Reviewed-By: Tom Lane <tgl@sss.pgh.pa.us>

Backpatch-through: 15
This commit is contained in:
Andrew Dunstan 2026-03-23 16:17:08 -04:00
parent 0841b219bf
commit 01d58d7e3f
3 changed files with 14 additions and 4 deletions

View file

@ -266,7 +266,9 @@ astreamer_extractor_content(astreamer *streamer, astreamer_member *member,
case ASTREAMER_MEMBER_TRAILER:
if (mystreamer->file == NULL)
break;
fclose(mystreamer->file);
if (fclose(mystreamer->file) != 0)
pg_fatal("could not close file \"%s\": %m",
mystreamer->filename);
mystreamer->file = NULL;
break;

View file

@ -318,8 +318,9 @@ astreamer_gzip_decompressor_content(astreamer *streamer,
*/
res = inflate(zs, Z_NO_FLUSH);
if (res == Z_STREAM_ERROR)
pg_fatal("could not decompress data: %s", zs->msg);
if (res != Z_OK && res != Z_STREAM_END && res != Z_BUF_ERROR)
pg_fatal("could not decompress data: %s",
zs->msg ? zs->msg : "unknown error");
mystreamer->bytes_written =
mystreamer->base.bbs_buffer.maxlen - zs->avail_out;
@ -364,7 +365,12 @@ astreamer_gzip_decompressor_finalize(astreamer *streamer)
static void
astreamer_gzip_decompressor_free(astreamer *streamer)
{
astreamer_gzip_decompressor *mystreamer;
mystreamer = (astreamer_gzip_decompressor *) streamer;
astreamer_free(streamer->bbs_next);
inflateEnd(&mystreamer->zstream);
pfree(streamer->bbs_buffer.data);
pfree(streamer);
}

View file

@ -224,7 +224,8 @@ astreamer_tar_parser_content(astreamer *streamer, astreamer_member *member,
/* OK, now we can send it. */
astreamer_content(mystreamer->base.bbs_next,
&mystreamer->member,
data, mystreamer->pad_bytes_expected,
mystreamer->base.bbs_buffer.data,
mystreamer->pad_bytes_expected,
ASTREAMER_MEMBER_TRAILER);
/* Expect next file header. */
@ -346,6 +347,7 @@ astreamer_tar_parser_free(astreamer *streamer)
{
pfree(streamer->bbs_buffer.data);
astreamer_free(streamer->bbs_next);
pfree(streamer);
}
/*