From 1547f3abb0ab3abd2ef074b8e29938541e1886c3 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Fri, 19 Jun 2026 19:39:03 +0100 Subject: [PATCH] OSS-Fuzz: Add new fuzzer targets fai processing Signed-off-by: Arthur Chan --- Makefile | 5 +++ test/fuzz/fai_build_fuzzer.c | 79 ++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 test/fuzz/fai_build_fuzzer.c diff --git a/Makefile b/Makefile index 61d01720e..836517a63 100644 --- a/Makefile +++ b/Makefile @@ -100,6 +100,7 @@ BUILT_TEST_PROGRAMS = \ test/test-vcf-sweep \ test/test-bcf-sr \ test/fuzz/hts_open_fuzzer.o \ + test/fuzz/fai_build_fuzzer.o \ test/test-bcf-translate \ test/test-parse-reg \ test/test_introspection \ @@ -739,6 +740,9 @@ test/hts_endian: test/hts_endian.o test/fuzz/hts_open_fuzzer: test/fuzz/hts_open_fuzzer.o libhts.a $(CC) $(LDFLAGS) -o $@ test/fuzz/hts_open_fuzzer.o libhts.a $(LIBS) -lpthread +test/fuzz/fai_build_fuzzer: test/fuzz/fai_build_fuzzer.o libhts.a + $(CC) $(LDFLAGS) -o $@ test/fuzz/fai_build_fuzzer.o libhts.a $(LIBS) -lpthread + test/fieldarith: test/fieldarith.o libhts.a $(CC) $(LDFLAGS) -o $@ test/fieldarith.o libhts.a $(LIBS) -lpthread @@ -878,6 +882,7 @@ htscodecs/tests/varint_test.o: htscodecs/tests/varint_test.c config.h $(htscodec test/hts_endian.o: test/hts_endian.c config.h $(htslib_hts_endian_h) test/fuzz/hts_open_fuzzer.o: test/fuzz/hts_open_fuzzer.c config.h $(htslib_hfile_h) $(htslib_hts_h) $(htslib_sam_h) $(htslib_vcf_h) +test/fuzz/fai_build_fuzzer.o: test/fuzz/fai_build_fuzzer.c config.h $(htslib_faidx_h) test/fieldarith.o: test/fieldarith.c config.h $(htslib_sam_h) test/hfile.o: test/hfile.c config.h $(htslib_hfile_h) $(htslib_hts_defs_h) $(htslib_kstring_h) test/pileup.o: test/pileup.c config.h $(htslib_sam_h) $(htslib_kstring_h) diff --git a/test/fuzz/fai_build_fuzzer.c b/test/fuzz/fai_build_fuzzer.c new file mode 100644 index 000000000..331a59a31 --- /dev/null +++ b/test/fuzz/fai_build_fuzzer.c @@ -0,0 +1,79 @@ +/* test/fuzz/fai_build_fuzzer.c -- Fuzz driver for the FASTA/FASTQ index parser. + + Copyright (C) 2026 Genome Research Ltd. + + Author: Arthur Chan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../htslib/faidx.h" + +// Exercises the FASTA/FASTQ body parser (fai_build_core) and the .fai/.gzi +// index parser (fai_read), neither of which hts_open_fuzzer reaches. +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size == 0 || size > (1 << 20)) + return 0; + + char fa[] = "/tmp/htsfai.XXXXXX"; + int fd = mkstemp(fa); + if (fd < 0) + return 0; + if (write(fd, data, size) != (ssize_t) size) { + close(fd); + unlink(fa); + return 0; + } + close(fd); + + // fai_load builds the index by parsing the FASTA/FASTQ body, then reads it + // back; fetching a few sequences exercises the random-access seek paths. + faidx_t *fai = fai_load(fa); + if (fai) { + int n = faidx_nseq(fai); + if (n > 64) + n = 64; + for (int i = 0; i < n; i++) { + const char *name = faidx_iseq(fai, i); + if (!name) + continue; + int len = 0; + char *seq = fai_fetch(fai, name, &len); + free(seq); + } + fai_destroy(fai); + } + + char idx[64]; + snprintf(idx, sizeof idx, "%s.fai", fa); + unlink(idx); + snprintf(idx, sizeof idx, "%s.gzi", fa); + unlink(idx); + unlink(fa); + return 0; +}