Move (some) XML_GetBuffer tests out of runtests.c

This commit is contained in:
Rhodri James 2022-10-28 20:00:24 +01:00
parent 4c28bb1681
commit bf11ca9025
4 changed files with 247 additions and 246 deletions

View file

@ -2549,6 +2549,214 @@ START_TEST(test_user_parameters) {
}
END_TEST
/* Test that an explicit external entity handler argument replaces
* the parser as the first argument.
*
* We do not call the first parameter to the external entity handler
* 'parser' for once, since the first time the handler is called it
* will actually be a text string. We need to be able to access the
* global 'parser' variable to create our external entity parser from,
* since there are code paths we need to ensure get executed.
*/
START_TEST(test_ext_entity_ref_parameter) {
const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
"<!DOCTYPE doc SYSTEM 'foo'>\n"
"<doc>&entity;</doc>";
XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
/* Set a handler arg that is not NULL and not parser (which is
* what NULL would cause to be passed.
*/
XML_SetExternalEntityRefHandlerArg(g_parser, (void *)text);
g_handler_data = (void *)text;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
/* Now try again with unset args */
XML_ParserReset(g_parser, NULL);
XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
XML_SetExternalEntityRefHandlerArg(g_parser, NULL);
g_handler_data = (void *)g_parser;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
}
END_TEST
/* Test the parsing of an empty string */
START_TEST(test_empty_parse) {
const char *text = "<doc></doc>";
const char *partial = "<doc>";
if (XML_Parse(g_parser, NULL, 0, XML_FALSE) == XML_STATUS_ERROR)
fail("Parsing empty string faulted");
if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
fail("Parsing final empty string not faulted");
if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_ELEMENTS)
fail("Parsing final empty string faulted for wrong reason");
/* Now try with valid text before the empty end */
XML_ParserReset(g_parser, NULL);
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
if (XML_Parse(g_parser, NULL, 0, XML_TRUE) == XML_STATUS_ERROR)
fail("Parsing final empty string faulted");
/* Now try with invalid text before the empty end */
XML_ParserReset(g_parser, NULL);
if (_XML_Parse_SINGLE_BYTES(g_parser, partial, (int)strlen(partial),
XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
fail("Parsing final incomplete empty string not faulted");
}
END_TEST
/* Test odd corners of the XML_GetBuffer interface */
static enum XML_Status
get_feature(enum XML_FeatureEnum feature_id, long *presult) {
const XML_Feature *feature = XML_GetFeatureList();
if (feature == NULL)
return XML_STATUS_ERROR;
for (; feature->feature != XML_FEATURE_END; feature++) {
if (feature->feature == feature_id) {
*presult = feature->value;
return XML_STATUS_OK;
}
}
return XML_STATUS_ERROR;
}
/* Test odd corners of the XML_GetBuffer interface */
START_TEST(test_get_buffer_1) {
const char *text = get_buffer_test_text;
void *buffer;
long context_bytes;
/* Attempt to allocate a negative length buffer */
if (XML_GetBuffer(g_parser, -12) != NULL)
fail("Negative length buffer not failed");
/* Now get a small buffer and extend it past valid length */
buffer = XML_GetBuffer(g_parser, 1536);
if (buffer == NULL)
fail("1.5K buffer failed");
assert(buffer != NULL);
memcpy(buffer, text, strlen(text));
if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
if (XML_GetBuffer(g_parser, INT_MAX) != NULL)
fail("INT_MAX buffer not failed");
/* Now try extending it a more reasonable but still too large
* amount. The allocator in XML_GetBuffer() doubles the buffer
* size until it exceeds the requested amount or INT_MAX. If it
* exceeds INT_MAX, it rejects the request, so we want a request
* between INT_MAX and INT_MAX/2. A gap of 1K seems comfortable,
* with an extra byte just to ensure that the request is off any
* boundary. The request will be inflated internally by
* XML_CONTEXT_BYTES (if defined), so we subtract that from our
* request.
*/
if (get_feature(XML_FEATURE_CONTEXT_BYTES, &context_bytes) != XML_STATUS_OK)
context_bytes = 0;
if (XML_GetBuffer(g_parser, INT_MAX - (context_bytes + 1025)) != NULL)
fail("INT_MAX- buffer not failed");
/* Now try extending it a carefully crafted amount */
if (XML_GetBuffer(g_parser, 1000) == NULL)
fail("1000 buffer failed");
}
END_TEST
/* Test more corners of the XML_GetBuffer interface */
START_TEST(test_get_buffer_2) {
const char *text = get_buffer_test_text;
void *buffer;
/* Now get a decent buffer */
buffer = XML_GetBuffer(g_parser, 1536);
if (buffer == NULL)
fail("1.5K buffer failed");
assert(buffer != NULL);
memcpy(buffer, text, strlen(text));
if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
/* Extend it, to catch a different code path */
if (XML_GetBuffer(g_parser, 1024) == NULL)
fail("1024 buffer failed");
}
END_TEST
/* Test for signed integer overflow CVE-2022-23852 */
#if defined(XML_CONTEXT_BYTES)
START_TEST(test_get_buffer_3_overflow) {
XML_Parser parser = XML_ParserCreate(NULL);
assert(parser != NULL);
const char *const text = "\n";
const int expectedKeepValue = (int)strlen(text);
// After this call, variable "keep" in XML_GetBuffer will
// have value expectedKeepValue
if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
== XML_STATUS_ERROR)
xml_failure(parser);
assert(expectedKeepValue > 0);
if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
fail("enlarging buffer not failed");
XML_ParserFree(parser);
}
END_TEST
#endif // defined(XML_CONTEXT_BYTES)
/* Test position information macros */
START_TEST(test_byte_info_at_end) {
const char *text = "<doc></doc>";
if (XML_GetCurrentByteIndex(g_parser) != -1
|| XML_GetCurrentByteCount(g_parser) != 0)
fail("Byte index/count incorrect at start of parse");
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
/* At end, the count will be zero and the index the end of string */
if (XML_GetCurrentByteCount(g_parser) != 0)
fail("Terminal byte count incorrect");
if (XML_GetCurrentByteIndex(g_parser) != (XML_Index)strlen(text))
fail("Terminal byte index incorrect");
}
END_TEST
/* Test position information from errors */
#define PRE_ERROR_STR "<doc></"
#define POST_ERROR_STR "wombat></doc>"
START_TEST(test_byte_info_at_error) {
const char *text = PRE_ERROR_STR POST_ERROR_STR;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_OK)
fail("Syntax error not faulted");
if (XML_GetCurrentByteCount(g_parser) != 0)
fail("Error byte count incorrect");
if (XML_GetCurrentByteIndex(g_parser) != strlen(PRE_ERROR_STR))
fail("Error byte index incorrect");
}
END_TEST
#undef PRE_ERROR_STR
#undef POST_ERROR_STR
TCase *
make_basic_test_case(Suite *s) {
TCase *tc_basic = tcase_create("basic tests");
@ -2662,6 +2870,15 @@ make_basic_test_case(Suite *s) {
tcase_add_test(tc_basic, test_ext_entity_trailing_rsqb);
tcase_add_test(tc_basic, test_ext_entity_good_cdata);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_user_parameters);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
tcase_add_test(tc_basic, test_empty_parse);
tcase_add_test(tc_basic, test_get_buffer_1);
tcase_add_test(tc_basic, test_get_buffer_2);
#if defined(XML_CONTEXT_BYTES)
tcase_add_test(tc_basic, test_get_buffer_3_overflow);
#endif
tcase_add_test(tc_basic, test_byte_info_at_end);
tcase_add_test(tc_basic, test_byte_info_at_error);
return tc_basic; /* TEMPORARY: this will become a void function */
}

View file

@ -100,6 +100,35 @@ const char *long_cdata_text
"012345678901234567890123456789012345678901234567890123456789"
"]]></s>";
/* Having an element name longer than 1024 characters exercises some
* of the pool allocation code in the parser that otherwise does not
* get executed. The count at the end of the line is the number of
* characters (bytes) in the element name by that point.x
*/
const char *get_buffer_test_text
= "<documentwitharidiculouslylongelementnametotease" /* 0x030 */
"aparticularcorneroftheallocationinXML_GetBuffers" /* 0x060 */
"othatwecanimprovethecoverageyetagain012345678901" /* 0x090 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x0c0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x0f0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x120 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x150 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x180 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x1b0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x1e0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x210 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x240 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x270 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x2a0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x2d0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x300 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x330 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x360 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x390 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x3c0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x3f0 */
"123456789abcdef0123456789abcdef0123456789>\n<ef0"; /* 0x420 */
/* Test control globals */
/* Used as the "resumable" parameter to XML_StopParser by some tests */

View file

@ -84,6 +84,7 @@ extern XML_Bool g_abortable;
extern const char *long_character_data_text;
extern const char *long_cdata_text;
extern const char *get_buffer_test_text;
extern void tcase_add_test__ifdef_xml_dtd(TCase *tc, tcase_test_function test);

View file

@ -74,243 +74,6 @@
XML_Parser g_parser = NULL;
/* Test that an explicit external entity handler argument replaces
* the parser as the first argument.
*
* We do not call the first parameter to the external entity handler
* 'parser' for once, since the first time the handler is called it
* will actually be a text string. We need to be able to access the
* global 'parser' variable to create our external entity parser from,
* since there are code paths we need to ensure get executed.
*/
START_TEST(test_ext_entity_ref_parameter) {
const char *text = "<?xml version='1.0' encoding='us-ascii'?>\n"
"<!DOCTYPE doc SYSTEM 'foo'>\n"
"<doc>&entity;</doc>";
XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
/* Set a handler arg that is not NULL and not parser (which is
* what NULL would cause to be passed.
*/
XML_SetExternalEntityRefHandlerArg(g_parser, (void *)text);
g_handler_data = (void *)text;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
/* Now try again with unset args */
XML_ParserReset(g_parser, NULL);
XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
XML_SetExternalEntityRefHandler(g_parser, external_entity_ref_param_checker);
XML_SetExternalEntityRefHandlerArg(g_parser, NULL);
g_handler_data = (void *)g_parser;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
}
END_TEST
/* Test the parsing of an empty string */
START_TEST(test_empty_parse) {
const char *text = "<doc></doc>";
const char *partial = "<doc>";
if (XML_Parse(g_parser, NULL, 0, XML_FALSE) == XML_STATUS_ERROR)
fail("Parsing empty string faulted");
if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
fail("Parsing final empty string not faulted");
if (XML_GetErrorCode(g_parser) != XML_ERROR_NO_ELEMENTS)
fail("Parsing final empty string faulted for wrong reason");
/* Now try with valid text before the empty end */
XML_ParserReset(g_parser, NULL);
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
if (XML_Parse(g_parser, NULL, 0, XML_TRUE) == XML_STATUS_ERROR)
fail("Parsing final empty string faulted");
/* Now try with invalid text before the empty end */
XML_ParserReset(g_parser, NULL);
if (_XML_Parse_SINGLE_BYTES(g_parser, partial, (int)strlen(partial),
XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
if (XML_Parse(g_parser, NULL, 0, XML_TRUE) != XML_STATUS_ERROR)
fail("Parsing final incomplete empty string not faulted");
}
END_TEST
/* Test odd corners of the XML_GetBuffer interface */
static enum XML_Status
get_feature(enum XML_FeatureEnum feature_id, long *presult) {
const XML_Feature *feature = XML_GetFeatureList();
if (feature == NULL)
return XML_STATUS_ERROR;
for (; feature->feature != XML_FEATURE_END; feature++) {
if (feature->feature == feature_id) {
*presult = feature->value;
return XML_STATUS_OK;
}
}
return XML_STATUS_ERROR;
}
/* Having an element name longer than 1024 characters exercises some
* of the pool allocation code in the parser that otherwise does not
* get executed. The count at the end of the line is the number of
* characters (bytes) in the element name by that point.x
*/
static const char *get_buffer_test_text
= "<documentwitharidiculouslylongelementnametotease" /* 0x030 */
"aparticularcorneroftheallocationinXML_GetBuffers" /* 0x060 */
"othatwecanimprovethecoverageyetagain012345678901" /* 0x090 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x0c0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x0f0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x120 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x150 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x180 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x1b0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x1e0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x210 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x240 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x270 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x2a0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x2d0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x300 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x330 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x360 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x390 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x3c0 */
"123456789abcdef0123456789abcdef0123456789abcdef0" /* 0x3f0 */
"123456789abcdef0123456789abcdef0123456789>\n<ef0"; /* 0x420 */
/* Test odd corners of the XML_GetBuffer interface */
START_TEST(test_get_buffer_1) {
const char *text = get_buffer_test_text;
void *buffer;
long context_bytes;
/* Attempt to allocate a negative length buffer */
if (XML_GetBuffer(g_parser, -12) != NULL)
fail("Negative length buffer not failed");
/* Now get a small buffer and extend it past valid length */
buffer = XML_GetBuffer(g_parser, 1536);
if (buffer == NULL)
fail("1.5K buffer failed");
assert(buffer != NULL);
memcpy(buffer, text, strlen(text));
if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
if (XML_GetBuffer(g_parser, INT_MAX) != NULL)
fail("INT_MAX buffer not failed");
/* Now try extending it a more reasonable but still too large
* amount. The allocator in XML_GetBuffer() doubles the buffer
* size until it exceeds the requested amount or INT_MAX. If it
* exceeds INT_MAX, it rejects the request, so we want a request
* between INT_MAX and INT_MAX/2. A gap of 1K seems comfortable,
* with an extra byte just to ensure that the request is off any
* boundary. The request will be inflated internally by
* XML_CONTEXT_BYTES (if defined), so we subtract that from our
* request.
*/
if (get_feature(XML_FEATURE_CONTEXT_BYTES, &context_bytes) != XML_STATUS_OK)
context_bytes = 0;
if (XML_GetBuffer(g_parser, INT_MAX - (context_bytes + 1025)) != NULL)
fail("INT_MAX- buffer not failed");
/* Now try extending it a carefully crafted amount */
if (XML_GetBuffer(g_parser, 1000) == NULL)
fail("1000 buffer failed");
}
END_TEST
/* Test more corners of the XML_GetBuffer interface */
START_TEST(test_get_buffer_2) {
const char *text = get_buffer_test_text;
void *buffer;
/* Now get a decent buffer */
buffer = XML_GetBuffer(g_parser, 1536);
if (buffer == NULL)
fail("1.5K buffer failed");
assert(buffer != NULL);
memcpy(buffer, text, strlen(text));
if (XML_ParseBuffer(g_parser, (int)strlen(text), XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
/* Extend it, to catch a different code path */
if (XML_GetBuffer(g_parser, 1024) == NULL)
fail("1024 buffer failed");
}
END_TEST
/* Test for signed integer overflow CVE-2022-23852 */
#if defined(XML_CONTEXT_BYTES)
START_TEST(test_get_buffer_3_overflow) {
XML_Parser parser = XML_ParserCreate(NULL);
assert(parser != NULL);
const char *const text = "\n";
const int expectedKeepValue = (int)strlen(text);
// After this call, variable "keep" in XML_GetBuffer will
// have value expectedKeepValue
if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
== XML_STATUS_ERROR)
xml_failure(parser);
assert(expectedKeepValue > 0);
if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
fail("enlarging buffer not failed");
XML_ParserFree(parser);
}
END_TEST
#endif // defined(XML_CONTEXT_BYTES)
/* Test position information macros */
START_TEST(test_byte_info_at_end) {
const char *text = "<doc></doc>";
if (XML_GetCurrentByteIndex(g_parser) != -1
|| XML_GetCurrentByteCount(g_parser) != 0)
fail("Byte index/count incorrect at start of parse");
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
/* At end, the count will be zero and the index the end of string */
if (XML_GetCurrentByteCount(g_parser) != 0)
fail("Terminal byte count incorrect");
if (XML_GetCurrentByteIndex(g_parser) != (XML_Index)strlen(text))
fail("Terminal byte index incorrect");
}
END_TEST
/* Test position information from errors */
#define PRE_ERROR_STR "<doc></"
#define POST_ERROR_STR "wombat></doc>"
START_TEST(test_byte_info_at_error) {
const char *text = PRE_ERROR_STR POST_ERROR_STR;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
== XML_STATUS_OK)
fail("Syntax error not faulted");
if (XML_GetCurrentByteCount(g_parser) != 0)
fail("Error byte count incorrect");
if (XML_GetCurrentByteIndex(g_parser) != strlen(PRE_ERROR_STR))
fail("Error byte index incorrect");
}
END_TEST
#undef PRE_ERROR_STR
#undef POST_ERROR_STR
/* Test position information in handler */
typedef struct ByteTestData {
int start_element_len;
@ -8394,15 +8157,6 @@ make_suite(void) {
TCase *tc_accounting = tcase_create("accounting tests");
#endif
tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_ref_parameter);
tcase_add_test(tc_basic, test_empty_parse);
tcase_add_test(tc_basic, test_get_buffer_1);
tcase_add_test(tc_basic, test_get_buffer_2);
#if defined(XML_CONTEXT_BYTES)
tcase_add_test(tc_basic, test_get_buffer_3_overflow);
#endif
tcase_add_test(tc_basic, test_byte_info_at_end);
tcase_add_test(tc_basic, test_byte_info_at_error);
tcase_add_test(tc_basic, test_byte_info_at_cdata);
tcase_add_test(tc_basic, test_predefined_entities);
tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_tag_in_dtd);