Skip to content

Commit 0ddb8e4

Browse files
nandojvejhedberg
authored andcommitted
lib: json: Fix escape sequence unescaping during decoding
Fix the JSON library to properly unescape sequences during decoding. Previously, escape sequences like \n, \t, \" etc were validated but not converted back to their actual character representations during decoding, causing backslash duplication with each encode/decode cycle. This fix modifies decode_string_buf() to properly handle escape sequence unescaping and ensures strings maintain their original content across multiple encode/decode cycles. Signed-off-by: BUDKE Gerson Fernando <gerson.budke@leica-geosystems.com>
1 parent 70961e2 commit 0ddb8e4

File tree

1 file changed

+73
-5
lines changed

1 file changed

+73
-5
lines changed

lib/utils/json.c

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,17 +462,85 @@ static int skip_field(struct json_obj *obj, struct json_obj_key_value *kv)
462462
return 0;
463463
}
464464

465+
/**
466+
* @brief Unescape a JSON string, can work in-place if src == dst
467+
* @param src Source string with escape sequences
468+
* @param dst Destination buffer for unescaped string (can be same as src)
469+
* @param src_len Length of source string
470+
* @param dst_size Size of destination buffer
471+
* @return int Number of bytes written to destination, or -EINVAL on error
472+
*/
473+
static int json_unescape_string(const char *src, char *dst, size_t src_len, size_t dst_size)
474+
{
475+
const char *src_end = src + src_len;
476+
char *dst_start = dst;
477+
char *dst_end = dst_start + dst_size - 1; /* Point to last available byte (for null) */
478+
479+
while (src < src_end && dst < dst_end) {
480+
if (*src == '\\') {
481+
src++;
482+
if (src >= src_end) {
483+
return -EINVAL;
484+
}
485+
486+
switch (*src) {
487+
case '"':
488+
*dst++ = '"';
489+
break;
490+
case '\\':
491+
*dst++ = '\\';
492+
break;
493+
case '/':
494+
*dst++ = '/';
495+
break;
496+
case 'b':
497+
*dst++ = '\b';
498+
break;
499+
case 'f':
500+
*dst++ = '\f';
501+
break;
502+
case 'n':
503+
*dst++ = '\n';
504+
break;
505+
case 'r':
506+
*dst++ = '\r';
507+
break;
508+
case 't':
509+
*dst++ = '\t';
510+
break;
511+
default:
512+
/* Unknown escape sequence, copy as-is */
513+
*dst++ = '\\';
514+
*dst++ = *src;
515+
break;
516+
}
517+
src++;
518+
} else {
519+
*dst++ = *src++;
520+
}
521+
}
522+
523+
*dst = '\0';
524+
return dst - dst_start;
525+
}
526+
465527
static int decode_string_buf(const struct json_token *token, char *str, size_t size)
466528
{
467-
size_t len = token->end - token->start;
529+
size_t escaped_len = token->end - token->start;
530+
int ret;
468531

469-
/* buffer 'str' must be large enough to fit string and null-terminator */
470-
if (size <= len) {
532+
/* Safe approach: never copy more than (size - 1) bytes to leave room for null */
533+
size_t safe_len = (escaped_len < size) ? escaped_len : (size - 1);
534+
535+
ret = json_unescape_string(token->start, str, safe_len, size);
536+
if (ret < 0) {
471537
return -EINVAL;
472538
}
473539

474-
memcpy(str, token->start, len);
475-
str[len] = '\0';
540+
/* Check if we had to truncate due to the original escaped string being too long */
541+
if (escaped_len >= size) {
542+
return -EINVAL;
543+
}
476544

477545
return 0;
478546
}

0 commit comments

Comments
 (0)