diff options
| author | Hauke Mehrtens | 2026-04-09 20:23:01 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2026-06-18 22:06:50 +0000 |
| commit | 6f01162d29d6c9412f8c98f365a4e861274d5ff7 (patch) | |
| tree | 5681bc2eb4f4eb83a0e4bf472a30605811ff34aa | |
| parent | afc0fa8680e2076af534c5ebc4dd73f5d8658e25 (diff) | |
| download | libubox-6f01162d29d6c9412f8c98f365a4e861274d5ff7.tar.gz | |
blobmsg_json: floor strbuf size and tighten the post-format guard
setup_strbuf() initialised the strbuf size from blob_len(attr). For
attributes with zero or very small payloads this resulted in a
malloc(0) call, whose return value is implementation-defined: glibc
hands back a non-null pointer, but other allocators may return NULL,
which made blobmsg_format_json_with_cb() and
blobmsg_format_json_value_with_cb() fail for valid but empty blobs.
Floor the initial allocation at 16 bytes so the strbuf is always
usable regardless of input size; the buffer still grows on demand
in blobmsg_puts(). 16 bytes is enough to hold the literal scalars
("null", "true", "false") and small integer serialisations
(int8/int16/int32) without an immediate realloc; int64 and double
formatting still triggers one extra realloc, which is acceptable.
With s->len now guaranteed to be >= 16 whenever s->buf is non-NULL,
the post-format guard
if (!s.len) { free(s.buf); return NULL; }
in both formatters becomes dead code, so empty/invalid attributes
that produced no output are returned as a freshly-malloc'd empty
buffer instead of NULL. The original intent of the guard was to
detect "nothing was written"; the correct expression for that is
!s.pos. Switch the check accordingly so callers continue to see
NULL on attributes that produce no output (and the leftover buffer
is freed instead of being leaked through the caller as an empty
string).
Finally, give the floor/grow-slack value a name, STRBUF_MIN_SIZE,
and a short comment explaining what it is and why; the same value
was already used as the realloc slack in blobmsg_puts(), and using
the named constant in both places makes the relationship explicit.
Link: https://github.com/openwrt/libubox/pull/42
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
(cherry picked from commit 6351fe552162394429f263feb842cad3735110e7)
| -rw-r--r-- | blobmsg_json.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/blobmsg_json.c b/blobmsg_json.c index 0fa5d85..49eea61 100644 --- a/blobmsg_json.c +++ b/blobmsg_json.c @@ -122,6 +122,16 @@ struct strbuf { int indent_level; }; +/* + * 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; @@ -131,7 +141,9 @@ static bool blobmsg_puts(struct strbuf *s, const char *c, size_t len) return true; if (s->len - s->pos <= len) { - new_len = s->len + 16 + 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; @@ -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; @@ -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; } @@ -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; } |