Fix binary varint decoders to reject values overflowing uint32_t/uint64_t declared widths

This commit is contained in:
Andrea Mazzoleni 2026-07-30 10:58:35 +02:00
parent 02413b756c
commit 05884d7aa0
2 changed files with 99 additions and 4 deletions

View file

@ -13,6 +13,7 @@
#include "elem.h"
#include "state.h"
#include "support.h"
#include "stream.h"
#include "tommyds/tommyhash.h"
#include "tommyds/tommyarray.h"
#include "tommyds/tommyarrayblkof.h"
@ -994,6 +995,85 @@ static void test_smart_ignore(void)
}
}
static void test_stream(void)
{
STREAM f;
uint32_t v32;
uint64_t v64;
/* 32-bit valid boundary */
unsigned char buf_v32[] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x8f };
memset(&f, 0, sizeof(f));
f.pos = buf_v32;
f.end = buf_v32 + sizeof(buf_v32);
if (sgetb32(&f, &v32) != 0 || v32 != 0xffffffffU) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "test_stream: valid uint32 max failed\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* 32-bit overflow payload in 5th terminal byte */
unsigned char buf_o32[] = { 0x00, 0x00, 0x00, 0x00, 0xf1 };
memset(&f, 0, sizeof(f));
f.pos = buf_o32;
f.end = buf_o32 + sizeof(buf_o32);
if (sgetb32(&f, &v32) == 0) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "test_stream: uint32 overflow accepted\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* 32-bit 5th byte non-terminal */
unsigned char buf_nt32[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x80 };
memset(&f, 0, sizeof(f));
f.pos = buf_nt32;
f.end = buf_nt32 + sizeof(buf_nt32);
if (sgetb32(&f, &v32) == 0) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "test_stream: uint32 5th non-terminal accepted\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* 64-bit valid boundary */
unsigned char buf_v64[] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x81 };
memset(&f, 0, sizeof(f));
f.pos = buf_v64;
f.end = buf_v64 + sizeof(buf_v64);
if (sgetb64(&f, &v64) != 0 || v64 != 0xffffffffffffffffULL) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "test_stream: valid uint64 max failed\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* 64-bit overflow payload in 10th terminal byte */
unsigned char buf_o64[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83 };
memset(&f, 0, sizeof(f));
f.pos = buf_o64;
f.end = buf_o64 + sizeof(buf_o64);
if (sgetb64(&f, &v64) == 0) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "test_stream: uint64 overflow accepted\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
/* 64-bit 10th byte non-terminal */
unsigned char buf_nt64[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80 };
memset(&f, 0, sizeof(f));
f.pos = buf_nt64;
f.end = buf_nt64 + sizeof(buf_nt64);
if (sgetb64(&f, &v64) == 0) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "test_stream: uint64 10th non-terminal accepted\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
void selftest(void)
{
@ -1019,6 +1099,7 @@ void selftest(void)
test_hash();
test_crc32c();
test_tommy();
test_stream();
test_wnmatch();
test_parse_smartctl();
test_smart_ignore();

View file

@ -586,7 +586,7 @@ int sgetb32(STREAM* f, uint32_t* value)
{
uint32_t v;
unsigned char b;
unsigned char s;
unsigned s;
int c;
v = 0;
@ -603,7 +603,7 @@ loop:
if ((b & 0x80) == 0) {
v |= (uint32_t)b << s;
s += 7;
if (s >= 32) {
if (tommy_unlikely(s >= 32)) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
@ -611,6 +611,13 @@ loop:
goto loop;
}
/*
* Check for overflow: at s == 28 only 4 bits (0x0f) fit in uint32_t
*/
if (tommy_unlikely(s == 28 && (b & 0x70) != 0)) {
return -1;
}
v |= (uint32_t)(b & 0x7f) << s;
*value = v;
@ -622,7 +629,7 @@ int sgetb64(STREAM* f, uint64_t* value)
{
uint64_t v;
unsigned char b;
unsigned char s;
unsigned s;
int c;
v = 0;
@ -639,7 +646,7 @@ loop:
if ((b & 0x80) == 0) {
v |= (uint64_t)b << s;
s += 7;
if (s >= 64) {
if (tommy_unlikely(s >= 64)) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
@ -647,6 +654,13 @@ loop:
goto loop;
}
/*
* Check for overflow: at s == 63 only 1 bit (0x01) fits in uint64_t
*/
if (tommy_unlikely(s == 63 && (b & 0x7e) != 0)) {
return -1;
}
v |= (uint64_t)(b & 0x7f) << s;
*value = v;