mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-02 19:16:15 +00:00
func_json: Fix crashes for some types
This commit fixes crashes in JSON_DECODE() for types null, true, false and real numbers. In addition it ensures that a path is not deeper than 32 levels. Also allow root object to be an array. Add unit tests for above cases.
This commit is contained in:
committed by
asterisk-org-access-app[bot]
parent
d60c3c36e7
commit
8536ce8c38
@@ -101,6 +101,7 @@ static int parse_node(char **key, char *currentkey, char *nestchar, int count, s
|
|||||||
struct ast_json *jsonval = json;
|
struct ast_json *jsonval = json;
|
||||||
|
|
||||||
/* Prevent a huge JSON string from blowing the stack. */
|
/* Prevent a huge JSON string from blowing the stack. */
|
||||||
|
(*depth)++;
|
||||||
if (*depth > MAX_JSON_STACK) {
|
if (*depth > MAX_JSON_STACK) {
|
||||||
ast_log(LOG_WARNING, "Max JSON stack (%d) exceeded\n", MAX_JSON_STACK);
|
ast_log(LOG_WARNING, "Max JSON stack (%d) exceeded\n", MAX_JSON_STACK);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -115,6 +116,7 @@ static int parse_node(char **key, char *currentkey, char *nestchar, int count, s
|
|||||||
switch(ast_json_typeof(jsonval)) {
|
switch(ast_json_typeof(jsonval)) {
|
||||||
unsigned long int size;
|
unsigned long int size;
|
||||||
int r;
|
int r;
|
||||||
|
double d;
|
||||||
|
|
||||||
case AST_JSON_STRING:
|
case AST_JSON_STRING:
|
||||||
result = ast_json_string_get(jsonval);
|
result = ast_json_string_get(jsonval);
|
||||||
@@ -126,6 +128,11 @@ static int parse_node(char **key, char *currentkey, char *nestchar, int count, s
|
|||||||
ast_debug(1, "Got JSON integer: %d\n", r);
|
ast_debug(1, "Got JSON integer: %d\n", r);
|
||||||
snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
|
snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
|
||||||
break;
|
break;
|
||||||
|
case AST_JSON_REAL:
|
||||||
|
d = ast_json_real_get(jsonval);
|
||||||
|
ast_debug(1, "Got JSON real: %.17g\n", d);
|
||||||
|
snprintf(buf, len, "%.17g", d); /* the snprintf below is mutually exclusive with this one */
|
||||||
|
break;
|
||||||
case AST_JSON_ARRAY:
|
case AST_JSON_ARRAY:
|
||||||
ast_debug(1, "Got JSON array\n");
|
ast_debug(1, "Got JSON array\n");
|
||||||
previouskey = currentkey;
|
previouskey = currentkey;
|
||||||
@@ -148,41 +155,39 @@ static int parse_node(char **key, char *currentkey, char *nestchar, int count, s
|
|||||||
} else if (r >= size) {
|
} else if (r >= size) {
|
||||||
ast_debug(1, "Requested index '%d' does not exist in parsed array\n", r);
|
ast_debug(1, "Requested index '%d' does not exist in parsed array\n", r);
|
||||||
} else {
|
} else {
|
||||||
struct ast_json *json2 = ast_json_array_get(jsonval, r);
|
ast_debug(1, "Recursing on index %d in array\n", r);
|
||||||
if (!json2) {
|
if (parse_node(key, currentkey, nestchar, count, ast_json_array_get(jsonval, r), buf, len, depth)) { /* recurse on this node */
|
||||||
ast_debug(1, "Array index %d contains empty item\n", r);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
previouskey = currentkey;
|
|
||||||
currentkey = strsep(key, nestchar); /* get the next subkey */
|
|
||||||
ast_debug(1, "Recursing on index %d in array (key was '%s' and is now '%s')\n", r, previouskey, currentkey);
|
|
||||||
/* json2 is a borrowed ref. That's fine, since json won't get freed until recursing is over */
|
|
||||||
/* If there are keys remaining, then parse the next object we can get. Otherwise, just dump the child */
|
|
||||||
if (parse_node(key, currentkey, nestchar, count, currentkey ? ast_json_object_get(json2, currentkey) : json2, buf, len, depth)) { /* recurse on this node */
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case AST_JSON_TRUE:
|
||||||
|
case AST_JSON_FALSE:
|
||||||
|
r = ast_json_is_true(jsonval);
|
||||||
|
ast_debug(1, "Got JSON %s for key %s\n", r ? "true" : "false", currentkey);
|
||||||
|
snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
|
||||||
|
break;
|
||||||
|
case AST_JSON_NULL:
|
||||||
|
ast_debug(1, "Got JSON null for key %s\n", currentkey);
|
||||||
|
break;
|
||||||
case AST_JSON_OBJECT:
|
case AST_JSON_OBJECT:
|
||||||
default:
|
|
||||||
ast_debug(1, "Got generic JSON object for key %s\n", currentkey);
|
ast_debug(1, "Got generic JSON object for key %s\n", currentkey);
|
||||||
|
previouskey = currentkey;
|
||||||
|
currentkey = strsep(key, nestchar); /* retrieve the desired index */
|
||||||
if (!currentkey) { /* this is the end, so just dump the object */
|
if (!currentkey) { /* this is the end, so just dump the object */
|
||||||
char *result2 = ast_json_dump_string(jsonval);
|
char *result2 = ast_json_dump_string(jsonval);
|
||||||
ast_copy_string(buf, result2, len);
|
ast_copy_string(buf, result2, len);
|
||||||
ast_json_free(result2);
|
ast_json_free(result2);
|
||||||
} else {
|
} else {
|
||||||
previouskey = currentkey;
|
|
||||||
currentkey = strsep(key, nestchar); /* retrieve the desired index */
|
|
||||||
ast_debug(1, "Recursing on object (key was '%s' and is now '%s')\n", previouskey, currentkey);
|
ast_debug(1, "Recursing on object (key was '%s' and is now '%s')\n", previouskey, currentkey);
|
||||||
if (!currentkey) { /* this is the end, so just dump the object */
|
if (parse_node(key, currentkey, nestchar, count, ast_json_object_get(jsonval, currentkey), buf, len, depth)) { /* recurse on this node */
|
||||||
char *result2 = ast_json_dump_string(jsonval);
|
|
||||||
ast_copy_string(buf, result2, len);
|
|
||||||
ast_json_free(result2);
|
|
||||||
} else if (parse_node(key, currentkey, nestchar, count, ast_json_object_get(jsonval, currentkey), buf, len, depth)) { /* recurse on this node */
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
ast_log(LOG_WARNING, "Got unsuported type %d\n", ast_json_typeof(jsonval));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -191,9 +196,9 @@ static int json_decode_read(struct ast_channel *chan, const char *cmd, char *dat
|
|||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
struct ast_flags flags = {0};
|
struct ast_flags flags = {0};
|
||||||
struct ast_json *json = NULL;
|
struct ast_json *json = NULL, *start = NULL;
|
||||||
char *nestchar = "."; /* default delimeter for nesting key indexing is . */
|
char *nestchar = "."; /* default delimeter for nesting key indexing is . */
|
||||||
int res, depth = 0;
|
int index, res, depth = 0;
|
||||||
|
|
||||||
AST_DECLARE_APP_ARGS(args,
|
AST_DECLARE_APP_ARGS(args,
|
||||||
AST_APP_ARG(varname);
|
AST_APP_ARG(varname);
|
||||||
@@ -217,10 +222,12 @@ static int json_decode_read(struct ast_channel *chan, const char *cmd, char *dat
|
|||||||
ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
|
ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ast_strlen_zero(args.key)) {
|
if (ast_strlen_zero(args.key)) {
|
||||||
ast_log(LOG_WARNING, "%s requires a key\n", cmd);
|
ast_log(LOG_WARNING, "%s requires a key\n", cmd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = ast_strdupa(args.key);
|
key = ast_strdupa(args.key);
|
||||||
if (!ast_strlen_zero(args.nestchar)) {
|
if (!ast_strlen_zero(args.nestchar)) {
|
||||||
int seplen = strlen(args.nestchar);
|
int seplen = strlen(args.nestchar);
|
||||||
@@ -264,6 +271,7 @@ static int json_decode_read(struct ast_channel *chan, const char *cmd, char *dat
|
|||||||
ast_debug(1, "JSON node '%s', contains no data, nothing to search!\n", currentkey);
|
ast_debug(1, "JSON node '%s', contains no data, nothing to search!\n", currentkey);
|
||||||
return -1; /* empty json string */
|
return -1; /* empty json string */
|
||||||
}
|
}
|
||||||
|
|
||||||
json = ast_json_load_str(str, NULL);
|
json = ast_json_load_str(str, NULL);
|
||||||
if (!json) {
|
if (!json) {
|
||||||
ast_log(LOG_WARNING, "Failed to parse as JSON: %s\n", ast_str_buffer(str));
|
ast_log(LOG_WARNING, "Failed to parse as JSON: %s\n", ast_str_buffer(str));
|
||||||
@@ -272,7 +280,17 @@ static int json_decode_read(struct ast_channel *chan, const char *cmd, char *dat
|
|||||||
|
|
||||||
/* parse the JSON object, potentially recursively */
|
/* parse the JSON object, potentially recursively */
|
||||||
nextkey = strsep(&key, nestchar);
|
nextkey = strsep(&key, nestchar);
|
||||||
res = parse_node(&key, nextkey, nestchar, count, ast_json_object_get(json, firstkey), buf, len, &depth);
|
if (ast_json_is_object(json)) {
|
||||||
|
start = ast_json_object_get(json, firstkey);
|
||||||
|
} else {
|
||||||
|
if (ast_str_to_int(currentkey, &index)) {
|
||||||
|
ast_debug(1, "Requested index '%s' is not numeric or is invalid\n", currentkey);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
start = ast_json_array_get(json, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = parse_node(&key, nextkey, nestchar, count, start, buf, len, &depth);
|
||||||
ast_json_unref(json);
|
ast_json_unref(json);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -290,6 +308,17 @@ AST_TEST_DEFINE(test_JSON_DECODE)
|
|||||||
struct ast_str *str; /* fancy string for holding comparing value */
|
struct ast_str *str; /* fancy string for holding comparing value */
|
||||||
|
|
||||||
const char *test_strings[][6] = {
|
const char *test_strings[][6] = {
|
||||||
|
{"{\"myboolean\": true, \"state\": \"USA\"}", "", "myboolean", "1"},
|
||||||
|
{"{\"myboolean\": false, \"state\": \"USA\"}", "", "myboolean", "0"},
|
||||||
|
{"{\"myreal\": 1E+2, \"state\": \"USA\"}", "", "myreal", "100"},
|
||||||
|
{"{\"myreal\": 1.23, \"state\": \"USA\"}", "", "myreal", "1.23"},
|
||||||
|
{"{\"myarray\": [[1]], \"state\": \"USA\"}", "", "myarray.0.0", "1"},
|
||||||
|
{"{\"myarray\": [null], \"state\": \"USA\"}", "", "myarray.0", ""},
|
||||||
|
{"{\"myarray\": [0, 1], \"state\": \"USA\"}", "", "myarray", "[0,1]"},
|
||||||
|
{"[0, 1]", "", "", ""},
|
||||||
|
{"[0, 1]", "", "0", "0"},
|
||||||
|
{"[0, 1]", "", "foo", ""},
|
||||||
|
{"{\"mynull\": null, \"state\": \"USA\"}", "", "mynull", ""},
|
||||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "city", "Anytown"},
|
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "city", "Anytown"},
|
||||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "state", "USA"},
|
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "state", "USA"},
|
||||||
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "blah", ""},
|
{"{\"city\": \"Anytown\", \"state\": \"USA\"}", "", "blah", ""},
|
||||||
|
@@ -268,6 +268,22 @@ struct ast_json *ast_json_boolean(int value);
|
|||||||
*/
|
*/
|
||||||
struct ast_json *ast_json_null(void);
|
struct ast_json *ast_json_null(void);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Check if \a value is JSON array.
|
||||||
|
* \since 12.0.0
|
||||||
|
* \retval True (non-zero) if \a value == \ref ast_json_array().
|
||||||
|
* \retval False (zero) otherwise..
|
||||||
|
*/
|
||||||
|
int ast_json_is_array(const struct ast_json *value);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Check if \a value is JSON object.
|
||||||
|
* \since 12.0.0
|
||||||
|
* \retval True (non-zero) if \a value == \ref ast_json_object().
|
||||||
|
* \retval False (zero) otherwise..
|
||||||
|
*/
|
||||||
|
int ast_json_is_object(const struct ast_json *value);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Check if \a value is JSON true.
|
* \brief Check if \a value is JSON true.
|
||||||
* \since 12.0.0
|
* \since 12.0.0
|
||||||
|
10
main/json.c
10
main/json.c
@@ -250,6 +250,16 @@ struct ast_json *ast_json_null(void)
|
|||||||
return (struct ast_json *)json_null();
|
return (struct ast_json *)json_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ast_json_is_array(const struct ast_json *json)
|
||||||
|
{
|
||||||
|
return json_is_array((const json_t *)json);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ast_json_is_object(const struct ast_json *json)
|
||||||
|
{
|
||||||
|
return json_is_object((const json_t *)json);
|
||||||
|
}
|
||||||
|
|
||||||
int ast_json_is_true(const struct ast_json *json)
|
int ast_json_is_true(const struct ast_json *json)
|
||||||
{
|
{
|
||||||
return json_is_true((const json_t *)json);
|
return json_is_true((const json_t *)json);
|
||||||
|
Reference in New Issue
Block a user