* [LTP] [PATCH 00/15] Fix ujson bugs
@ 2026-10-05 13:25 Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding Andrea Cervesato
` (14 more replies)
0 siblings, 15 replies; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
- fix 4-byte UTF-8 sequence decoding in ujson_utf8_next()
- fix out-of-bounds read on invalid UTF-8 in ujson_writer
- prevent infinite loop when read() returns 0 in ujson_reader
- fix inverted open() error check in ujson_writer_file_open()
- combine UTF-16 surrogate pairs and reject lone surrogates
- check for signed integer overflow when parsing numbers
- fix error propagation when newline() fails in ujson_writer
- check output return values in ESC_FLUSH macro
- avoid skewing recursion depth on overflow in ujson_reader
- unescape object ID strings in ujson_reader
- support reading files with zero lseek size such as procfs
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
Andrea Cervesato (15):
ujson: Fix 4-byte UTF-8 sequence decoding
ujson: writer: Fix OOB read on invalid UTF-8
ujson: reader: Prevent infinite loop on EOF
ujson: writer: Fix open() return value check
ujson: reader: Combine UTF-16 surrogate pairs
ujson: reader: Check for signed integer overflow
ujson: writer: Return error when newline() fails
ujson: writer: Check return value in ESC_FLUSH
ujson: reader: Do not increment depth on overflow
ujson: reader: Parse escape sequences in object IDs
ujson: reader: Support files with zero lseek size
ujson: reader: Reject \u0000 escape in strings
ujson: writer: Set errno when a flush fails
ujson: writer: Advance buffer on short write
ujson: writer: Flush buffer before direct writes
include/ujson_utf.h | 6 +--
libs/ujson/ujson_reader.c | 127 ++++++++++++++++++++++++++++++----------------
libs/ujson/ujson_writer.c | 37 ++++++++++----
3 files changed, 114 insertions(+), 56 deletions(-)
---
base-commit: 78e6353fdff663b2fc2f77704ceec6c3ca786b96
change-id: 20260925-fix_core_lib_deepseek-2ba9018c7588
Best regards,
--
Andrea Cervesato <andrea.cervesato@suse.com>
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-05 14:57 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 02/15] ujson: writer: Fix OOB read on invalid UTF-8 Andrea Cervesato
` (13 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
In ujson_utf8_next(), decoding 4-byte UTF-8 sequences read past the
byte boundary, verified the already-masked byte s2 instead of the
fourth byte s3, and failed to advance the string pointer correctly.
As a result, any 4-byte UTF-8 sequence was rejected and returned 0.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
include/ujson_utf.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/ujson_utf.h b/include/ujson_utf.h
index 83fe8ab09..801a1fcb2 100644
--- a/include/ujson_utf.h
+++ b/include/ujson_utf.h
@@ -65,15 +65,15 @@ static inline uint32_t ujson_utf8_next(const char **str)
if (UJSON_UTF8_IS_3BYTE(s0))
return (s0 & 0x0f)<<12 | s1<<6 | s2;
- (*str)++;
-
uint32_t s3 = *str[0];
- if (!UJSON_UTF8_IS_NBYTE(s2))
+ if (!UJSON_UTF8_IS_NBYTE(s3))
return 0;
s3 &= UJSON_UTF8_NBYTE_MASK;
+ (*str)++;
+
if (UJSON_UTF8_IS_4BYTE(s0))
return (s0 & 0x07)<<18 | s1<<12 | s2<<6 | s3;
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 02/15] ujson: writer: Fix OOB read on invalid UTF-8
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-05 15:04 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF Andrea Cervesato
` (12 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
ujson_utf8_next_chsz() returns -1 on invalid UTF-8 sequences.
Because i is unsigned (size_t), adding -1 moves i back by one byte.
When i > 0, the loop steps back to the previous character and then
forward onto the invalid byte again, looping forever. When i == 0
(invalid byte at the start of the string or right after an escaped
character), i wraps to SIZE_MAX, causing out-of-bounds reads in
subsequent iterations and during output flushing. Stop and set an
error when an invalid UTF-8 byte sequence is encountered.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index c86a3b2af..b5da494d1 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -114,6 +114,10 @@ static inline int out_esc_str(ujson_writer *self, const char *val)
do {
next_chsz = ujson_utf8_next_chsz(val, i);
+ if (next_chsz < 0) {
+ err(self, "Invalid UTF-8 sequence");
+ return 1;
+ }
if (next_chsz == 1) {
switch (val[i]) {
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 02/15] ujson: writer: Fix OOB read on invalid UTF-8 Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-05 15:12 ` Cyril Hrubis
2026-10-05 18:06 ` [LTP] ujson: Fix 4-byte UTF-8 sequence decoding linuxtestproject.agent
2026-10-05 13:25 ` [LTP] [PATCH 04/15] ujson: writer: Fix open() return value check Andrea Cervesato
` (11 subsequent siblings)
14 siblings, 2 replies; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
When read() returns 0 before reaching the size reported by lseek()
(e.g. sysfs files or truncated files), off does not advance and the
loop spins indefinitely. Treat read() == 0 as EOF by breaking the loop
and updating the buffer length and terminator.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index 9f86f25b7..2219c5801 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -1032,9 +1032,15 @@ ujson_reader *ujson_reader_load(const char *path)
goto err1;
}
+ if (res == 0)
+ break;
+
off += res;
}
+ ret->buf[off] = 0;
+ ret->len = off;
+
close(fd);
return ret;
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 04/15] ujson: writer: Fix open() return value check
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (2 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-05 15:15 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 05/15] ujson: reader: Combine UTF-16 surrogate pairs Andrea Cervesato
` (10 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
open() returns -1 on failure and a non-negative file descriptor on
success (including 0). Checking !writer_file->fd wrongly rejects fd 0
as an error and accepts -1 on open failure. Check for negative fd
instead.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index b5da494d1..040ae680e 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -475,7 +475,7 @@ ujson_writer *ujson_writer_file_open(const char *path)
writer_file = (void*)ret + sizeof(ujson_writer);
writer_file->fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0664);
- if (!writer_file->fd) {
+ if (writer_file->fd < 0) {
free(ret);
return NULL;
}
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 05/15] ujson: reader: Combine UTF-16 surrogate pairs
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (3 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 04/15] ujson: writer: Fix open() return value check Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-05 15:36 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 06/15] ujson: reader: Check for signed integer overflow Andrea Cervesato
` (9 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
RFC 8259 specifies that characters outside the BMP may be represented
as a surrogate pair (\uD800-\uDBFF followed by \uDC00-\uDFFF).
Currently, each \uXXXX escape is converted independently to UTF-8,
producing invalid surrogate code point sequences instead of combining
them into a single UTF-8 sequence. Lone surrogates are also improperly
accepted. Combine valid surrogate pairs and reject unpaired surrogates.
Also return the UTF-8 length instead of the code point when no output
buffer is given. The caller treats 0 as failure, so \u0000 made
copy_str() fail without setting an error message.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index 2219c5801..0975ca1a1 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -134,8 +134,34 @@ static unsigned int parse_ucode_esc(ujson_reader *buf, char *str,
if (ucode < 0)
return 0;
+ if (ucode >= 0xd800 && ucode <= 0xdbff) {
+ int32_t low;
+
+ if (peekb(buf) != '\\' || peekb_off(buf, 1) != 'u') {
+ ujson_err(buf, "Unpaired high surrogate");
+ return 0;
+ }
+
+ getb(buf);
+ getb(buf);
+
+ low = parse_ucode_cp(buf);
+ if (low < 0)
+ return 0;
+
+ if (low < 0xdc00 || low > 0xdfff) {
+ ujson_err(buf, "Invalid low surrogate in escape sequence");
+ return 0;
+ }
+
+ ucode = 0x10000 + (((ucode - 0xd800) << 10) | (low - 0xdc00));
+ } else if (ucode >= 0xdc00 && ucode <= 0xdfff) {
+ ujson_err(buf, "Unpaired low surrogate");
+ return 0;
+ }
+
if (!str)
- return ucode;
+ return ujson_utf8_bytes(ucode);
if (ujson_utf8_bytes(ucode) + 1 >= len - off) {
ujson_err(buf, "String buffer too short!");
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 06/15] ujson: reader: Check for signed integer overflow
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (4 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 05/15] ujson: reader: Combine UTF-16 surrogate pairs Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 11:59 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 07/15] ujson: writer: Return error when newline() fails Andrea Cervesato
` (8 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
In get_int(), integers longer than LONG_MAX overflow long during
multiplication and addition, resulting in undefined behavior.
Accumulate the value in unsigned long, check against LONG_MAX or
LONG_MIN limits on each digit, and report an error on overflow.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index 0975ca1a1..272df4d55 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -11,6 +11,7 @@
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
+#include <limits.h>
#include "ujson_utf.h"
#include "ujson_reader.h"
@@ -309,7 +310,7 @@ static int is_digit(char b)
static int get_int(ujson_reader *buf, struct ujson_val *res)
{
- long val = 0;
+ unsigned long val = 0;
int sign = 1;
if (eatb(buf, '-')) {
@@ -325,17 +326,25 @@ static int get_int(ujson_reader *buf, struct ujson_val *res)
return 1;
}
+ unsigned long max = (sign < 0) ? (unsigned long)LONG_MAX + 1UL : (unsigned long)LONG_MAX;
+
while (is_digit(peekb(buf))) {
- val *= 10;
- val += getb(buf) - '0';
- //TODO: overflow?
+ unsigned int d = getb(buf) - '0';
+
+ if (val > (max - d) / 10) {
+ ujson_err(buf, "Integer overflow");
+ return 1;
+ }
+
+ val = val * 10 + d;
}
if (sign < 0)
- val = -val;
+ res->val_int = (long)(0UL - val);
+ else
+ res->val_int = (long)val;
- res->val_int = val;
- res->val_float = val;
+ res->val_float = res->val_int;
return 0;
}
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 07/15] ujson: writer: Return error when newline() fails
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (5 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 06/15] ujson: reader: Check for signed integer overflow Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 13:44 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 08/15] ujson: writer: Check return value in ESC_FLUSH Andrea Cervesato
` (7 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
When out_ch(self, '\n') fails, newline() returns 0 instead of 1,
masking output write errors from the caller. ujson_obj_finish() and
ujson_arr_finish() also ignore the newline() return value. Return 1
on write failure and propagate it to the caller.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index 040ae680e..c203ee079 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -179,7 +179,7 @@ static int do_padd(ujson_writer *self)
static int newline(ujson_writer *self)
{
if (out_ch(self, '\n'))
- return 0;
+ return 1;
if (do_padd(self))
return 1;
@@ -261,8 +261,8 @@ int ujson_obj_finish(ujson_writer *self)
clear_depth_bit(self);
- if (!first)
- newline(self);
+ if (!first && newline(self))
+ return 1;
return out_ch(self, '}');
}
@@ -304,8 +304,8 @@ int ujson_arr_finish(ujson_writer *self)
clear_depth_bit(self);
- if (!first)
- newline(self);
+ if (!first && newline(self))
+ return 1;
return out_ch(self, ']');
}
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 08/15] ujson: writer: Check return value in ESC_FLUSH
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (6 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 07/15] ujson: writer: Return error when newline() fails Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 14:01 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 09/15] ujson: reader: Do not increment depth on overflow Andrea Cervesato
` (6 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
ESC_FLUSH ignores the return values of out() and out_str().
If an output error occurs while flushing escaped characters, the error
is ignored and writing continues. Check the return values and return
immediately on failure.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index c203ee079..15faca9be 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -98,10 +98,12 @@ static inline int out_ch(ujson_writer *self, char ch)
}
#define ESC_FLUSH(esc_char) do {\
- out(self, val, i); \
+ if (out(self, val, i)) \
+ return 1; \
val += i + 1; \
i = 0; \
- out_str(self, esc_char); \
+ if (out_str(self, esc_char)) \
+ return 1; \
} while (0)
static inline int out_esc_str(ujson_writer *self, const char *val)
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 09/15] ujson: reader: Do not increment depth on overflow
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (7 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 08/15] ujson: writer: Check return value in ESC_FLUSH Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 14:15 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 10/15] ujson: reader: Parse escape sequences in object IDs Andrea Cervesato
` (5 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
any_first() increments buf->depth before verifying whether
depth > max_depth. If recursion overflows, depth is left skewed
beyond max_depth. Check bounds before incrementing.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index 272df4d55..b73a31b8f 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -811,13 +811,13 @@ static int any_first(ujson_reader *buf, char b)
return 1;
}
- buf->depth++;
-
- if (buf->depth > buf->max_depth) {
+ if (buf->depth >= buf->max_depth) {
ujson_err(buf, "Recursion too deep");
return 1;
}
+ buf->depth++;
+
return 0;
}
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 10/15] ujson: reader: Parse escape sequences in object IDs
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (8 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 09/15] ujson: reader: Do not increment depth on overflow Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 11/15] ujson: reader: Support files with zero lseek size Andrea Cervesato
` (4 subsequent siblings)
14 siblings, 0 replies; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
copy_id_str() copied characters verbatim without handling escape
sequences, which caused escaped quotes \" to prematurely terminate
the key and left other escape sequences such as \uXXXX unparsed.
Reuse copy_str() to parse object ID strings with full escape support.
As a side effect, too long and unterminated IDs now report the generic
copy_str() errors ("String buffer too short!", "Unterminated string")
instead of the ID-specific ones.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 24 +++---------------------
1 file changed, 3 insertions(+), 21 deletions(-)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index b73a31b8f..f4335407a 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -256,32 +256,14 @@ static int copy_str(ujson_reader *buf, char *str, size_t len)
static int copy_id_str(ujson_reader *buf, char *str, size_t len)
{
- size_t pos = 0;
-
if (eatws(buf))
goto err0;
- if (!eatb(buf, '"'))
+ if (peekb(buf) != '"')
goto err0;
- for (;;) {
- if (buf_empty(buf)) {
- ujson_err(buf, "Unterminated ID string");
- return 1;
- }
-
- if (eatb(buf, '"')) {
- str[pos] = 0;
- break;
- }
-
- if (pos >= len-1) {
- ujson_err(buf, "ID string too long");
- return 1;
- }
-
- str[pos++] = getb(buf);
- }
+ if (copy_str(buf, str, len))
+ return 1;
if (eatws(buf))
goto err1;
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 11/15] ujson: reader: Support files with zero lseek size
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (9 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 10/15] ujson: reader: Parse escape sequences in object IDs Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 15:21 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 12/15] ujson: reader: Reject \u0000 escape in strings Andrea Cervesato
` (3 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
Files in pseudo-filesystems like /proc/sys report a size of 0 with
lseek(), causing ujson_reader_load() to allocate a 1-byte buffer and
skip the read loop entirely. seq_file based files such as
/proc/self/status do not support SEEK_END at all, making the load fail.
Allocate an initial capacity when the size is unknown and grow the
buffer with realloc() dynamically until EOF.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index f4335407a..dd1d96d5f 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -1011,23 +1011,23 @@ ujson_reader *ujson_reader_load(const char *path)
int fd = open(path, O_RDONLY);
ujson_reader *ret;
ssize_t res;
- off_t len, off = 0;
+ off_t len;
+ size_t off = 0, capacity;
if (fd < 0)
return NULL;
+ /* Some pseudo-files (e.g. seq_file in /proc) do not support SEEK_END */
len = lseek(fd, 0, SEEK_END);
- if (len == (off_t)-1) {
- fprintf(stderr, "lseek() failed\n");
- goto err0;
- }
- if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+ if (len > 0 && lseek(fd, 0, SEEK_SET) == (off_t)-1) {
fprintf(stderr, "lseek() failed\n");
goto err0;
}
- ret = malloc(sizeof(ujson_reader) + len + 1);
+ capacity = len > 0 ? (size_t)len + 1 : 4096;
+
+ ret = malloc(sizeof(ujson_reader) + capacity + 1);
if (!ret) {
fprintf(stderr, "malloc() failed\n");
goto err0;
@@ -1035,15 +1035,24 @@ ujson_reader *ujson_reader_load(const char *path)
memset(ret, 0, sizeof(*ret));
- ret->buf[len] = 0;
- ret->len = len;
ret->max_depth = UJSON_RECURSION_MAX;
- ret->json = ret->buf;
ret->err_print = UJSON_ERR_PRINT;
ret->err_print_priv = UJSON_ERR_PRINT_PRIV;
- while (off < len) {
- res = read(fd, ret->buf + off, len - off);
+ for (;;) {
+ if (off >= capacity) {
+ ujson_reader *new_ret;
+
+ capacity *= 2;
+ new_ret = realloc(ret, sizeof(ujson_reader) + capacity + 1);
+ if (!new_ret) {
+ fprintf(stderr, "realloc() failed\n");
+ goto err1;
+ }
+ ret = new_ret;
+ }
+
+ res = read(fd, ret->buf + off, capacity - off);
if (res < 0) {
fprintf(stderr, "read() failed\n");
goto err1;
@@ -1057,6 +1066,7 @@ ujson_reader *ujson_reader_load(const char *path)
ret->buf[off] = 0;
ret->len = off;
+ ret->json = ret->buf;
close(fd);
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 12/15] ujson: reader: Reject \u0000 escape in strings
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (10 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 11/15] ujson: reader: Support files with zero lseek size Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 15:24 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 13/15] ujson: writer: Set errno when a flush fails Andrea Cervesato
` (2 subsequent siblings)
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
String values and object IDs are returned as NUL-terminated C strings.
A \u0000 escape was written as a NUL byte into the output buffer,
silently truncating the string. Report an error instead.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_reader.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
index dd1d96d5f..c8b8979ec 100644
--- a/libs/ujson/ujson_reader.c
+++ b/libs/ujson/ujson_reader.c
@@ -135,6 +135,12 @@ static unsigned int parse_ucode_esc(ujson_reader *buf, char *str,
if (ucode < 0)
return 0;
+ /* Strings are NUL-terminated, an embedded NUL would truncate them */
+ if (!ucode) {
+ ujson_err(buf, "\\u0000 is not supported");
+ return 0;
+ }
+
if (ucode >= 0xd800 && ucode <= 0xdbff) {
int32_t low;
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 13/15] ujson: writer: Set errno when a flush fails
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (11 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 12/15] ujson: reader: Reject \u0000 escape in strings Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 15:46 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 14/15] ujson: writer: Advance buffer on short write Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 15/15] ujson: writer: Flush buffer before direct writes Andrea Cervesato
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
ujson_writer_file_close() uses errno to decide whether flushing the
buffered data failed. out_writer_file_write() can fail without setting
errno, when write() returns 0 or more bytes than requested, so close
could report success even though data was lost. Set errno to EIO in
these cases.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index 15faca9be..49f73e436 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -399,11 +399,15 @@ static int out_writer_file_write(ujson_writer *self, int fd, const char *buf, ss
ssize_t ret = write(fd, buf, buf_len);
if (ret <= 0) {
err(self, "Failed to write to a file");
+ /* ujson_writer_file_close() relies on errno */
+ if (!ret)
+ errno = EIO;
return 1;
}
if (ret > buf_len) {
err(self, "Wrote more bytes than requested?!");
+ errno = EIO;
return 1;
}
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 14/15] ujson: writer: Advance buffer on short write
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (12 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 13/15] ujson: writer: Set errno when a flush fails Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 15:52 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 15/15] ujson: writer: Flush buffer before direct writes Andrea Cervesato
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
On a short write(), out_writer_file_write() decremented the remaining
length but kept writing from the start of the buffer, so the beginning
of the data was written again and the output file got corrupted.
Advance the buffer pointer by the number of bytes written.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index 49f73e436..2553d8cd2 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -411,6 +411,7 @@ static int out_writer_file_write(ujson_writer *self, int fd, const char *buf, ss
return 1;
}
+ buf += ret;
buf_len -= ret;
} while (buf_len);
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [LTP] [PATCH 15/15] ujson: writer: Flush buffer before direct writes
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
` (13 preceding siblings ...)
2026-10-05 13:25 ` [LTP] [PATCH 14/15] ujson: writer: Advance buffer on short write Andrea Cervesato
@ 2026-10-05 13:25 ` Andrea Cervesato
2026-10-09 16:05 ` Cyril Hrubis
14 siblings, 1 reply; 31+ messages in thread
From: Andrea Cervesato @ 2026-10-05 13:25 UTC (permalink / raw)
To: Linux Test Project
From: Andrea Cervesato <andrea.cervesato@suse.com>
out_writer_file() writes chunks larger than a quarter of its buffer
directly to the file, bypassing data still sitting in the buffer. Any
string longer than 256 bytes was therefore written before the preceding
output, producing corrupted JSON. Flush the buffer first.
Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
libs/ujson/ujson_writer.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index 2553d8cd2..df67b1f4d 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -424,8 +424,16 @@ static int out_writer_file(ujson_writer *self, const char *buf, size_t buf_len)
size_t buf_size = sizeof(writer_file->buf);
size_t buf_avail = buf_size - writer_file->buf_used;
- if (buf_len > buf_size/4)
+ if (buf_len > buf_size / 4) {
+ if (writer_file->buf_used &&
+ out_writer_file_write(self, writer_file->fd,
+ writer_file->buf, writer_file->buf_used))
+ return 1;
+
+ writer_file->buf_used = 0;
+
return out_writer_file_write(self, writer_file->fd, buf, buf_len);
+ }
if (buf_len >= buf_avail) {
if (out_writer_file_write(self, writer_file->fd,
--
2.51.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding
2026-10-05 13:25 ` [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding Andrea Cervesato
@ 2026-10-05 14:57 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-05 14:57 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
This has been fixed in ujson upstream actually:
https://github.com/metan-ucw/ujson/commit/cf30c1be12a06976d2c11ad2ccd3fd48ddf58e7d
I wonder if it would make more sense to route these into ujson library
first, then vendor fixed library into LTP. Or add it as a submodule.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 02/15] ujson: writer: Fix OOB read on invalid UTF-8
2026-10-05 13:25 ` [LTP] [PATCH 02/15] ujson: writer: Fix OOB read on invalid UTF-8 Andrea Cervesato
@ 2026-10-05 15:04 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-05 15:04 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> ujson_utf8_next_chsz() returns -1 on invalid UTF-8 sequences.
> Because i is unsigned (size_t), adding -1 moves i back by one byte.
> When i > 0, the loop steps back to the previous character and then
> forward onto the invalid byte again, looping forever. When i == 0
> (invalid byte at the start of the string or right after an escaped
> character), i wraps to SIZE_MAX, causing out-of-bounds reads in
> subsequent iterations and during output flushing. Stop and set an
> error when an invalid UTF-8 byte sequence is encountered.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_writer.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
> index c86a3b2af..b5da494d1 100644
> --- a/libs/ujson/ujson_writer.c
> +++ b/libs/ujson/ujson_writer.c
> @@ -114,6 +114,10 @@ static inline int out_esc_str(ujson_writer *self, const char *val)
>
> do {
> next_chsz = ujson_utf8_next_chsz(val, i);
> + if (next_chsz < 0) {
> + err(self, "Invalid UTF-8 sequence");
> + return 1;
> + }
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
We should really get the fix into the ujson library as well.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF
2026-10-05 13:25 ` [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF Andrea Cervesato
@ 2026-10-05 15:12 ` Cyril Hrubis
2026-10-05 18:06 ` [LTP] ujson: Fix 4-byte UTF-8 sequence decoding linuxtestproject.agent
1 sibling, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-05 15:12 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> When read() returns 0 before reaching the size reported by lseek()
> (e.g. sysfs files or truncated files), off does not advance and the
> loop spins indefinitely. Treat read() == 0 as EOF by breaking the loop
> and updating the buffer length and terminator.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_reader.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
> index 9f86f25b7..2219c5801 100644
> --- a/libs/ujson/ujson_reader.c
> +++ b/libs/ujson/ujson_reader.c
> @@ -1032,9 +1032,15 @@ ujson_reader *ujson_reader_load(const char *path)
> goto err1;
> }
>
> + if (res == 0)
> + break;
> +
> off += res;
> }
>
> + ret->buf[off] = 0;
> + ret->len = off;
> +
This is mostly theoretical, since it's unlikely that the file we are
reading has changed size during the loop that reads it.
I suppose that it would be safer to just fail the load call instead of
loading truncated file that would likely be rejected by the parser later
on anyways.
So maybe just:
diff --git a/ujson_reader.c b/ujson_reader.c
index f4ec607..0f34bc5 100644
--- a/ujson_reader.c
+++ b/ujson_reader.c
@@ -1032,6 +1032,11 @@ ujson_reader *ujson_reader_load(const char *path)
goto err1;
}
+ if (res == 0) {
+ fprintf(stderr, "read() returned 0 (file was truncated?)\n");
+ goto err1;
+ }
+
off += res;
}
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 04/15] ujson: writer: Fix open() return value check
2026-10-05 13:25 ` [LTP] [PATCH 04/15] ujson: writer: Fix open() return value check Andrea Cervesato
@ 2026-10-05 15:15 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-05 15:15 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> open() returns -1 on failure and a non-negative file descriptor on
> success (including 0). Checking !writer_file->fd wrongly rejects fd 0
> as an error and accepts -1 on open failure. Check for negative fd
> instead.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_writer.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
> index b5da494d1..040ae680e 100644
> --- a/libs/ujson/ujson_writer.c
> +++ b/libs/ujson/ujson_writer.c
> @@ -475,7 +475,7 @@ ujson_writer *ujson_writer_file_open(const char *path)
> writer_file = (void*)ret + sizeof(ujson_writer);
>
> writer_file->fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0664);
> - if (!writer_file->fd) {
> + if (writer_file->fd < 0) {
> free(ret);
> return NULL;
> }
This one is also very minor, since it's very unlikely that the fd 0
wouldn't be taken by stdin.
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 05/15] ujson: reader: Combine UTF-16 surrogate pairs
2026-10-05 13:25 ` [LTP] [PATCH 05/15] ujson: reader: Combine UTF-16 surrogate pairs Andrea Cervesato
@ 2026-10-05 15:36 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-05 15:36 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> RFC 8259 specifies that characters outside the BMP may be represented
> as a surrogate pair (\uD800-\uDBFF followed by \uDC00-\uDFFF).
> Currently, each \uXXXX escape is converted independently to UTF-8,
> producing invalid surrogate code point sequences instead of combining
> them into a single UTF-8 sequence. Lone surrogates are also improperly
> accepted. Combine valid surrogate pairs and reject unpaired surrogates.
>
> Also return the UTF-8 length instead of the code point when no output
> buffer is given. The caller treats 0 as failure, so \u0000 made
> copy_str() fail without setting an error message.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_reader.c | 28 +++++++++++++++++++++++++++-
> 1 file changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
> index 2219c5801..0975ca1a1 100644
> --- a/libs/ujson/ujson_reader.c
> +++ b/libs/ujson/ujson_reader.c
> @@ -134,8 +134,34 @@ static unsigned int parse_ucode_esc(ujson_reader *buf, char *str,
> if (ucode < 0)
> return 0;
>
> + if (ucode >= 0xd800 && ucode <= 0xdbff) {
> + int32_t low;
> +
> + if (peekb(buf) != '\\' || peekb_off(buf, 1) != 'u') {
> + ujson_err(buf, "Unpaired high surrogate");
> + return 0;
> + }
> +
> + getb(buf);
> + getb(buf);
> +
> + low = parse_ucode_cp(buf);
> + if (low < 0)
> + return 0;
> +
> + if (low < 0xdc00 || low > 0xdfff) {
> + ujson_err(buf, "Invalid low surrogate in escape sequence");
> + return 0;
> + }
> +
> + ucode = 0x10000 + (((ucode - 0xd800) << 10) | (low - 0xdc00));
> + } else if (ucode >= 0xdc00 && ucode <= 0xdfff) {
> + ujson_err(buf, "Unpaired low surrogate");
> + return 0;
> + }
I would put this code first, not in an elase branch.
if (ucode >= 0xdc00 && ucode <= 0xdfff) {
ujson_err(buf, "Unpaired low surrogate");
return 0;
}
if (ucode >= 0xd800 && ucode <= 0xdbff) {
...
}
Also it's unlikely that we will hit this in LTP, unless someone adds
emojis or hieroglyphs in test strings.
> if (!str)
> - return ucode;
> + return ujson_utf8_bytes(ucode);
This is a good bugfix, should have gone in a separate patch. Without
this we may overshoot the required size for the buffer by order of a
magnitude.
> if (ujson_utf8_bytes(ucode) + 1 >= len - off) {
> ujson_err(buf, "String buffer too short!");
>
> --
> 2.51.0
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] ujson: Fix 4-byte UTF-8 sequence decoding
2026-10-05 13:25 ` [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF Andrea Cervesato
2026-10-05 15:12 ` Cyril Hrubis
@ 2026-10-05 18:06 ` linuxtestproject.agent
1 sibling, 0 replies; 31+ messages in thread
From: linuxtestproject.agent @ 2026-10-05 18:06 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: ltp
Hi Andrea,
On 5 October 2026, Andrea Cervesato wrote:
> ujson: Fix 4-byte UTF-8 sequence decoding
--- [PATCH 10/15] ---
> diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
> + if (copy_str(buf, str, len))
> + return 1;
Medium: For {"first":1,"bad\q":2}, ujson_obj_next() returns zero but leaves
res->type as UJSON_INT, id as "badst", and the previous integer value.
UJSON_OBJ_FOREACH therefore executes its body again with that stale value.
Set res->type to UJSON_VOID on ID-parsing errors in both obj_next() and
obj_next_filter().
--- [PATCH 11/15] ---
> diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
> + capacity = len > 0 ? (size_t)len + 1 : 4096;
Medium: On 32-bit musl, where off_t is 64-bit, a 4 GiB - 1 byte file makes
capacity wrap to zero; doubling zero still yields zero, read(..., 0)
returns zero, and loading succeeds with an empty reader.
Reject lengths that cannot fit the allocation before narrowing to size_t,
and check the allocation additions and capacity doubling for overflow.
Verdict - Needs revision
Pre-existing issue exercised by patch 5:
> if (ujson_utf8_bytes(ucode) + 1 >= len - off) {
Medium: The new surrogate-pair path reaches this off-by-one check:
["\uD83D\uDE00"] fails with a five-byte output buffer even though the four
UTF-8 bytes and terminating NUL fit exactly.
Change >= to > so an exactly sufficient buffer is accepted.
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 06/15] ujson: reader: Check for signed integer overflow
2026-10-05 13:25 ` [LTP] [PATCH 06/15] ujson: reader: Check for signed integer overflow Andrea Cervesato
@ 2026-10-09 11:59 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 11:59 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> static int get_int(ujson_reader *buf, struct ujson_val *res)
> {
> - long val = 0;
> + unsigned long val = 0;
> int sign = 1;
>
> if (eatb(buf, '-')) {
> @@ -325,17 +326,25 @@ static int get_int(ujson_reader *buf, struct ujson_val *res)
> return 1;
> }
>
> + unsigned long max = (sign < 0) ? (unsigned long)LONG_MAX + 1UL : (unsigned long)LONG_MAX;
> +
> while (is_digit(peekb(buf))) {
> - val *= 10;
> - val += getb(buf) - '0';
> - //TODO: overflow?
> + unsigned int d = getb(buf) - '0';
> +
> + if (val > (max - d) / 10) {
> + ujson_err(buf, "Integer overflow");
> + return 1;
> + }
> +
> + val = val * 10 + d;
> }
>
> if (sign < 0)
> - val = -val;
> + res->val_int = (long)(0UL - val);
This would be better res->val_int = -(long)val;
And I do not like the code that much, but I think that we cannot do much
better without having separate functions for possitive and negative
numbers.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 07/15] ujson: writer: Return error when newline() fails
2026-10-05 13:25 ` [LTP] [PATCH 07/15] ujson: writer: Return error when newline() fails Andrea Cervesato
@ 2026-10-09 13:44 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 13:44 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 08/15] ujson: writer: Check return value in ESC_FLUSH
2026-10-05 13:25 ` [LTP] [PATCH 08/15] ujson: writer: Check return value in ESC_FLUSH Andrea Cervesato
@ 2026-10-09 14:01 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 14:01 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> ESC_FLUSH ignores the return values of out() and out_str().
> If an output error occurs while flushing escaped characters, the error
> is ignored and writing continues. Check the return values and return
> immediately on failure.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_writer.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
> index c203ee079..15faca9be 100644
> --- a/libs/ujson/ujson_writer.c
> +++ b/libs/ujson/ujson_writer.c
> @@ -98,10 +98,12 @@ static inline int out_ch(ujson_writer *self, char ch)
> }
>
> #define ESC_FLUSH(esc_char) do {\
> - out(self, val, i); \
> + if (out(self, val, i)) \
> + return 1; \
> val += i + 1; \
> i = 0; \
> - out_str(self, esc_char); \
> + if (out_str(self, esc_char)) \
> + return 1; \
> } while (0)
An return in a macro is a bit borderline.
Maybe it would have been cleaner to change the ESC_FLUSH() into a
funciton and return a proper return value that would be stored in the
switch instead.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 09/15] ujson: reader: Do not increment depth on overflow
2026-10-05 13:25 ` [LTP] [PATCH 09/15] ujson: reader: Do not increment depth on overflow Andrea Cervesato
@ 2026-10-09 14:15 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 14:15 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> any_first() increments buf->depth before verifying whether
> depth > max_depth. If recursion overflows, depth is left skewed
> beyond max_depth. Check bounds before incrementing.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_reader.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
> index 272df4d55..b73a31b8f 100644
> --- a/libs/ujson/ujson_reader.c
> +++ b/libs/ujson/ujson_reader.c
> @@ -811,13 +811,13 @@ static int any_first(ujson_reader *buf, char b)
> return 1;
> }
>
> - buf->depth++;
> -
> - if (buf->depth > buf->max_depth) {
> + if (buf->depth >= buf->max_depth) {
> ujson_err(buf, "Recursion too deep");
> return 1;
> }
>
> + buf->depth++;
> +
> return 0;
> }
I think that this is not a real problem, since once the error is set in
the buffer all subsequent calls to the library just return, since all of
them call check_err() at the start. That means that once the error is
set we will never call this function again.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 11/15] ujson: reader: Support files with zero lseek size
2026-10-05 13:25 ` [LTP] [PATCH 11/15] ujson: reader: Support files with zero lseek size Andrea Cervesato
@ 2026-10-09 15:21 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 15:21 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> Files in pseudo-filesystems like /proc/sys report a size of 0 with
> lseek(), causing ujson_reader_load() to allocate a 1-byte buffer and
> skip the read loop entirely. seq_file based files such as
> /proc/self/status do not support SEEK_END at all, making the load fail.
> Allocate an initial capacity when the size is unknown and grow the
> buffer with realloc() dynamically until EOF.
I would say that the proper fix here is to err out on lseek() returning
zero. I do not think that it make sense to support loading JSON from a
sysfs until we really need that.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 12/15] ujson: reader: Reject \u0000 escape in strings
2026-10-05 13:25 ` [LTP] [PATCH 12/15] ujson: reader: Reject \u0000 escape in strings Andrea Cervesato
@ 2026-10-09 15:24 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 15:24 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> String values and object IDs are returned as NUL-terminated C strings.
> A \u0000 escape was written as a NUL byte into the output buffer,
> silently truncating the string. Report an error instead.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_reader.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/libs/ujson/ujson_reader.c b/libs/ujson/ujson_reader.c
> index dd1d96d5f..c8b8979ec 100644
> --- a/libs/ujson/ujson_reader.c
> +++ b/libs/ujson/ujson_reader.c
> @@ -135,6 +135,12 @@ static unsigned int parse_ucode_esc(ujson_reader *buf, char *str,
> if (ucode < 0)
> return 0;
>
> + /* Strings are NUL-terminated, an embedded NUL would truncate them */
> + if (!ucode) {
> + ujson_err(buf, "\\u0000 is not supported");
> + return 0;
> + }
> +
> if (ucode >= 0xd800 && ucode <= 0xdbff) {
> int32_t low;
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 13/15] ujson: writer: Set errno when a flush fails
2026-10-05 13:25 ` [LTP] [PATCH 13/15] ujson: writer: Set errno when a flush fails Andrea Cervesato
@ 2026-10-09 15:46 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 15:46 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> ujson_writer_file_close() uses errno to decide whether flushing the
> buffered data failed. out_writer_file_write() can fail without setting
> errno, when write() returns 0 or more bytes than requested, so close
> could report success even though data was lost. Set errno to EIO in
> these cases.
>
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_writer.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
> index 15faca9be..49f73e436 100644
> --- a/libs/ujson/ujson_writer.c
> +++ b/libs/ujson/ujson_writer.c
> @@ -399,11 +399,15 @@ static int out_writer_file_write(ujson_writer *self, int fd, const char *buf, ss
> ssize_t ret = write(fd, buf, buf_len);
> if (ret <= 0) {
> err(self, "Failed to write to a file");
> + /* ujson_writer_file_close() relies on errno */
> + if (!ret)
> + errno = EIO;
This is very edge case, most likely we will get ret == 0 if we are
writing to a non-blocking socket or pipe, however the code in the
library does only work on files. Maybe it would have more sense to add a
separtate message for the ret == 0 case:
if (ret == 0) {
err(self, "The write() syscall returned 0");
errno = EAGAIN;
return 1;
}
> return 1;
> }
>
> if (ret > buf_len) {
> err(self, "Wrote more bytes than requested?!");
> + errno = EIO;
> return 1;
> }
This one looks good.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 14/15] ujson: writer: Advance buffer on short write
2026-10-05 13:25 ` [LTP] [PATCH 14/15] ujson: writer: Advance buffer on short write Andrea Cervesato
@ 2026-10-09 15:52 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 15:52 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
Reviewed-by: Cyril Hrubis <chrubis@suse.cz>
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [LTP] [PATCH 15/15] ujson: writer: Flush buffer before direct writes
2026-10-05 13:25 ` [LTP] [PATCH 15/15] ujson: writer: Flush buffer before direct writes Andrea Cervesato
@ 2026-10-09 16:05 ` Cyril Hrubis
0 siblings, 0 replies; 31+ messages in thread
From: Cyril Hrubis @ 2026-10-09 16:05 UTC (permalink / raw)
To: Andrea Cervesato; +Cc: Linux Test Project
Hi!
> Fixes: c50a0e0751c8 ("libs: Vendor ujson library")
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> libs/ujson/ujson_writer.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
> index 2553d8cd2..df67b1f4d 100644
> --- a/libs/ujson/ujson_writer.c
> +++ b/libs/ujson/ujson_writer.c
> @@ -424,8 +424,16 @@ static int out_writer_file(ujson_writer *self, const char *buf, size_t buf_len)
> size_t buf_size = sizeof(writer_file->buf);
> size_t buf_avail = buf_size - writer_file->buf_used;
>
> - if (buf_len > buf_size/4)
> + if (buf_len > buf_size / 4) {
> + if (writer_file->buf_used &&
> + out_writer_file_write(self, writer_file->fd,
> + writer_file->buf, writer_file->buf_used))
The check for buf_len == 0 should be inside the out_writer_fiel_write()
so that we do not have to handle it in each caller.
diff --git a/libs/ujson/ujson_writer.c b/libs/ujson/ujson_writer.c
index c86a3b2af..29a863287 100644
--- a/libs/ujson/ujson_writer.c
+++ b/libs/ujson/ujson_writer.c
@@ -389,6 +389,9 @@ struct json_writer_file {
static int out_writer_file_write(ujson_writer *self, int fd, const char *buf, ssize_t buf_len)
{
+ if (!buf_len)
+ return 0;
+
do {
> + return 1;
> +
> + writer_file->buf_used = 0;
> +
> return out_writer_file_write(self, writer_file->fd, buf, buf_len);
> + }
>
> if (buf_len >= buf_avail) {
> if (out_writer_file_write(self, writer_file->fd,
For example we need the check here.
--
Cyril Hrubis
chrubis@suse.cz
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 31+ messages in thread
end of thread, other threads:[~2026-10-09 16:05 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-05 13:25 [LTP] [PATCH 00/15] Fix ujson bugs Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 01/15] ujson: Fix 4-byte UTF-8 sequence decoding Andrea Cervesato
2026-10-05 14:57 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 02/15] ujson: writer: Fix OOB read on invalid UTF-8 Andrea Cervesato
2026-10-05 15:04 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 03/15] ujson: reader: Prevent infinite loop on EOF Andrea Cervesato
2026-10-05 15:12 ` Cyril Hrubis
2026-10-05 18:06 ` [LTP] ujson: Fix 4-byte UTF-8 sequence decoding linuxtestproject.agent
2026-10-05 13:25 ` [LTP] [PATCH 04/15] ujson: writer: Fix open() return value check Andrea Cervesato
2026-10-05 15:15 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 05/15] ujson: reader: Combine UTF-16 surrogate pairs Andrea Cervesato
2026-10-05 15:36 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 06/15] ujson: reader: Check for signed integer overflow Andrea Cervesato
2026-10-09 11:59 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 07/15] ujson: writer: Return error when newline() fails Andrea Cervesato
2026-10-09 13:44 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 08/15] ujson: writer: Check return value in ESC_FLUSH Andrea Cervesato
2026-10-09 14:01 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 09/15] ujson: reader: Do not increment depth on overflow Andrea Cervesato
2026-10-09 14:15 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 10/15] ujson: reader: Parse escape sequences in object IDs Andrea Cervesato
2026-10-05 13:25 ` [LTP] [PATCH 11/15] ujson: reader: Support files with zero lseek size Andrea Cervesato
2026-10-09 15:21 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 12/15] ujson: reader: Reject \u0000 escape in strings Andrea Cervesato
2026-10-09 15:24 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 13/15] ujson: writer: Set errno when a flush fails Andrea Cervesato
2026-10-09 15:46 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 14/15] ujson: writer: Advance buffer on short write Andrea Cervesato
2026-10-09 15:52 ` Cyril Hrubis
2026-10-05 13:25 ` [LTP] [PATCH 15/15] ujson: writer: Flush buffer before direct writes Andrea Cervesato
2026-10-09 16:05 ` Cyril Hrubis
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.