Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2982bfb
blob: fix wrong type for realloc result in blob_buffer_grow()
hauke Apr 8, 2026
78c20f6
json_script: convert recursive __json_script_file_free() to iterative
hauke Apr 8, 2026
e7c13bf
usock: fix off-by-one in nanosecond normalization in poll_restart()
hauke Apr 8, 2026
68b3f15
uloop: usock: add error checking for fcntl and remove duplicate include
hauke Apr 8, 2026
03821f9
uloop: fix undefined behavior in signal bit operations for signals > 32
hauke Apr 8, 2026
e6e6fd8
blobmsg: fix policy name length overflow and add bounds check in blob…
hauke Apr 8, 2026
d30b9cc
usock: fix integer overflow in timeout calculations
hauke Apr 8, 2026
406e342
udebug: fix double off-by-one in udebug_entry_vprintf()
hauke Apr 8, 2026
700eca0
blobmsg_json: fix integer overflow in blobmsg_puts()
hauke Apr 9, 2026
6351fe5
blobmsg_json: floor strbuf size and tighten the post-format guard
hauke Apr 9, 2026
58b6543
blobmsg: fix unsigned integer overflow in blobmsg_alloc_string_buffer()
hauke Apr 9, 2026
d7a3ae6
blobmsg: use correct byte-order macro when setting BLOB_ATTR_EXTENDED
hauke Apr 9, 2026
23c6618
blobmsg_json: fix double format string to avoid truncation and data loss
hauke Apr 16, 2026
1edf1d7
jshn: fix integer overflow and type confusion in jshn_parse_file
hauke Apr 16, 2026
9b48801
utils: fix integer overflow in __calloc_a()
hauke Apr 23, 2026
40a87f7
blob: fix integer overflow in buffer growth functions
hauke Apr 23, 2026
02fccb4
blob: use size_t for blob_memdup() length
hauke Apr 23, 2026
0fa612c
json_script: avoid alloca() on attacker-controlled pattern length
hauke Apr 23, 2026
8c9862b
blobmsg: fix integer overflow in blobmsg_realloc_string_buffer()
hauke Apr 23, 2026
5fbef5b
ustream: avoid INT_MAX overflow on malloc in ustream_vprintf()
hauke Apr 23, 2026
1501e60
md5: detect read errors in md5sum() instead of returning a bogus hash
hauke Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions blob.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <limits.h>

#include "blob.h"

static bool
blob_buffer_grow(struct blob_buf *buf, int minlen)
{
struct blob_buf *new;
int delta = ((minlen / 256) + 1) * 256;
void *new;
int delta;

if (minlen < 0 || minlen > INT_MAX - 256)
return false;

delta = ((minlen / 256) + 1) * 256;
if (buf->buflen < 0 || delta > INT_MAX - buf->buflen)
return false;

new = realloc(buf->buf, buf->buflen + delta);
if (new) {
buf->buf = new;
Expand Down Expand Up @@ -58,7 +68,9 @@ blob_buf_grow(struct blob_buf *buf, int required)
{
int offset_head = attr_to_offset(buf, buf->head);

if ((buf->buflen + required) > BLOB_ATTR_LEN_MASK)
if (required < 0 || buf->buflen < 0)
return false;
if (required > BLOB_ATTR_LEN_MASK - buf->buflen)
return false;
if (!buf->grow || !buf->grow(buf, required))
return false;
Expand Down Expand Up @@ -326,7 +338,7 @@ struct blob_attr *
blob_memdup(const struct blob_attr *attr)
{
struct blob_attr *ret;
int size = blob_pad_len(attr);
size_t size = blob_pad_len(attr);

ret = malloc(size);
if (!ret)
Expand Down
34 changes: 29 additions & 5 deletions blobmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <limits.h>

#include "blobmsg.h"

static const int blob_type[__BLOBMSG_TYPE_LAST] = {
Expand Down Expand Up @@ -163,18 +165,30 @@ int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
return 0;
}

/*
* Upper bound on the number of policy entries blobmsg_parse() will
* accept. The pslen scratch array is allocated on the stack via
* alloca(); 4096 entries cap the per-call stack use at
* 4096 * sizeof(uint16_t) = 8 KiB, which comfortably fits the typical
* 8 KiB thread default while being far above any realistic policy
* size used in practice (a few dozen entries).
*/
#define BLOBMSG_PARSE_MAX_POLICY 4096

int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len)
{
const struct blobmsg_hdr *hdr;
struct blob_attr *attr;
uint8_t *pslen;
uint16_t *pslen;
int i;

memset(tb, 0, policy_len * sizeof(*tb));
if (!data || !len)
return -EINVAL;
pslen = alloca(policy_len);
if (policy_len < 0 || policy_len > BLOBMSG_PARSE_MAX_POLICY)
return -ENOMEM;
pslen = alloca(policy_len * sizeof(*pslen));
for (i = 0; i < policy_len; i++) {
if (!policy[i].name)
continue;
Expand Down Expand Up @@ -240,7 +254,7 @@ blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, v
if (!attr)
return NULL;

attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED);
attr->id_len |= cpu_to_be32(BLOB_ATTR_EXTENDED);
hdr = blob_data(attr);
hdr->namelen = cpu_to_be16(namelen);

Expand Down Expand Up @@ -328,6 +342,8 @@ blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int
struct blob_attr *attr;
void *data_dest;

if (maxlen == (unsigned int)-1)
return NULL;
maxlen++;
attr = blobmsg_new(buf, BLOBMSG_TYPE_STRING, name, maxlen, &data_dest);
if (!attr)
Expand All @@ -344,11 +360,19 @@ blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen)
{
struct blob_attr *attr = blob_next(buf->head);
int offset = attr_to_offset(buf, blob_next(buf->head)) + blob_pad_len(attr) - BLOB_COOKIE;
int required = maxlen + 1 - (buf->buflen - offset);
int required;

if (required <= 0)
if (maxlen >= INT_MAX)
return NULL;

if (buf->buflen < 0 || offset < 0 || offset > buf->buflen)
return NULL;

if ((int)maxlen + 1 <= buf->buflen - offset)
goto out;

required = (int)maxlen + 1 - (buf->buflen - offset);

if (!blob_buf_grow(buf, required))
return NULL;
attr = blob_next(buf->head);
Expand Down
34 changes: 24 additions & 10 deletions blobmsg_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str)


struct strbuf {
int len;
int pos;
size_t len;
size_t pos;
char *buf;

blobmsg_json_format_t custom_format;
Expand All @@ -122,16 +122,28 @@ struct strbuf {
int indent_level;
};

static bool blobmsg_puts(struct strbuf *s, const char *c, int len)
/*
* Minimum and growth slack for the JSON strbuf. The minimum size
* keeps malloc(0) out of setup_strbuf() and is large enough to hold
* any short scalar serialisation ("null", "true", "false", small
* numbers) without an immediate realloc. The same value is added on
* each grow in blobmsg_puts() so that successive small writes are
* amortised across a few extra bytes per realloc.
*/
#define STRBUF_MIN_SIZE 16

static bool blobmsg_puts(struct strbuf *s, const char *c, size_t len)
{
size_t new_len;
char *new_buf;

if (len <= 0)
if (!len)
return true;

if (s->pos + len >= s->len) {
new_len = s->len + 16 + len;
if (s->len - s->pos <= len) {
if (len > SIZE_MAX - STRBUF_MIN_SIZE - s->len)
return false;
new_len = s->len + STRBUF_MIN_SIZE + len;
new_buf = realloc(s->buf, new_len);
if (!new_buf)
return false;
Expand Down Expand Up @@ -170,7 +182,7 @@ static void blobmsg_format_string(struct strbuf *s, const char *str)
blobmsg_puts(s, "\"", 1);
for (p = (unsigned char *) str, last = p; *p; p++) {
char escape = '\0';
int len;
size_t len;

switch(*p) {
case '\b':
Expand Down Expand Up @@ -260,7 +272,7 @@ static void blobmsg_format_element(struct strbuf *s, struct blob_attr *attr, boo
snprintf(buf, sizeof(buf), "%" PRId64, (int64_t) be64_to_cpu(*(uint64_t *)data));
break;
case BLOBMSG_TYPE_DOUBLE:
snprintf(buf, sizeof(buf), "%lf", blobmsg_get_double(attr));
snprintf(buf, sizeof(buf), "%.17g", blobmsg_get_double(attr));
break;
case BLOBMSG_TYPE_STRING:
blobmsg_format_string(s, data);
Expand Down Expand Up @@ -303,6 +315,8 @@ static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, i
static void setup_strbuf(struct strbuf *s, struct blob_attr *attr, blobmsg_json_format_t cb, void *priv, int indent)
{
s->len = blob_len(attr);
if (s->len < STRBUF_MIN_SIZE)
s->len = STRBUF_MIN_SIZE;
s->buf = malloc(s->len);
s->pos = 0;
s->custom_format = cb;
Expand Down Expand Up @@ -333,7 +347,7 @@ char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, blobmsg_jso
else
blobmsg_format_element(&s, attr, false, false);

if (!s.len) {
if (!s.pos) {
free(s.buf);
return NULL;
}
Expand All @@ -360,7 +374,7 @@ char *blobmsg_format_json_value_with_cb(struct blob_attr *attr, blobmsg_json_for

blobmsg_format_element(&s, attr, true, false);

if (!s.len) {
if (!s.pos) {
free(s.buf);
return NULL;
}
Expand Down
12 changes: 10 additions & 2 deletions jshn.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ static int jshn_parse_file(const char *path)
struct stat sb;
int ret = 0;
char *fbuf;
ssize_t n;
int fd;

if ((fd = open(path, O_RDONLY)) == -1) {
Expand All @@ -351,13 +352,20 @@ static int jshn_parse_file(const char *path)
return 3;
}

if (!(fbuf = calloc(1, sb.st_size+1))) {
if (sb.st_size < 0 || (uintmax_t)sb.st_size >= SIZE_MAX) {
fprintf(stderr, "File %s has invalid size\n", path);
close(fd);
return 3;
}

if (!(fbuf = calloc(1, (size_t)sb.st_size + 1))) {
fprintf(stderr, "Error allocating memory for %s\n", path);
close(fd);
return 3;
}

if (read(fd, fbuf, sb.st_size) != sb.st_size) {
n = read(fd, fbuf, (size_t)sb.st_size);
if (n < 0 || n != sb.st_size) {
fprintf(stderr, "Error reading %s\n", path);
free(fbuf);
close(fd);
Expand Down
30 changes: 18 additions & 12 deletions json_script.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,17 +426,24 @@ static int json_process_expr(struct json_call *call, struct blob_attr *cur)

static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern)
{
char *dest, *next, *str;
char *dest, *next, *str, *buffer;
size_t pattern_len;
int len = 0;
bool var = false;
char c = '%';

dest = blobmsg_alloc_string_buffer(buf, name, 0);
if (!dest)
pattern_len = strlen(pattern);
buffer = malloc(pattern_len + 1);
if (!buffer)
return -1;
memcpy(buffer, pattern, pattern_len + 1);
next = buffer;

next = alloca(strlen(pattern) + 1);
strcpy(next, pattern);
dest = blobmsg_alloc_string_buffer(buf, name, 0);
if (!dest) {
free(buffer);
return -1;
}

for (str = next; str; str = next) {
const char *cur;
Expand Down Expand Up @@ -487,6 +494,7 @@ static int eval_string(struct json_call *call, struct blob_buf *buf, const char

dest[len] = 0;
blobmsg_add_string_buffer(buf);
free(buffer);

if (var)
return -1;
Expand Down Expand Up @@ -639,13 +647,11 @@ static void __json_script_file_free(struct json_script_file *f)
{
struct json_script_file *next;

if (!f)
return;

next = f->next;
free(f);

__json_script_file_free(next);
while (f) {
next = f->next;
free(f);
f = next;
}
}

void
Expand Down
9 changes: 7 additions & 2 deletions md5.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,14 @@ int md5sum(const char *file, void *md5_buf)

md5_begin(&ctx);
do {
int len = fread(buf, 1, sizeof(buf), f);
if (!len)
size_t len = fread(buf, 1, sizeof(buf), f);
if (!len) {
if (ferror(f)) {
fclose(f);
return -1;
}
break;
}

md5_hash(buf, len, &ctx);
ret += len;
Expand Down
8 changes: 4 additions & 4 deletions tests/cram/test_blobmsg.t
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ check that blobmsg is producing expected results:
\tworld : 2 (str) (esc)
}

[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":0.000000,"dbl-max":179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,0.000000,179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000]}
[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":2.2250738585072014e-308,"dbl-max":1.7976931348623157e+308,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,2.2250738585072014e-308,1.7976931348623157e+308]}

[*] blobmsg from json:
Message: Hello, world!
Expand Down Expand Up @@ -102,7 +102,7 @@ check that blobmsg is producing expected results:
\tworld : 2 (str) (esc)
}

[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":0.000000,"dbl-max":179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,0.000000,179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000]}
[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":2.2250738585072014e-308,"dbl-max":1.7976931348623157e+308,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,2.2250738585072014e-308,1.7976931348623157e+308]}

[*] blobmsg from json:
Message: Hello, world!
Expand Down Expand Up @@ -169,7 +169,7 @@ check that blobmsg is producing expected results:
\tworld : 2 (str) (esc)
}

[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":0.000000,"dbl-max":179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,0.000000,179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000]}
[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":2.2250738585072014e-308,"dbl-max":1.7976931348623157e+308,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,2.2250738585072014e-308,1.7976931348623157e+308]}

[*] blobmsg from json:
Message: Hello, world!
Expand Down Expand Up @@ -236,7 +236,7 @@ check that blobmsg is producing expected results:
\tworld : 2 (str) (esc)
}

[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":0.000000,"dbl-max":179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,0.000000,179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000]}
[*] blobmsg to json: {"message":"Hello, world!","testdata":{"dbl-min":2.2250738585072014e-308,"dbl-max":1.7976931348623157e+308,"foo":false,"poo":true,"moo-min":true,"moo-max":true,"bar-min":-32768,"bar-max":32767,"baz-min":-2147483648,"baz-max":2147483647,"taz-min":-9223372036854775808,"taz-max":9223372036854775807,"world":"2"},"list":[false,true,true,true,-32768,32767,-2147483648,2147483647,-9223372036854775808,9223372036854775807,2.2250738585072014e-308,1.7976931348623157e+308]}

[*] blobmsg from json:
Message: Hello, world!
Expand Down
8 changes: 4 additions & 4 deletions tests/cram/test_blobmsg_types.t
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ check that blobmsg is producing expected results:
double_max: 1.797693e+308
double_min: 2.225074e-308

[*] blobmsg to json: {"string":"Hello, world!","int64_max":9223372036854775807,"int64_min":-9223372036854775808,"int32_max":2147483647,"int32_min":-2147483648,"int16_max":32767,"int16_min":-32768,"int8_max":true,"int8_min":true,"double_max":179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000,"double_min":0.000000}
[*] blobmsg to json: {"string":"Hello, world!","int64_max":9223372036854775807,"int64_min":-9223372036854775808,"int32_max":2147483647,"int32_min":-2147483648,"int16_max":32767,"int16_min":-32768,"int8_max":true,"int8_min":true,"double_max":1.7976931348623157e+308,"double_min":2.2250738585072014e-308}

[*] blobmsg from json:
string: Hello, world!
Expand All @@ -53,7 +53,7 @@ check that blobmsg is producing expected results:
int8_max: 1
int8_min: 1
double_max: 1.797693e+308
double_min: 0.000000e+00
double_min: 2.225074e-308

[*] blobmsg from json/cast_u64:
string: Hello, world!
Expand All @@ -66,7 +66,7 @@ check that blobmsg is producing expected results:
int8_max: 1
int8_min: 1
double_max: 1.797693e+308
double_min: 0.000000e+00
double_min: 2.225074e-308

[*] blobmsg from json/cast_s64:
string: Hello, world!
Expand All @@ -79,4 +79,4 @@ check that blobmsg is producing expected results:
int8_max: 1
int8_min: 1
double_max: 1.797693e+308
double_min: 0.000000e+00
double_min: 2.225074e-308
Loading
Loading