Skip to content

Commit d1a2a87

Browse files
committed
Release 1.23.1
2 parents 86e74e0 + 81fb71c commit d1a2a87

File tree

21 files changed

+607
-179
lines changed

21 files changed

+607
-179
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -757,8 +757,8 @@ test/test_kfunc: test/test_kfunc.o libhts.a
757757
test/test_khash: test/test_khash.o libhts.a
758758
$(CC) $(LDFLAGS) -o $@ test/test_khash.o libhts.a $(LIBS) -lpthread
759759

760-
test/test_kstring: test/test_kstring.o libhts.a
761-
$(CC) $(LDFLAGS) -o $@ test/test_kstring.o libhts.a $(LIBS) -lpthread
760+
test/test_kstring: test/test_kstring.o
761+
$(CC) $(LDFLAGS) -o $@ test/test_kstring.o -lm
762762

763763
test/test_mod: test/test_mod.o libhts.a
764764
$(CC) $(LDFLAGS) -o $@ test/test_mod.o libhts.a $(LIBS) -lpthread
@@ -867,7 +867,7 @@ test/test_bgzf.o: test/test_bgzf.c config.h $(htslib_bgzf_h) $(htslib_hfile_h) $
867867
test/test_expr.o: test/test_expr.c config.h $(htslib_hts_expr_h)
868868
test/test_kfunc.o: test/test_kfunc.c config.h $(htslib_kfunc_h)
869869
test/test_khash.o: test/test_khash.c config.h $(htslib_khash_h) $(htslib_kroundup_h)
870-
test/test_kstring.o: test/test_kstring.c config.h $(htslib_kstring_h)
870+
test/test_kstring.o: test/test_kstring.c config.h $(htslib_kstring_h) kstring.c
871871
test/test_mod.o: test/test_mod.c config.h $(htslib_sam_h)
872872
test/test_nibbles.o: test/test_nibbles.c config.h $(htslib_sam_h) $(sam_internal_h)
873873
test/test-parse-reg.o: test/test-parse-reg.c config.h $(htslib_hts_h) $(htslib_sam_h)

NEWS

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,51 @@
1+
Noteworthy changes in release 1.23.1 (18th March 2026)
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Bug fixes
5+
---------
6+
7+
* Fix a number of bugs in the CRAM decoder which could result in undefined
8+
behaviour on invalid inputs (PR #1981, PR #1991):
9+
10+
- Not checking the amount of byte array len data returned matched the amount
11+
expected. (CVE-2026-31971)
12+
- Incorrect check for the length of byte array stop data. (CVE-2026-31969)
13+
- Invalid use of the varint and const codecs. (CVE-2026-31968)
14+
- Missing check for a valid reference ID. (CVE-2026-31965)
15+
- Missing check for a valid mate reference ID. (CVE-2026-31967)
16+
- Incomplete validation of CRAM feature locations.
17+
(CVE-2026-31965, CVE-2026-31966)
18+
- Bugs due to improper handling of records where no sequence or quality
19+
values were stored (CVE-2026-31962, CVE-2026-31964)
20+
21+
* Reject GZI indexes with impossibly-large item counts. (CVE-2026-31970)
22+
(PR #1978. Reported by Harrison Green)
23+
24+
* Prevent the wrong item count from being written to GZI indexes of
25+
empty files.
26+
(PR #1988. Reported by Matthieu Muffato)
27+
28+
* Fix invalid behaviour if kmemmem(), kstrstr() or kstrnstr() were
29+
called with a zero-length pattern, or if kstrstr() was given a very
30+
long input. Also ensure they can never fail by supplying a fallback
31+
algorithm that does not allocate any memory.
32+
(PR #1980. Reported by Harrison Green)
33+
34+
* Prevent redundant copies of hash keys in string pools.
35+
(PR #1982)
36+
37+
* Fix regressions in the S3 plugin which caused uploads to fail.
38+
(PR #1984)
39+
40+
* Disallow attempts to set the thread pool attached to an htsFile twice.
41+
(PR #1985)
42+
43+
Build Changes
44+
-------------
45+
46+
* The htscodecs submodule is updated to v1.6.6.
47+
(PR #1989)
48+
149
Noteworthy changes in release 1.23 (16th December 2025)
250
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
351

annot-tsv.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'\" t
2-
.TH annot-tsv 1 "16 December 2025" "htslib-1.23" "Bioinformatics tools"
2+
.TH annot-tsv 1 "18 March 2026" "htslib-1.23.1" "Bioinformatics tools"
33
.\"
44
.\" Copyright (C) 2015, 2017-2018, 2023-2024 Genome Research Ltd.
55
.\"

bgzf.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,8 @@ int bgzf_thread_pool(BGZF *fp, hts_tpool *pool, int qsize) {
17411741
// No gain from multi-threading when not compressed
17421742
if (!fp->is_compressed)
17431743
return 0;
1744+
if (fp->mt)
1745+
return -2; //already exists!
17441746

17451747
mtaux_t *mt;
17461748
mt = (mtaux_t*)calloc(1, sizeof(mtaux_t));
@@ -1789,9 +1791,10 @@ int bgzf_mt(BGZF *fp, int n_threads, int n_sub_blks)
17891791
if (!p)
17901792
return -1;
17911793

1792-
if (bgzf_thread_pool(fp, p, 0) != 0) {
1794+
int ret = 0;
1795+
if ((ret = bgzf_thread_pool(fp, p, 0)) < 0) {
17931796
hts_tpool_destroy(p);
1794-
return -1;
1797+
return ret;
17951798
}
17961799

17971800
fp->mt->own_pool = 1;
@@ -2396,10 +2399,11 @@ int bgzf_index_dump_hfile(BGZF *fp, struct hFILE *idx, const char *name)
23962399
if (bgzf_flush(fp) != 0) return -1;
23972400

23982401
// discard the entry marking the end of the file
2399-
if (fp->mt && fp->idx)
2402+
if (fp->mt && fp->idx && fp->idx->noffs > 0)
24002403
fp->idx->noffs--;
24012404

2402-
if (hwrite_uint64(fp->idx->noffs - 1, idx) < 0) goto fail;
2405+
if (hwrite_uint64(fp->idx->noffs > 0 ? fp->idx->noffs - 1 : 0, idx) < 0)
2406+
goto fail;
24032407
for (i=1; i<fp->idx->noffs; i++)
24042408
{
24052409
if (hwrite_uint64(fp->idx->offs[i].caddr, idx) < 0) goto fail;
@@ -2471,6 +2475,9 @@ int bgzf_index_load_hfile(BGZF *fp, struct hFILE *idx, const char *name)
24712475
if (fp->idx == NULL) goto fail;
24722476
uint64_t x;
24732477
if (hread_uint64(&x, idx) < 0) goto fail;
2478+
if (x >= ((SIZE_MAX < UINT64_MAX ? SIZE_MAX : UINT64_MAX)
2479+
/ sizeof(bgzidx1_t) / 2))
2480+
goto fail;
24742481

24752482
fp->idx->noffs = fp->idx->moffs = x + 1;
24762483
fp->idx->offs = (bgzidx1_t*) malloc(fp->idx->moffs*sizeof(bgzidx1_t));

bgzip.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.TH bgzip 1 "16 December 2025" "htslib-1.23" "Bioinformatics tools"
1+
.TH bgzip 1 "18 March 2026" "htslib-1.23.1" "Bioinformatics tools"
22
.SH NAME
33
.PP
44
bgzip \- Block compression/decompression utility

cram/cram_codecs.c

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2012-2021,2023, 2025 Genome Research Ltd.
2+
Copyright (c) 2012-2021,2023, 2025, 2026 Genome Research Ltd.
33
Author: James Bonfield <jkb@sanger.ac.uk>
44
55
Redistribution and use in source and binary forms, with or without
@@ -776,17 +776,23 @@ cram_codec *cram_varint_decode_init(cram_block_compression_hdr *hdr,
776776
// does not change.
777777
switch(codec) {
778778
case E_VARINT_UNSIGNED:
779-
c->decode = (option == E_INT)
780-
? cram_varint_decode_int
781-
: cram_varint_decode_long;
779+
if (option == E_INT || option == E_SINT)
780+
c->decode = cram_varint_decode_int;
781+
else if (option == E_LONG || option == E_SLONG)
782+
c->decode = cram_varint_decode_long;
783+
else
784+
goto malformed;
782785
break;
783786
case E_VARINT_SIGNED:
784-
c->decode = (option == E_INT)
785-
? cram_varint_decode_sint
786-
: cram_varint_decode_slong;
787+
if (option == E_INT || option == E_SINT)
788+
c->decode = cram_varint_decode_sint;
789+
else if (option == E_LONG || option == E_SLONG)
790+
c->decode = cram_varint_decode_slong;
791+
else
792+
goto malformed;
787793
break;
788794
default:
789-
return NULL;
795+
goto malformed;
790796
}
791797

792798
c->free = cram_varint_decode_free;
@@ -798,14 +804,17 @@ cram_codec *cram_varint_decode_init(cram_block_compression_hdr *hdr,
798804
c->u.varint.offset = vv->varint_get64s(&cp, cp_end, NULL);
799805

800806
if (cp - data != size) {
801-
fprintf(stderr, "Malformed varint header stream\n");
802-
free(c);
803-
return NULL;
807+
goto malformed;
804808
}
805809

806810
c->u.varint.type = option;
807811

808812
return c;
813+
814+
malformed:
815+
hts_log_error("Malformed varint header stream");
816+
free(c);
817+
return NULL;
809818
}
810819

811820
int cram_varint_encode_int(cram_slice *slice, cram_codec *c,
@@ -924,6 +933,9 @@ int cram_const_decode_byte(cram_slice *slice, cram_codec *c,
924933
cram_block *in, char *out, int *out_size) {
925934
int i, n;
926935

936+
if (!out)
937+
return 0;
938+
927939
for (i = 0, n = *out_size; i < n; i++)
928940
out[i] = c->u.xconst.val;
929941

@@ -978,12 +990,17 @@ cram_codec *cram_const_decode_init(cram_block_compression_hdr *hdr,
978990
return NULL;
979991

980992
c->codec = codec;
981-
if (codec == E_CONST_BYTE)
993+
if (codec == E_CONST_BYTE && option == E_BYTE)
982994
c->decode = cram_const_decode_byte;
983-
else if (option == E_INT)
995+
else if (codec == E_CONST_INT && (option == E_INT || option == E_SINT))
984996
c->decode = cram_const_decode_int;
985-
else
997+
else if (codec == E_CONST_INT && (option == E_LONG || option == E_SLONG))
986998
c->decode = cram_const_decode_long;
999+
else {
1000+
hts_log_error("Malformed const header stream");
1001+
free(c);
1002+
return NULL;
1003+
}
9871004
c->free = cram_const_decode_free;
9881005
c->size = cram_const_decode_size;
9891006
c->get_block = NULL;
@@ -1404,7 +1421,7 @@ int cram_xpack_decode_char(cram_slice *slice, cram_codec *c, cram_block *in, cha
14041421
if (out)
14051422
memcpy(out, b->data + b->byte, *out_size);
14061423
b->byte += *out_size;
1407-
} else {
1424+
} else if (out) {
14081425
memset(out, c->u.xpack.rmap[0], *out_size);
14091426
}
14101427

@@ -2111,7 +2128,8 @@ int cram_xrle_decode_char(cram_slice *slice, cram_codec *c, cram_block *in, char
21112128
cram_xrle_decode_expand_char(slice, c);
21122129
cram_block *b = slice->block_by_id[512 + c->codec_id];
21132130

2114-
memcpy(out, b->data + b->idx, n);
2131+
if (out)
2132+
memcpy(out, b->data + b->idx, n);
21152133
b->idx += n;
21162134
return 0;
21172135

@@ -3357,14 +3375,19 @@ int cram_byte_array_len_decode(cram_slice *slice, cram_codec *c,
33573375
int32_t len = 0, one = 1;
33583376
int r;
33593377

3360-
r = c->u.byte_array_len.len_codec->decode(slice, c->u.byte_array_len.len_codec,
3361-
in, (char *)&len, &one);
3362-
//printf("ByteArray Len=%d\n", len);
3378+
cram_codec *len_codec = c->u.byte_array_len.len_codec;
3379+
cram_codec *val_codec = c->u.byte_array_len.val_codec;
33633380

3364-
if (!r && c->u.byte_array_len.val_codec && len >= 0) {
3365-
r = c->u.byte_array_len.val_codec->decode(slice,
3366-
c->u.byte_array_len.val_codec,
3367-
in, out, &len);
3381+
r = len_codec->decode(slice, len_codec, in, (char *)&len, &one);
3382+
if (len < 0 || (len > *out_size &&
3383+
!(val_codec->codec == E_EXTERNAL &&
3384+
val_codec->u.external.type == E_BYTE_ARRAY_BLOCK))) {
3385+
fprintf(stderr, "Error: overflow in cram_byte_array_len_decode.\n");
3386+
return -1;
3387+
}
3388+
3389+
if (!r && val_codec) {
3390+
r = val_codec->decode(slice, val_codec, in, out, &len);
33683391
} else {
33693392
return -1;
33703393
}
@@ -3563,7 +3586,7 @@ cram_codec *cram_byte_array_len_encode_init(cram_stats *st,
35633586
static int cram_byte_array_stop_decode_char(cram_slice *slice, cram_codec *c,
35643587
cram_block *in, char *out,
35653588
int *out_size) {
3566-
char *cp, ch;
3589+
uint8_t *cp;
35673590
cram_block *b = NULL;
35683591

35693592
b = cram_get_block_by_id(slice, c->u.byte_array_stop.content_id);
@@ -3573,31 +3596,29 @@ static int cram_byte_array_stop_decode_char(cram_slice *slice, cram_codec *c,
35733596
if (b->idx >= b->uncomp_size)
35743597
return -1;
35753598

3576-
cp = (char *)b->data + b->idx;
3599+
ssize_t term = b->uncomp_size - b->idx;
3600+
cp = b->data + b->idx;
35773601
if (out) {
35783602
// memccpy equivalent but without copying the terminating byte
3579-
ssize_t term = MIN(*out_size, b->uncomp_size - b->idx);
3580-
while ((ch = *cp) != (char)c->u.byte_array_stop.stop) {
3581-
if (term-- < 0)
3582-
break;
3583-
*out++ = ch;
3584-
cp++;
3603+
if (term > *out_size)
3604+
term = *out_size;
3605+
while (--term >= 0 && *cp != c->u.byte_array_stop.stop) {
3606+
*out++ = *cp++;
35853607
}
35863608

3587-
// Attempted overrun on input or output
3588-
if (ch != (char)c->u.byte_array_stop.stop)
3589-
return -1;
35903609
} else {
35913610
// Consume input, but produce no output
3592-
while ((ch = *cp) != (char)c->u.byte_array_stop.stop) {
3593-
if (cp - (char *)b->data >= b->uncomp_size)
3594-
return -1;
3611+
while (--term >= 0 && *cp != c->u.byte_array_stop.stop) {
35953612
cp++;
35963613
}
35973614
}
35983615

3599-
*out_size = cp - (char *)(b->data + b->idx);
3600-
b->idx = cp - (char *)b->data + 1;
3616+
// Attempted overrun on input or output
3617+
if (cp >= b->data + b->uncomp_size || *cp != c->u.byte_array_stop.stop)
3618+
return -1;
3619+
3620+
*out_size = cp - (b->data + b->idx);
3621+
b->idx = cp - b->data + 1;
36013622

36023623
return 0;
36033624
}

0 commit comments

Comments
 (0)