diff options
| author | user <user@clank> | 2026-08-19 22:48:31 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-19 22:48:31 +0200 |
| commit | 009350acbcd4360680b8fa228d8cd03a2f2b5e93 (patch) | |
| tree | ef0eadb14cc8186829c4b7ce46955ea88443a405 /src/jsonval.c | |
| parent | webdemo: the theme symlink is a dependency of the image (diff) | |
| download | slosh-009350acbcd4360680b8fa228d8cd03a2f2b5e93.tar.gz slosh-009350acbcd4360680b8fa228d8cd03a2f2b5e93.tar.xz slosh-009350acbcd4360680b8fa228d8cd03a2f2b5e93.zip | |
parsers: fix seven bugs found by fuzzing
kdl: heap-buffer-overflow on trailing backslash in a string (a"\)
jsonval: heap-buffer-overflow on truncated \uXXXX escape ("\u)
jsonval: UB float-to-long cast on out-of-range numbers (2e22)
expr: UB signed overflow in arithmetic ops — wrap as uint32
expr: INT32_MIN / -1 SIGFPE guard in OP_DIV/OP_MOD
expr: stack overflow on deep nesting — add MAX_DEPTH 256
expr: 64-bit + saturation in OP_DIST to prevent squared overflow
input: NULL memcpy in input_timeout's re-parse path
input: signed overflow in CSI and SGR mouse parameter accumulation
Diffstat (limited to 'src/jsonval.c')
| -rw-r--r-- | src/jsonval.c | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/src/jsonval.c b/src/jsonval.c index bd718b1..48cc870 100644 --- a/src/jsonval.c +++ b/src/jsonval.c @@ -1,6 +1,7 @@ #define _GNU_SOURCE #include "jsonval.h" +#include <limits.h> #include <stdlib.h> #include <string.h> @@ -80,9 +81,17 @@ static char *parse_string_raw(P *s, size_t *out_len) { case '"': *o++ = '"'; break; case '\\': *o++ = '\\'; break; case 'u': { + /* \uXXXX needs exactly four hex digits after the 'u'; a truncated + * escape must not read past the NUL terminator. */ + if (!s->p[1] || !s->p[2] || !s->p[3] || !s->p[4]) { + s->bad = true; + free(buf); + return NULL; + } unsigned cp = hex4(s->p + 1); s->p += 4; - if (cp >= 0xd800 && cp < 0xdc00 && s->p[1] == '\\' && s->p[2] == 'u') { + if (cp >= 0xd800 && cp < 0xdc00 && s->p[1] == '\\' && s->p[2] == 'u' && + s->p[3] && s->p[4] && s->p[5] && s->p[6]) { unsigned lo = hex4(s->p + 3); s->p += 6; cp = 0x10000 + ((cp - 0xd800) << 10) + (lo - 0xdc00); @@ -273,7 +282,14 @@ const char *jv_str(const jv_t *v, const char *fallback) { long jv_int(const jv_t *v, long fallback) { if (!v) return fallback; - if (v->kind == JV_NUM) return (long)v->num; + if (v->kind == JV_NUM) { + /* A double outside LONG_MIN..LONG_MAX is UB to cast. Clamp rather than + * returning fallback so a "big number" is still big and not silently + * replaced by a default. */ + if (v->num >= (double)LONG_MAX) return LONG_MAX; + if (v->num <= (double)LONG_MIN) return LONG_MIN; + return (long)v->num; + } if (v->kind == JV_STR) return strtol(v->str, NULL, 10); return fallback; } |
