Checking malloc errors, added limits on nesting and object/array size.

Also small refractoring and one new test regarding nesting.
This commit is contained in:
Krzysztof Gabis 2012-10-19 16:24:24 +02:00
parent e36fa2ab41
commit 5edab75bdc
2 changed files with 86 additions and 59 deletions

109
parson.c
View File

@ -27,6 +27,8 @@
#include "parson.h" #include "parson.h"
#define STARTING_CAPACITY 10 #define STARTING_CAPACITY 10
#define MAX_CAPACITY 10000
#define MAX_NESTING 19
#define sizeof_token(a) (sizeof(a) - 1) #define sizeof_token(a) (sizeof(a) - 1)
/* Type definitions */ /* Type definitions */
@ -58,22 +60,22 @@ struct json_array_t {
}; };
/* JSON Object */ /* JSON Object */
static JSON_Object * json_object_init(); static JSON_Object * json_object_init(void);
static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value); static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value);
static void json_object_free(JSON_Object *object); static void json_object_free(JSON_Object *object);
/* JSON Array */ /* JSON Array */
static JSON_Array * json_array_init(); static JSON_Array * json_array_init(void);
static void json_array_add(JSON_Array *array, JSON_Value *value); static int json_array_add(JSON_Array *array, JSON_Value *value);
static void json_array_free(JSON_Array *array); static void json_array_free(JSON_Array *array);
/* JSON Value */ /* JSON Value */
static JSON_Value * json_value_init_object(); static JSON_Value * json_value_init_object(void);
static JSON_Value * json_value_init_array(); static JSON_Value * json_value_init_array(void);
static JSON_Value * json_value_init_string(const char *string); static JSON_Value * json_value_init_string(const char *string);
static JSON_Value * json_value_init_number(double number); static JSON_Value * json_value_init_number(double number);
static JSON_Value * json_value_init_boolean(int boolean); static JSON_Value * json_value_init_boolean(int boolean);
static JSON_Value * json_value_init_null(); static JSON_Value * json_value_init_null(void);
/* Parser */ /* Parser */
char * parson_strndup(const char *string, size_t n); char * parson_strndup(const char *string, size_t n);
@ -83,28 +85,32 @@ static char * copy_and_remove_whitespaces(const char *string);
static int is_utf_string(const char *string); static int is_utf_string(const char *string);
static const char * parse_escaped_characters(const char *string); static const char * parse_escaped_characters(const char *string);
static const char * get_string(const char **string); static const char * get_string(const char **string);
static JSON_Value * parse_object_value(const char **string); static JSON_Value * parse_object_value(const char **string, size_t depth);
static JSON_Value * parse_array_value(const char **string); static JSON_Value * parse_array_value(const char **string, size_t depth);
static JSON_Value * parse_string_value(const char **string); static JSON_Value * parse_string_value(const char **string);
static JSON_Value * parse_boolean_value(const char **string); static JSON_Value * parse_boolean_value(const char **string);
static JSON_Value * parse_number_value(const char **string); static JSON_Value * parse_number_value(const char **string);
static JSON_Value * parse_null_value(const char **string); static JSON_Value * parse_null_value(const char **string);
static JSON_Value * parse_value(const char **string); static JSON_Value * parse_value(const char **string, size_t depth);
/* JSON Object */ /* JSON Object */
static JSON_Object * json_object_init() { static JSON_Object * json_object_init(void) {
JSON_Object *new_object = (JSON_Object*)malloc(sizeof(JSON_Object)); JSON_Object *new_obj = (JSON_Object*)malloc(sizeof(JSON_Object));
new_object->names = (const char**)malloc(sizeof(char*) * STARTING_CAPACITY); if (!new_obj) { return NULL; }
new_object->values = (JSON_Value**)malloc(sizeof(JSON_Value*) * STARTING_CAPACITY); new_obj->names = (const char**)malloc(sizeof(char*) * STARTING_CAPACITY);
new_object->capacity = STARTING_CAPACITY; if (!new_obj->names) { free(new_obj); return NULL; }
new_object->count = 0; new_obj->values = (JSON_Value**)malloc(sizeof(JSON_Value*) * STARTING_CAPACITY);
return new_object; if (!new_obj->names) { free(new_obj->names); free(new_obj); return NULL; }
new_obj->capacity = STARTING_CAPACITY;
new_obj->count = 0;
return new_obj;
} }
static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) {
size_t index; size_t index;
if (object->count >= object->capacity) { if (object->count >= object->capacity) {
size_t new_capacity = object->capacity * 2; size_t new_capacity = object->capacity * 2;
if (new_capacity > MAX_CAPACITY) { return 0; }
object->names = (const char**)realloc((void*)object->names, new_capacity * sizeof(char*)); object->names = (const char**)realloc((void*)object->names, new_capacity * sizeof(char*));
object->values = (JSON_Value**)realloc(object->values, new_capacity * sizeof(JSON_Value*)); object->values = (JSON_Value**)realloc(object->values, new_capacity * sizeof(JSON_Value*));
object->capacity = new_capacity; object->capacity = new_capacity;
@ -129,22 +135,26 @@ static void json_object_free(JSON_Object *object) {
} }
/* JSON Array */ /* JSON Array */
static JSON_Array * json_array_init() { static JSON_Array * json_array_init(void) {
JSON_Array *new_array = (JSON_Array*)malloc(sizeof(JSON_Array)); JSON_Array *new_array = (JSON_Array*)malloc(sizeof(JSON_Array));
if (!new_array) { return NULL; }
new_array->items = (JSON_Value**)malloc(STARTING_CAPACITY * sizeof(JSON_Value*)); new_array->items = (JSON_Value**)malloc(STARTING_CAPACITY * sizeof(JSON_Value*));
if (!new_array->items) { free(new_array); return NULL; }
new_array->capacity = STARTING_CAPACITY; new_array->capacity = STARTING_CAPACITY;
new_array->count = 0; new_array->count = 0;
return new_array; return new_array;
} }
static void json_array_add(JSON_Array *array, JSON_Value *value) { static int json_array_add(JSON_Array *array, JSON_Value *value) {
if (array->count >= array->capacity) { if (array->count >= array->capacity) {
size_t new_capacity = array->capacity * 2; size_t new_capacity = array->capacity * 2;
if (new_capacity > MAX_CAPACITY) { return 0; }
array->items = (JSON_Value**)realloc(array->items, new_capacity * sizeof(JSON_Value*)); array->items = (JSON_Value**)realloc(array->items, new_capacity * sizeof(JSON_Value*));
array->capacity = new_capacity; array->capacity = new_capacity;
} }
array->items[array->count] = value; array->items[array->count] = value;
array->count++; array->count++;
return 1;
} }
static void json_array_free(JSON_Array *array) { static void json_array_free(JSON_Array *array) {
@ -157,22 +167,27 @@ static void json_array_free(JSON_Array *array) {
} }
/* JSON Value */ /* JSON Value */
static JSON_Value * json_value_init_object() { static JSON_Value * json_value_init_object(void) {
JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value)); JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value));
if (!new_value) { return NULL; }
new_value->type = JSONObject; new_value->type = JSONObject;
new_value->value.object = json_object_init(); new_value->value.object = json_object_init();
if (!new_value->value.object) { free(new_value); return NULL; }
return new_value; return new_value;
} }
static JSON_Value * json_value_init_array() { static JSON_Value * json_value_init_array(void) {
JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value)); JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value));
if (!new_value) { return NULL; }
new_value->type = JSONArray; new_value->type = JSONArray;
new_value->value.array = json_array_init(); new_value->value.array = json_array_init();
if (!new_value->value.array) { free(new_value); return NULL; }
return new_value; return new_value;
} }
static JSON_Value * json_value_init_string(const char *string) { static JSON_Value * json_value_init_string(const char *string) {
JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value)); JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value));
if (!new_value) { return NULL; }
new_value->type = JSONString; new_value->type = JSONString;
new_value->value.string = string; new_value->value.string = string;
return new_value; return new_value;
@ -180,6 +195,7 @@ static JSON_Value * json_value_init_string(const char *string) {
static JSON_Value * json_value_init_number(double number) { static JSON_Value * json_value_init_number(double number) {
JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value)); JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value));
if (!new_value) { return NULL; }
new_value->type = JSONNumber; new_value->type = JSONNumber;
new_value->value.number = number; new_value->value.number = number;
return new_value; return new_value;
@ -187,20 +203,24 @@ static JSON_Value * json_value_init_number(double number) {
static JSON_Value * json_value_init_boolean(int boolean) { static JSON_Value * json_value_init_boolean(int boolean) {
JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value)); JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value));
if (!new_value) { return NULL; }
new_value->type = JSONBoolean; new_value->type = JSONBoolean;
new_value->value.boolean = boolean; new_value->value.boolean = boolean;
return new_value; return new_value;
} }
static JSON_Value * json_value_init_null() { static JSON_Value * json_value_init_null(void) {
JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value)); JSON_Value *new_value = (JSON_Value*)malloc(sizeof(JSON_Value));
if (!new_value) { return NULL; }
new_value->type = JSONNull; new_value->type = JSONNull;
return new_value; return new_value;
} }
/* Parser */ /* Parser */
char * parson_strndup(const char *string, size_t n) { char * parson_strndup(const char *string, size_t n) {
char *output_string = (char*)calloc(n + 1, 1); char *output_string = (char*)malloc(n + 1);
if (!output_string) { return NULL; }
output_string[n] = '\0';
strncpy(output_string, string, n); strncpy(output_string, string, n);
return output_string; return output_string;
} }
@ -224,7 +244,8 @@ static char *copy_and_remove_whitespaces(const char *string) {
char *output_string_ptr = output_string; char *output_string_ptr = output_string;
const char *string_ptr = string; const char *string_ptr = string;
const char *skipped_string = NULL; const char *skipped_string = NULL;
char current_char; char current_char;
if (!output_string) { return NULL; }
while (*string_ptr) { while (*string_ptr) {
current_char = *string_ptr; current_char = *string_ptr;
switch (current_char) { switch (current_char) {
@ -262,7 +283,8 @@ static const char * parse_escaped_characters(const char *string) {
char *output_string_ptr = output_string; char *output_string_ptr = output_string;
const char *string_ptr = string; const char *string_ptr = string;
char current_char; char current_char;
unsigned int utf_val; unsigned int utf_val;
if (!output_string) { return NULL; }
while (*string_ptr) { while (*string_ptr) {
current_char = *string_ptr; current_char = *string_ptr;
if (current_char == '\\') { if (current_char == '\\') {
@ -326,15 +348,15 @@ static const char * get_string(const char **string) {
return (const char*)parsed_string; return (const char*)parsed_string;
} }
static JSON_Value * parse_value(const char **string) { static JSON_Value * parse_value(const char **string, size_t depth) {
JSON_Value *output_value = NULL; JSON_Value *output_value = NULL;
if (*string == NULL) { return NULL; } if (*string == NULL || depth > MAX_NESTING) { return NULL; }
switch ((*string)[0]) { switch ((*string)[0]) {
case '{': case '{':
output_value = parse_object_value(string); output_value = parse_object_value(string, depth + 1);
break; break;
case '[': case '[':
output_value = parse_array_value(string); output_value = parse_array_value(string, depth + 1);
break; break;
case '\"': case '\"':
output_value = parse_string_value(string); output_value = parse_string_value(string);
@ -357,21 +379,22 @@ static JSON_Value * parse_value(const char **string) {
return output_value; return output_value;
} }
static JSON_Value * parse_object_value(const char **string) { static JSON_Value * parse_object_value(const char **string, size_t depth) {
JSON_Value *output_value = json_value_init_object(); JSON_Value *output_value = json_value_init_object();
const char *new_key = NULL; const char *new_key = NULL;
JSON_Value *new_value = NULL; JSON_Value *new_value = NULL;
if (!output_value) { return NULL; }
(*string)++; (*string)++;
if (**string == '}') { (*string)++; return output_value; } /* empty object */ if (**string == '}') { (*string)++; return output_value; } /* empty object */
while (**string != '\0') { while (**string != '\0') {
new_key = get_string(string); new_key = get_string(string);
if (new_key == NULL || **string != ':') { if (!new_key || **string != ':') {
json_value_free(output_value); json_value_free(output_value);
return NULL; return NULL;
} }
(*string)++; (*string)++;
new_value = parse_value(string); new_value = parse_value(string, depth);
if (new_value == NULL) { if (!new_value) {
free((void*)new_key); free((void*)new_key);
json_value_free(output_value); json_value_free(output_value);
return NULL; return NULL;
@ -391,21 +414,26 @@ static JSON_Value * parse_object_value(const char **string) {
return output_value; return output_value;
} }
static JSON_Value * parse_array_value(const char **string) { static JSON_Value * parse_array_value(const char **string, size_t depth) {
JSON_Value *output_value = json_value_init_array(); JSON_Value *output_value = json_value_init_array();
JSON_Value *new_array_value = NULL; JSON_Value *new_array_value = NULL;
if (!output_value) { return NULL; }
(*string)++; (*string)++;
if (**string == ']') { /* empty array */ if (**string == ']') { /* empty array */
(*string)++; (*string)++;
return output_value; return output_value;
} }
while (**string != '\0') { while (**string != '\0') {
new_array_value = parse_value(string); new_array_value = parse_value(string, depth);
if (new_array_value == NULL) { if (new_array_value == NULL) {
json_value_free(output_value); json_value_free(output_value);
return NULL; return NULL;
} }
json_array_add(json_value_get_array(output_value), new_array_value); if(!json_array_add(json_value_get_array(output_value), new_array_value)) {
free(new_array_value);
json_value_free(output_value);
return NULL;
}
if (**string != ',') { break; } if (**string != ',') { break; }
(*string)++; (*string)++;
} }
@ -455,11 +483,12 @@ JSON_Value * json_parse_file(const char *filename) {
size_t file_size; size_t file_size;
char *file_contents; char *file_contents;
JSON_Value *output_value; JSON_Value *output_value;
if (fp == NULL) { return NULL; } if (!fp) { return NULL; }
fseek(fp, 0L, SEEK_END); fseek(fp, 0L, SEEK_END);
file_size = ftell(fp); file_size = ftell(fp);
rewind(fp); rewind(fp);
file_contents = (char*)malloc(sizeof(char) * (file_size + 1)); file_contents = (char*)malloc(sizeof(char) * (file_size + 1));
if (!file_contents) { fclose(fp); return NULL; }
fread(file_contents, file_size, 1, fp); fread(file_contents, file_size, 1, fp);
fclose(fp); fclose(fp);
file_contents[file_size] = '\0'; file_contents[file_size] = '\0';
@ -472,9 +501,9 @@ JSON_Value * json_parse_string(const char *string) {
JSON_Value *output_value = NULL; JSON_Value *output_value = NULL;
const char *json_string = string ? copy_and_remove_whitespaces(string) : NULL; const char *json_string = string ? copy_and_remove_whitespaces(string) : NULL;
const char *json_string_ptr = json_string; const char *json_string_ptr = json_string;
if (json_string == NULL) { return NULL; } if (!json_string) { return NULL; }
if (*json_string == '{' || *json_string == '[') { if (*json_string == '{' || *json_string == '[') {
output_value = parse_value((const char**)&json_string_ptr); output_value = parse_value((const char**)&json_string_ptr, 0);
} }
free((void*)json_string); free((void*)json_string);
return output_value; return output_value;

36
tests.c
View File

@ -29,11 +29,11 @@
else{puts(" FAIL");tests_failed++;} else{puts(" FAIL");tests_failed++;}
#define STREQ(A, B) (A && B ? strcmp(A, B) == 0 : 0) #define STREQ(A, B) (A && B ? strcmp(A, B) == 0 : 0)
void test_suite_1(); void test_suite_1(void);
void test_suite_2(); void test_suite_2(void);
void test_suite_3(); void test_suite_3(void);
void print_commit_info(const char *username, const char * repo); void print_commit_info(const char *username, const char *repo);
static int tests_passed; static int tests_passed;
static int tests_failed; static int tests_failed;
@ -50,22 +50,18 @@ int main(int argc, const char * argv[]) {
} }
/* 3 test files from json.org */ /* 3 test files from json.org */
void test_suite_1() { void test_suite_1(void) {
int i; JSON_Value *val;
JSON_Value *root_value; TEST((val = json_parse_file("tests/test_1_1.txt")) != NULL);
char filename[128]; if (val) { json_value_free(val); }
for (i = 1; i <= 3; i++) { TEST((val = json_parse_file("tests/test_1_2.txt")) != NULL);
filename[0] = '\0'; if (val) { json_value_free(val); }
sprintf(filename, "tests/test_1_%d.txt", i); TEST((val = json_parse_file("tests/test_1_3.txt")) != NULL);
printf("Testing %s:\n", filename); if (val) { json_value_free(val); }
root_value = json_parse_file(filename);
TEST(root_value != NULL);
if (root_value != NULL) { json_value_free(root_value); }
}
} }
/* Testing correctness of parsed values */ /* Testing correctness of parsed values */
void test_suite_2() { void test_suite_2(void) {
JSON_Value *root_value; JSON_Value *root_value;
JSON_Object *object; JSON_Object *object;
JSON_Array *array; JSON_Array *array;
@ -138,7 +134,8 @@ void test_suite_2() {
} }
/* Testing values, on which parsing should fail */ /* Testing values, on which parsing should fail */
void test_suite_3() { void test_suite_3(void) {
const char nested_20x[] = "[[[[[[[[[[[[[[[[[[[[\"hi\"]]]]]]]]]]]]]]]]]]]]";
TEST(json_parse_string(NULL) == NULL); TEST(json_parse_string(NULL) == NULL);
TEST(json_parse_string("") == NULL); /* empty string */ TEST(json_parse_string("") == NULL); /* empty string */
TEST(json_parse_string("[\"lorem\",]") == NULL); TEST(json_parse_string("[\"lorem\",]") == NULL);
@ -167,9 +164,10 @@ void test_suite_3() {
TEST(json_parse_string("[\"\n\"]") == NULL); /* control character */ TEST(json_parse_string("[\"\n\"]") == NULL); /* control character */
TEST(json_parse_string("[\"\f\"]") == NULL); /* control character */ TEST(json_parse_string("[\"\f\"]") == NULL); /* control character */
TEST(json_parse_string("[\"\r\"]") == NULL); /* control character */ TEST(json_parse_string("[\"\r\"]") == NULL); /* control character */
TEST(json_parse_string(nested_20x) == NULL); /* too deep */
} }
void print_commit_info(const char *username, const char * repo) { void print_commit_info(const char *username, const char *repo) {
JSON_Value *root_value; JSON_Value *root_value;
JSON_Array *commits; JSON_Array *commits;
JSON_Object *commit; JSON_Object *commit;