Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions parson.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,10 @@ static char * parson_strndup(const char *string, size_t n);
static char * parson_strdup(const char *string);
static int parson_sprintf(char * s, const char * format, ...);

static int hex_char_to_int(char c);
static JSON_Status parse_utf16_hex(const char *string, unsigned int *result);
static int num_bytes_in_utf8_sequence(unsigned char c);
static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len);
static int hex_char_to_int(char c);
static JSON_Status parse_utf16_hex(const char *string, unsigned int *result);
static int num_bytes_in_utf8_sequence(unsigned char c);
static JSON_Status verify_utf8_sequence(const unsigned char *string, size_t string_len, int *out_seq_len);
static parson_bool_t is_valid_utf8(const char *string, size_t string_len);
static parson_bool_t is_decimal(const char *string, size_t length);
static unsigned long hash_string(const char *string, size_t n);
Expand Down Expand Up @@ -347,20 +347,26 @@ static int num_bytes_in_utf8_sequence(unsigned char c) {
return 0; /* won't happen */
}

static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) {
static JSON_Status verify_utf8_sequence(const unsigned char *string, size_t string_len, int *out_seq_len) {
unsigned int cp = 0;
*len = num_bytes_in_utf8_sequence(string[0]);
int seq_len = -1;

if (*len == 1) {
*out_seq_len = -1;

seq_len = num_bytes_in_utf8_sequence(string[0]);

if ((size_t)seq_len > string_len) {
return JSONFailure;
} else if (seq_len == 1) {
cp = string[0];
} else if (*len == 2 && IS_CONT(string[1])) {
} else if (seq_len == 2 && IS_CONT(string[1])) {
cp = string[0] & 0x1F;
cp = (cp << 6) | (string[1] & 0x3F);
} else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
} else if (seq_len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
cp = ((unsigned char)string[0]) & 0xF;
cp = (cp << 6) | (string[1] & 0x3F);
cp = (cp << 6) | (string[2] & 0x3F);
} else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
} else if (seq_len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
cp = string[0] & 0x7;
cp = (cp << 6) | (string[1] & 0x3F);
cp = (cp << 6) | (string[2] & 0x3F);
Expand All @@ -370,9 +376,9 @@ static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) {
}

/* overlong encodings */
if ((cp < 0x80 && *len > 1) ||
(cp < 0x800 && *len > 2) ||
(cp < 0x10000 && *len > 3)) {
if ((cp < 0x80 && seq_len > 1) ||
(cp < 0x800 && seq_len > 2) ||
(cp < 0x10000 && seq_len > 3)) {
return JSONFailure;
}

Expand All @@ -386,17 +392,19 @@ static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) {
return JSONFailure;
}

*out_seq_len = seq_len;

return JSONSuccess;
}

static int is_valid_utf8(const char *string, size_t string_len) {
int len = 0;
static parson_bool_t is_valid_utf8(const char *string, size_t string_len) {
int seq_len = 0;
const char *string_end = string + string_len;
while (string < string_end) {
if (verify_utf8_sequence((const unsigned char*)string, &len) != JSONSuccess) {
if (verify_utf8_sequence((const unsigned char*)string, (size_t)(string_end - string), &seq_len) != JSONSuccess) {
return PARSON_FALSE;
}
string += len;
string += seq_len;
}
return PARSON_TRUE;
}
Expand Down
33 changes: 33 additions & 0 deletions tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void test_suite_8(void); /* Test serialization */
void test_suite_9(void); /* Test serialization (pretty) */
void test_suite_10(void); /* Testing for memory leaks */
void test_suite_11(void); /* Additional things that require testing */
void test_suite_12(void); /* Reading past string's end */
void test_memory_leaks(void);
void test_failing_allocations(void);
void test_custom_number_format(void);
Expand Down Expand Up @@ -135,6 +136,7 @@ int tests_main(int argc, char *argv[]) {
test_suite_9();
test_suite_10();
test_suite_11();
test_suite_12();
test_memory_leaks();
test_failing_allocations();
test_custom_number_format();
Expand Down Expand Up @@ -625,6 +627,37 @@ void test_suite_11(void) {
TEST(STREQ(array_with_escaped_slashes, serialized));
}

void test_suite_12(void) {
static const char buf_no_prefix[] = "\xf0\x9f\x98\x80";
static const char buf_4_byte[] = "abcd\xf0\x9f\x98\x80";
static const char buf_3_byte[] = "ab\xe2\x82\xac";
static const char buf_2_byte[] = "ab\xc2\xa9";
JSON_Value *value = NULL;

TEST(json_value_init_string_with_len(buf_no_prefix, 1) == NULL);
TEST(json_value_init_string_with_len(buf_no_prefix, 2) == NULL);
TEST(json_value_init_string_with_len(buf_no_prefix, 3) == NULL);
TEST(json_value_init_string_with_len(buf_4_byte, 5) == NULL);
TEST(json_value_init_string_with_len(buf_4_byte, 6) == NULL);
TEST(json_value_init_string_with_len(buf_4_byte, 7) == NULL);
TEST(json_value_init_string_with_len(buf_3_byte, 3) == NULL);
TEST(json_value_init_string_with_len(buf_3_byte, 4) == NULL);
TEST(json_value_init_string_with_len(buf_2_byte, 3) == NULL);

value = json_value_init_string_with_len(buf_no_prefix, 4);
TEST(value != NULL && json_value_get_string_len(value) == 4);
json_value_free(value);
value = json_value_init_string_with_len(buf_4_byte, 8);
TEST(value != NULL && json_value_get_string_len(value) == 8);
json_value_free(value);
value = json_value_init_string_with_len(buf_3_byte, 5);
TEST(value != NULL && json_value_get_string_len(value) == 5);
json_value_free(value);
value = json_value_init_string_with_len(buf_2_byte, 4);
TEST(value != NULL && json_value_get_string_len(value) == 4);
json_value_free(value);
}

void test_memory_leaks(void) {
g_malloc_count = 0;

Expand Down