summaryrefslogtreecommitdiffstats
path: root/src/jsmn.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-01-21 12:24:52 +0100
committerGravatar Nikias Bassen2026-01-21 12:26:13 +0100
commitc0f9df912d2a4001e56321fb53615e6474b32232 (patch)
treece3d46fa9ac9e173d2f86451037d1456205c067f /src/jsmn.c
parentc18d6b323e8121c041e8b88d2ea6b6e85ca41274 (diff)
downloadlibplist-c0f9df912d2a4001e56321fb53615e6474b32232.tar.gz
libplist-c0f9df912d2a4001e56321fb53615e6474b32232.tar.bz2
jsmn: use size_t for token offsets and harden against overflow
Use size_t for token start/end offsets instead of int, replace the -1 sentinel with SIZE_MAX, and add a defensive guard against offset wraparound. This prevents overflow when parsing very large JSON inputs. This addresses issue #282. Credit to @ylwango613 for repporting.
Diffstat (limited to 'src/jsmn.c')
-rw-r--r--src/jsmn.c43
1 files changed, 30 insertions, 13 deletions
diff --git a/src/jsmn.c b/src/jsmn.c
index f190312..889b8d7 100644
--- a/src/jsmn.c
+++ b/src/jsmn.c
@@ -3,6 +3,8 @@
* Simple JSON parser
*
* Copyright (c) 2010 Serge A. Zaitsev
+ * Updated to use size_t for token offsets and harden against overflows.
+ * (Nikias Bassen, January 2026)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,20 +26,25 @@
*/
#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <assert.h>
#include "jsmn.h"
+#define JSMN_POS_INVALID ((size_t)SIZE_MAX)
+
/**
* Allocates a fresh unused token from the token pull.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
- jsmntok_t *tokens, int num_tokens) {
+ jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *tok;
- if (parser->toknext >= num_tokens) {
+ if ((unsigned int)parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
- tok->start = tok->end = -1;
+ tok->start = tok->end = JSMN_POS_INVALID;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
@@ -49,7 +56,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
- int start, int end) {
+ size_t start, size_t end) {
token->type = type;
token->start = start;
token->end = end;
@@ -60,9 +67,9 @@ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
* Fills next available token with JSON primitive.
*/
static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
- jsmntok_t *tokens, int num_tokens) {
+ jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *token;
- int start;
+ size_t start;
start = parser->pos;
@@ -107,10 +114,10 @@ found:
* Fills next token with JSON string.
*/
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
- jsmntok_t *tokens, int num_tokens) {
+ jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *token;
- int start = parser->pos;
+ size_t start = parser->pos;
parser->pos++;
@@ -162,7 +169,7 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
/**
* Parse JSON string and fill tokens.
*/
-jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, jsmntok_t *tokens,
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t length, jsmntok_t *tokens,
unsigned int num_tokens) {
jsmnerr_t r;
int i;
@@ -170,6 +177,13 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
parser->end = length;
+ if (num_tokens >= INT_MAX) {
+ return JSMN_ERROR_LIMIT;
+ }
+ if (length > SIZE_MAX / 2) {
+ return JSMN_ERROR_LIMIT;
+ }
+
for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) {
char c;
jsmntype_t type;
@@ -198,10 +212,13 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
}
token = &tokens[parser->toknext - 1];
for (;;) {
- if (token->start != -1 && token->end == -1) {
+ if (token->start != JSMN_POS_INVALID && token->end == JSMN_POS_INVALID) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
+ if (parser->pos == SIZE_MAX) {
+ return JSMN_ERROR_INVAL;
+ }
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
@@ -214,7 +231,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
- if (token->start != -1 && token->end == -1) {
+ if (token->start != JSMN_POS_INVALID && token->end == JSMN_POS_INVALID) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
@@ -227,7 +244,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
if (i == -1) return JSMN_ERROR_INVAL;
for (; i >= 0; i--) {
token = &tokens[i];
- if (token->start != -1 && token->end == -1) {
+ if (token->start != JSMN_POS_INVALID && token->end == JSMN_POS_INVALID) {
parser->toksuper = i;
break;
}
@@ -268,7 +285,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, j
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
- if (tokens[i].start != -1 && tokens[i].end == -1) {
+ if (tokens[i].start != JSMN_POS_INVALID && tokens[i].end == JSMN_POS_INVALID) {
return JSMN_ERROR_PART;
}
}