aboutsummaryrefslogtreecommitdiffstats
path: root/src/jsonval.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-19 22:48:31 +0200
committeruser <user@clank>2026-08-19 22:48:31 +0200
commit009350acbcd4360680b8fa228d8cd03a2f2b5e93 (patch)
treeef0eadb14cc8186829c4b7ce46955ea88443a405 /src/jsonval.c
parentwebdemo: the theme symlink is a dependency of the image (diff)
downloadslosh-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.c20
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;
}