Skip to content

Commit 97c0681

Browse files
committed
Release 4.8.1
- [FIX] Send controller: bytes-scheduled underflow (issue #652). - [FIX] Enforce stream limit in RST_STREAM (issue #654). - [FIX] Reject invalid Content-Length syntax. - [IMPROVEMENT] Top-level cmake ASAN options.
1 parent 23ac195 commit 97c0681

7 files changed

Lines changed: 137 additions & 4 deletions

File tree

CHANGELOG

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
2026-06-20
2+
- 4.8.1
3+
- [FIX] Send controller: bytes-scheduled underflow (issue #652).
4+
- [FIX] Enforce stream limit in RST_STREAM (issue #654).
5+
- [FIX] Reject invalid Content-Length syntax.
6+
- [IMPROVEMENT] Top-level cmake ASAN options.
7+
18
2026-06-15
29
- 4.8.0
310
- [API] Deprecate all gQUIC versions.

CONTRIBUTORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ to the LiteSpeed QUIC and HTTP/3 Library:
4444
- Richard Ramos -- Windows fixes
4545
- youngseaz -- 32-bit transport param parser bug report
4646
- X1AOxiang -- Bug fixes
47+
- Alexey Mamontov -- Long-standing scheduled bytes underflow bug fix
4748

4849
Thank you!
4950

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
# The short X.Y version
2727
version = u'4.8'
2828
# The full version, including alpha/beta/rc tags
29-
release = u'4.8.0'
29+
release = u'4.8.1'
3030

3131

3232
# -- General configuration ---------------------------------------------------

include/lsquic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extern "C" {
2727

2828
#define LSQUIC_MAJOR_VERSION 4
2929
#define LSQUIC_MINOR_VERSION 8
30-
#define LSQUIC_PATCH_VERSION 0
30+
#define LSQUIC_PATCH_VERSION 1
3131

3232
#define LSQUIC_QUOTE(x) #x
3333
#define LSQUIC_SVAL(v) LSQUIC_QUOTE(v)

src/liblsquic/lsquic_qdec_hdl.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,16 +469,25 @@ process_content_length (const struct qpack_dec_hdl *qdh /* for logging */,
469469
unsigned len)
470470
{
471471
char *endcl, cont_len_buf[30];
472+
unsigned i;
472473

473474
if (0 == cl->has)
474475
{
475-
if (len >= sizeof(cont_len_buf))
476+
if (0 == len || len >= sizeof(cont_len_buf))
476477
{
477478
LSQ_DEBUG("content-length has invalid value `%.*s'",
478479
(int) len, val);
479480
cl->has = -1;
480481
return;
481482
}
483+
for (i = 0; i < len; ++i)
484+
if (val[i] < '0' || val[i] > '9')
485+
{
486+
LSQ_DEBUG("content-length has invalid value `%.*s'",
487+
(int) len, val);
488+
cl->has = -1;
489+
return;
490+
}
482491
memcpy(cont_len_buf, val, len);
483492
cont_len_buf[len] = '\0';
484493
cl->value = strtoull(cont_len_buf, &endcl, 10);

tests/test_h3_framing.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,49 @@ const struct lsquic_stream_if stream_if = {
181181
};
182182

183183

184+
struct dummy_hset
185+
{
186+
struct lsxpack_header xhdr;
187+
char buf[0x100];
188+
};
189+
190+
191+
static void *
192+
dummy_create_header_set (void *hsi_ctx, lsquic_stream_t *stream,
193+
int is_push_promise)
194+
{
195+
return calloc(1, sizeof(struct dummy_hset));
196+
}
197+
198+
199+
static struct lsxpack_header *
200+
dummy_prepare_decode (void *hdr_set, struct lsxpack_header *hdr, size_t space)
201+
{
202+
struct dummy_hset *const dummy = hdr_set;
203+
204+
if (space > sizeof(dummy->buf))
205+
return NULL;
206+
207+
lsxpack_header_prepare_decode(&dummy->xhdr, dummy->buf, 0,
208+
sizeof(dummy->buf));
209+
return &dummy->xhdr;
210+
}
211+
212+
213+
static int
214+
dummy_process_header (void *hdr_set, struct lsxpack_header *hdr)
215+
{
216+
return 0;
217+
}
218+
219+
220+
static void
221+
dummy_discard_header_set (void *hdr_set)
222+
{
223+
free(hdr_set);
224+
}
225+
226+
184227
static size_t
185228
read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t stream_id,
186229
unsigned char *const begin, size_t bufsz, uint64_t first_offset, int *p_fin,
@@ -1755,6 +1798,78 @@ test_reading_zero_size_data_frame_scenario3 (void)
17551798
}
17561799

17571800

1801+
static void
1802+
expect_content_length_not_verified (struct lsxpack_header *header_arr,
1803+
unsigned count)
1804+
{
1805+
struct test_objs tobjs;
1806+
struct lsquic_stream *stream;
1807+
struct lsquic_http_headers headers = { count, header_arr, };
1808+
const unsigned char *p;
1809+
unsigned char buf[0x400];
1810+
const size_t prefix_cap = 32;
1811+
size_t prefix_sz = prefix_cap, headers_sz = sizeof(buf) - prefix_cap;
1812+
uint64_t compl_off;
1813+
enum qwh_status qwh;
1814+
1815+
init_test_ctl_settings(&g_ctl_settings);
1816+
1817+
stream_ctor_flags |= SCF_IETF;
1818+
init_test_objs(&tobjs, 0x1000, 0x2000, 1252);
1819+
tobjs.hsi_if = (struct lsquic_hset_if) {
1820+
.hsi_create_header_set = dummy_create_header_set,
1821+
.hsi_prepare_decode = dummy_prepare_decode,
1822+
.hsi_process_header = dummy_process_header,
1823+
.hsi_discard_header_set = dummy_discard_header_set,
1824+
};
1825+
tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
1826+
1827+
stream = new_stream(&tobjs, 0, 0x1000);
1828+
1829+
qwh = lsquic_qeh_write_headers(&tobjs.qeh, stream->id, 0, &headers,
1830+
buf + prefix_cap, &prefix_sz, &headers_sz, &compl_off,
1831+
NULL);
1832+
assert(qwh == QWH_FULL);
1833+
1834+
p = buf + prefix_cap - prefix_sz;
1835+
assert(LQRHS_DONE == lsquic_qdh_header_in_begin(&tobjs.qdh, stream,
1836+
prefix_sz + headers_sz, &p,
1837+
prefix_sz + headers_sz));
1838+
1839+
assert(!(stream->sm_bflags & SMBF_VERIFY_CL));
1840+
1841+
lsquic_stream_destroy(stream);
1842+
deinit_test_objs(&tobjs);
1843+
1844+
stream_ctor_flags &= ~SCF_IETF;
1845+
}
1846+
1847+
1848+
static void
1849+
test_invalid_content_length_syntax_is_rejected (void)
1850+
{
1851+
struct lsxpack_header plus_arr[] = {
1852+
{ XHDR(":status", "200") },
1853+
{ XHDR("content-length", "+1") },
1854+
};
1855+
struct lsxpack_header space_arr[] = {
1856+
{ XHDR(":status", "200") },
1857+
{ XHDR("content-length", " 1") },
1858+
};
1859+
struct lsxpack_header empty_arr[] = {
1860+
{ XHDR(":status", "200") },
1861+
{ XHDR("content-length", "") },
1862+
};
1863+
1864+
expect_content_length_not_verified(plus_arr,
1865+
sizeof(plus_arr) / sizeof(plus_arr[0]));
1866+
expect_content_length_not_verified(space_arr,
1867+
sizeof(space_arr) / sizeof(space_arr[0]));
1868+
expect_content_length_not_verified(empty_arr,
1869+
sizeof(empty_arr) / sizeof(empty_arr[0]));
1870+
}
1871+
1872+
17581873
static void
17591874
test_content_length_overrun_blocks_payload_delivery (void)
17601875
{
@@ -1872,6 +1987,7 @@ main (int argc, char **argv)
18721987
test_reading_zero_size_data_frame();
18731988
test_reading_zero_size_data_frame_scenario2();
18741989
test_reading_zero_size_data_frame_scenario3();
1990+
test_invalid_content_length_syntax_is_rejected();
18751991
test_content_length_overrun_blocks_payload_delivery();
18761992
break;
18771993
default:
@@ -1892,6 +2008,7 @@ main (int argc, char **argv)
18922008
test_reading_zero_size_data_frame();
18932009
test_reading_zero_size_data_frame_scenario2();
18942010
test_reading_zero_size_data_frame_scenario3();
2011+
test_invalid_content_length_syntax_is_rejected();
18952012
test_content_length_overrun_blocks_payload_delivery();
18962013
}
18972014

tests/test_send_ctl_accounting.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* Copyright (c) 2017 - 2026 LiteSpeed Technologies Inc. See LICENSE. */
2-
32
#include <assert.h>
43
#include <stdint.h>
54
#include <stdlib.h>

0 commit comments

Comments
 (0)