summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/jplist.c23
-rw-r--r--src/jsmn.c43
-rw-r--r--src/jsmn.h18
-rw-r--r--src/plist.c21
4 files changed, 77 insertions, 28 deletions
diff --git a/src/jplist.c b/src/jplist.c
index 9a40844..2c88756 100644
--- a/src/jplist.c
+++ b/src/jplist.c
@@ -719,8 +719,8 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint3
return NULL;
}
plist_t arr = plist_new_array();
- int num_tokens = ti->tokens[*index].size;
- int num;
+ size_t num_tokens = ti->tokens[*index].size;
+ size_t num;
int j = (*index)+1;
for (num = 0; num < num_tokens; num++) {
if (j >= ti->count) {
@@ -770,8 +770,8 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint
ti->err = PLIST_ERR_MAX_NESTING;
return NULL;
}
- int num_tokens = ti->tokens[*index].size;
- int num;
+ size_t num_tokens = ti->tokens[*index].size;
+ size_t num;
int j = (*index)+1;
if (num_tokens % 2 != 0) {
PLIST_JSON_ERR("%s: number of children must be even\n", __func__);
@@ -844,14 +844,15 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
jsmn_parser parser;
jsmn_init(&parser);
- int maxtoks = 256;
- int curtoks = 0;
+ unsigned int maxtoks = 256;
+ unsigned int curtoks = 0;
int r = 0;
jsmntok_t *tokens = NULL;
do {
jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks);
if (!newtokens) {
+ free(tokens);
PLIST_JSON_ERR("%s: Out of memory\n", __func__);
return PLIST_ERR_NO_MEM;
}
@@ -861,8 +862,14 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
r = jsmn_parse(&parser, json, length, tokens, maxtoks);
if (r == JSMN_ERROR_NOMEM) {
+ if (maxtoks > (unsigned int)INT_MAX - 16) {
+ free(tokens);
+ return PLIST_ERR_NO_MEM;
+ }
maxtoks+=16;
continue;
+ } else if (r < 0) {
+ break;
}
} while (r == JSMN_ERROR_NOMEM);
@@ -879,6 +886,10 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__);
free(tokens);
return PLIST_ERR_PARSE;
+ case JSMN_ERROR_LIMIT:
+ PLIST_JSON_ERR("%s: Input data too large\n", __func__);
+ free(tokens);
+ return PLIST_ERR_PARSE;
default:
break;
}
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;
}
}
diff --git a/src/jsmn.h b/src/jsmn.h
index 380744d..629a0dd 100644
--- a/src/jsmn.h
+++ b/src/jsmn.h
@@ -3,6 +3,8 @@
* Simple JSON parser (header file)
*
* 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
@@ -25,6 +27,8 @@
#ifndef __JSMN_H_
#define __JSMN_H_
+#include <stddef.h>
+
/**
* JSON type identifier. Basic types are:
* o Object
@@ -46,6 +50,8 @@ typedef enum {
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3,
+ /* Input exceeds implementation-defined limits */
+ JSMN_ERROR_LIMIT = -4,
/* Everything was fine */
JSMN_SUCCESS = 0
} jsmnerr_t;
@@ -58,9 +64,9 @@ typedef enum {
*/
typedef struct {
jsmntype_t type;
- int start;
- int end;
- int size;
+ size_t start;
+ size_t end;
+ size_t size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
@@ -71,8 +77,8 @@ typedef struct {
* the string being parsed now and current position in that string
*/
typedef struct {
- unsigned int pos; /* offset in the JSON string */
- unsigned int end; /* offset after last character of JSON string */
+ size_t pos; /* offset in the JSON string */
+ size_t end; /* offset after last character of JSON string */
int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;
@@ -86,7 +92,7 @@ void jsmn_init(jsmn_parser *parser);
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing
* a single JSON object.
*/
-jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length,
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t length,
jsmntok_t *tokens, unsigned int num_tokens);
#endif /* __JSMN_H_ */
diff --git a/src/plist.c b/src/plist.c
index 9a488bb..6197e3d 100644
--- a/src/plist.c
+++ b/src/plist.c
@@ -581,12 +581,27 @@ static plist_t plist_copy_node(node_t node)
node_type = plist_get_node_type(node);
switch (node_type) {
case PLIST_DATA:
- newdata->buff = (uint8_t *) malloc(data->length);
- memcpy(newdata->buff, data->buff, data->length);
+ if (data->buff) {
+ newdata->buff = (uint8_t *) malloc(data->length);
+ assert(newdata->buff);
+ memcpy(newdata->buff, data->buff, data->length);
+ } else {
+ newdata->buff = NULL;
+ newdata->length = 0;
+ }
break;
case PLIST_KEY:
case PLIST_STRING:
- newdata->strval = strdup(data->strval);
+ if (data->strval) {
+ size_t n = strlen(data->strval) + 1;
+ newdata->strval = (char*)malloc(n);
+ assert(newdata->strval);
+ memcpy(newdata->strval, data->strval, n);
+ newdata->length = (uint64_t)n;
+ } else {
+ newdata->strval = NULL;
+ newdata->length = 0;
+ }
break;
case PLIST_ARRAY:
if (data->hashtable) {