Skip to content

Commit

Permalink
Add null terminator for char* based json string unescape, and precond…
Browse files Browse the repository at this point in the history
…ition on input length (#2303)

* Add null terminator for char* based json string unescape, and precondition on input length

* Add remark comment about precondition and null terimination

* Update doc comment typo

* Remove the test cases about insufficient space.

* Fix clang formatting.

* Add null terminator tests.

* Test that the null terminator is there at the end.

* Fix build errors.
  • Loading branch information
ahsonkhan committed Aug 10, 2022
1 parent 08fc01e commit e88c6b0
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 24 deletions.
4 changes: 4 additions & 0 deletions sdk/inc/azure/core/az_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,10 @@ AZ_NODISCARD az_result az_json_reader_skip_children(az_json_reader* ref_json_rea
* @retval #AZ_OK The string is returned.
* @retval #AZ_ERROR_NOT_ENOUGH_SPACE \p destination does not have enough size.
*
* @remarks The buffer referred to by \p destination must have a size that is at least 1 byte bigger
* than the \p json_string #az_span for the \p destination string to be zero-terminated.
* Content is copied from the source buffer, while unescaping and then `\0` is added at the end.
*
* @remarks This API can also be used to perform in place unescaping.
*/
AZ_NODISCARD az_result az_json_string_unescape(
Expand Down
5 changes: 4 additions & 1 deletion sdk/src/azure/core/az_json_token.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ AZ_NODISCARD az_result az_json_string_unescape(
{
_az_PRECONDITION_VALID_SPAN(json_string, 1, false);
_az_PRECONDITION_NOT_NULL(destination);
_az_PRECONDITION(destination_max_size > 0);
// The destination needs to be larger than the input, for null terminator.
_az_PRECONDITION(destination_max_size > az_span_size(json_string));

int32_t position = 0;
int32_t span_size = az_span_size(json_string);
Expand Down Expand Up @@ -370,6 +371,8 @@ AZ_NODISCARD az_result az_json_string_unescape(
position++;
}

destination[position] = 0;

if (out_string_length != NULL)
{
*out_string_length = position;
Expand Down
67 changes: 44 additions & 23 deletions sdk/tests/core/test_az_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -3029,6 +3029,31 @@ static void test_az_json_string_unescape(void** state)
assert_int_equal(AZ_ERROR_UNEXPECTED_END, result);
}

// null terminator
{
uint8_t buffer[3];
az_span test_span = AZ_SPAN_FROM_BUFFER(buffer);
test_span._internal.ptr[0] = 'a';
test_span._internal.ptr[1] = 'b';
test_span._internal.ptr[2] = 'c';

az_span expected = AZ_SPAN_FROM_STR("ab");

char destination[59] = { 0 };
destination[2] = 'd'; // verify that 'd' is overwritten with 0
az_span destination_span = AZ_SPAN_FROM_BUFFER(destination);

int final_size;
az_result result
= az_json_string_unescape(az_span_slice(test_span, 0, 2), destination, 59, &final_size);

destination_span = az_span_slice(destination_span, 0, final_size);

assert_int_equal(0, (int)destination[2]);
assert_int_equal(AZ_OK, result);
assert_true(az_span_is_content_equal(expected, destination_span));
}

// only escapes
{
az_span original = AZ_SPAN_FROM_STR("\\b\\f\\n\\r\\t\\\\");
Expand Down Expand Up @@ -3105,17 +3130,6 @@ static void test_az_json_string_unescape(void** state)
assert_true(az_span_is_content_equal(expected, destination_span));
}

// insufficient space
{
az_span json = AZ_SPAN_FROM_STR(" { \"name\": \"some value string\" , \"code\" : 123456 } ");
char destination[5] = { 0 };
int final_size;

az_result result = az_json_string_unescape(json, destination, 4, &final_size);

assert_int_equal(AZ_ERROR_NOT_ENOUGH_SPACE, result);
}

// magic test
{
az_span original = AZ_SPAN_FROM_STR("A\\\"\"Z");
Expand Down Expand Up @@ -3246,6 +3260,25 @@ static void test_az_json_string_unescape_same_buffer(void** state)
_az_span_free(&json);
}

// null terminator
{
uint8_t buffer[3];
az_span test_span = AZ_SPAN_FROM_BUFFER(buffer);
test_span._internal.ptr[0] = 'a';
test_span._internal.ptr[1] = 'b';
test_span._internal.ptr[2] = 'c'; // verify that 'c' is overwritten with 0

az_span expected = AZ_SPAN_FROM_STR("ab");

int final_size;
az_result result = az_json_string_unescape(
az_span_slice(test_span, 0, 2), (char*)az_span_ptr(test_span), 59, &final_size);

assert_int_equal(0, test_span._internal.ptr[2]);
assert_int_equal(AZ_OK, result);
assert_true(az_span_is_content_equal(expected, az_span_slice(test_span, 0, final_size)));
}

// only escapes
{
az_span original = az_span_create_from_str(strdup("\\b\\f\\n\\r\\t\\\\"));
Expand Down Expand Up @@ -3318,18 +3351,6 @@ static void test_az_json_string_unescape_same_buffer(void** state)
assert_int_equal(AZ_ERROR_UNEXPECTED_END, result);
_az_span_free(&original);
}

// insufficient space
{
az_span json = az_span_create_from_str(
strdup(" { \"name\": \"some value string\" , \"code\" : 123456 } "));
int final_size;

az_result result = az_json_string_unescape(json, (char*)az_span_ptr(json), 5, &final_size);

assert_int_equal(AZ_ERROR_NOT_ENOUGH_SPACE, result);
_az_span_free(&json);
}
}

int test_az_json()
Expand Down

0 comments on commit e88c6b0

Please sign in to comment.