Undefined behavior: Null pointer dereference in h2o_qpack_decoder_handle_input
Like in #3540, while porting h2o to the zig build system I discovered another undefined behavior bug.
In lib/http3/qpack.c, the function h2o_qpack_decoder_handle_input unconditionally accesses entries[0] even when the blocked_streams.list is empty and entries is NULL.
At line 490:
https://github.com/h2o/h2o/blob/2e62ee29c98ef70e9f1749884557229fd255a8e5/lib/http3/qpack.c#L490
When blocked_streams.list.size == 0, the entries pointer is NULL (set via memset in h2o_qpack_create_decoder).
The subsequent loop (lines 491-497) correctly checks num_unblocked < list.size and never executes when size is 0.
However, line 490 still executes and dereferences the NULL pointer via the subscript operator.
entries[0] is equivalent to *(entries + 0), which dereferences NULL when entries is NULL.
This is undefined behavior per the C standard, though I suppose it works in practice on most compilers because the actual dereference happens inside the address-of operator.
reproduce with:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void) {
struct entry { int64_t stream_id; int64_t largest_ref; };
struct { struct entry *entries; size_t size; } list = {NULL, 0};
// This is what h2o_qpack_decoder_handle_input does:
int64_t *result = &list.entries[0].stream_id;
printf("result: %p\n", (void*)result);
return 0;
}output:
$gcc -fsanitize=undefined test.c -o test
$ ./test
test.c:10:14: runtime error: member access within null pointer of type 'struct entry'
result: (nil)effect:
When QPACK encoder stream data arrives on a fresh connection before any streams have been blocked and the build used --sanitize=undefined, then there is a runtime panic.
Source: h2o/h2o