diff --git a/scripts/test_bgzf_truncated_input.py b/scripts/test_bgzf_truncated_input.py new file mode 100644 index 00000000..9745f2e9 --- /dev/null +++ b/scripts/test_bgzf_truncated_input.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Run: python3 scripts/test_bgzf_truncated_input.py ./fastp + +Synthetic BGZF fixtures; no external data or compression tools required. +""" +import gzip +import pathlib +import struct +import subprocess +import sys +import tempfile +import zlib + + +def block(data): + compressor = zlib.compressobj(wbits=-15) + payload = compressor.compress(data) + compressor.flush() + size = 18 + len(payload) + 8 + assert size <= 65536 + return (b'\x1f\x8b\x08\x04' + b'\x00'*4 + b'\x00\xff' + + struct.pack(' 0 and diagnostic.encode() in run.stderr + else: + ok = (run.returncode == 0 and output.exists() and + output.read_bytes() == expected) + detail = 'exit=%d' % run.returncode + except subprocess.TimeoutExpired: + ok, detail = False, 'timeout' + print('%s %s threads=%d %s' % + ('PASS' if ok else 'FAIL', name, threads, detail)) + failed += not ok + return bool(failed) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/bgzf.h b/src/bgzf.h index eb6eb241..f4ee161c 100644 --- a/src/bgzf.h +++ b/src/bgzf.h @@ -10,6 +10,7 @@ #include #include #include +#include "util.h" static const int BGZF_HEADER_SIZE = 18; static const int BGZF_MAX_BLOCK_SIZE = 65536; @@ -136,14 +137,25 @@ class BgzfMtReader { } size_t n = fread(header, 1, BGZF_HEADER_SIZE, mFp); - if (n < BGZF_HEADER_SIZE) { markDone(s); break; } + if (ferror(mFp)) + error_exit("BGZF input read error while reading block header"); + if (n == 0) { markDone(s); break; } + if (n < BGZF_HEADER_SIZE) + error_exit("Truncated BGZF block header"); uint32_t bsize = bgzfBlockSize(header); - if (bsize == 0 || bsize > BGZF_MAX_BLOCK_SIZE) { markDone(s); break; } + // Reject undersized blocks before subtracting the header length. + // A BGZF block includes the header, deflate stream and 8-byte trailer. + if (bsize < BGZF_HEADER_SIZE + 8 || bsize > BGZF_MAX_BLOCK_SIZE) + error_exit("Invalid BGZF block size or header"); memcpy(s.comp, header, BGZF_HEADER_SIZE); size_t rest = bsize - BGZF_HEADER_SIZE; - if (fread(s.comp + BGZF_HEADER_SIZE, 1, rest, mFp) < rest) { markDone(s); break; } + size_t bodyRead = fread(s.comp + BGZF_HEADER_SIZE, 1, rest, mFp); + if (ferror(mFp)) + error_exit("BGZF input read error while reading block body"); + if (bodyRead < rest) + error_exit("Truncated BGZF block body"); if (bsize == 28) { uint32_t isize = s.comp[24]|(s.comp[25]<<8)|(s.comp[26]<<16)|(s.comp[27]<<24);