diff --git a/src/CPE/cpedict_ext_priv.c b/src/CPE/cpedict_ext_priv.c index 8a9482fc26..85078616c2 100644 --- a/src/CPE/cpedict_ext_priv.c +++ b/src/CPE/cpedict_ext_priv.c @@ -179,7 +179,7 @@ static struct cpe_ext_deprecation *cpe_ext_deprecation_parse(xmlTextReaderPtr re deprecation->date = (char *) xmlTextReaderGetAttribute(reader, BAD_CAST ATTR_DATE_STR); if (xmlTextReaderIsEmptyElement(reader) == 0) { // the element contains child nodes xmlTextReaderNextNode(reader); - while (xmlStrcmp(xmlTextReaderConstLocalName(reader), BAD_CAST TAG_CPE_EXT_DEPRECATION_STR) != 0) { + while (xmlTextReaderConstLocalName(reader) != NULL && xmlStrcmp(xmlTextReaderConstLocalName(reader), BAD_CAST TAG_CPE_EXT_DEPRECATION_STR) != 0) { if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT) { xmlTextReaderNextNode(reader); continue; @@ -245,7 +245,7 @@ struct cpe23_item *cpe23_item_parse(xmlTextReaderPtr reader) item->name = (char *) xmlTextReaderGetAttribute(reader, BAD_CAST ATTR_NAME_STR); if (xmlTextReaderIsEmptyElement(reader) == 0) { // the element contains child nodes xmlTextReaderNextNode(reader); - while (xmlStrcmp(xmlTextReaderConstLocalName(reader), BAD_CAST TAG_CPE23_ITEM_STR) != 0) { + while (xmlTextReaderConstLocalName(reader) != NULL && xmlStrcmp(xmlTextReaderConstLocalName(reader), BAD_CAST TAG_CPE23_ITEM_STR) != 0) { if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT) { xmlTextReaderNextNode(reader); continue; diff --git a/src/CPE/cpedict_priv.c b/src/CPE/cpedict_priv.c index fbac34760e..116c4ba82c 100644 --- a/src/CPE/cpedict_priv.c +++ b/src/CPE/cpedict_priv.c @@ -617,7 +617,7 @@ struct cpe_generator *cpe_generator_parse(struct cpe_parser_ctx *ctx) // skip nodes until new element xmlTextReaderNextElement(reader); - while (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_GENERATOR_STR) != 0) { + while (xmlTextReaderConstLocalName(reader) != NULL && xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_GENERATOR_STR) != 0) { if ((xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_PRODUCT_NAME_STR) == 0) && @@ -644,9 +644,11 @@ struct cpe_generator *cpe_generator_parse(struct cpe_parser_ctx *ctx) "Unknown XML element in CPE dictionary generator, local name is '%s'.", xmlTextReaderConstLocalName(reader)); } - // element saved. Let's jump on the very next one node (not element, because we need to + // element saved. Let's jump on the very next one node (not element, because we need to // find XML_READER_TYPE_END_ELEMENT node, see "while" condition and the condition below "while" - xmlTextReaderNextNode(reader); + // Stop at end-of-document so a missing can't spin this loop. + if (xmlTextReaderNextNode(reader) != 1) + break; } } @@ -712,10 +714,13 @@ struct cpe_item *cpe_item_parse(struct cpe_parser_ctx *ctx) xmlTextReaderNextElementWE(reader, TAG_CPE_ITEM_STR); // Now it's time to go deaply to cpe-item element and parse it's children // Do while there is another cpe-item element. Then return. - while (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_CPE_ITEM_STR) != 0) { + while (xmlTextReaderConstLocalName(reader) != NULL && xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_CPE_ITEM_STR) != 0) { if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT) { - xmlTextReaderNextNode(reader); + // Break on end-of-document/error; xmlTextReaderRead() returns 0 + // at EOF without moving, which would otherwise spin this loop. + if (xmlTextReaderNextNode(reader) != 1) + break; continue; } @@ -836,7 +841,7 @@ static struct cpe_notes *cpe_notes_parse(xmlTextReaderPtr reader) notes->lang = (char *) xmlTextReaderXmlLang(reader); if (xmlTextReaderIsEmptyElement(reader) == 0) { // element contains child nodes xmlTextReaderNextNode(reader); - while (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_NOTES_STR) != 0) { + while (xmlTextReaderConstLocalName(reader) != NULL && xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_NOTES_STR) != 0) { if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT) { xmlTextReaderNextNode(reader); continue; @@ -882,7 +887,7 @@ struct cpe_vendor *cpe_vendor_parse(xmlTextReaderPtr reader) // jump to next element (which should be product) xmlTextReaderNextElement(reader); - while (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_VENDOR_STR) != 0) { + while (xmlTextReaderConstLocalName(reader) != NULL && xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_VENDOR_STR) != 0) { if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT) { xmlTextReaderNextNode(reader); @@ -922,22 +927,36 @@ struct cpe_vendor *cpe_vendor_parse(xmlTextReaderPtr reader) // initialization version = cpe_version_new(); version->value = (char *)xmlTextReaderGetAttribute(reader, ATTR_VALUE_STR); - oscap_list_add(product->versions, version); + // child elements may appear out of order / without their parent; + // skip an orphan rather than dereferencing a NULL parent. + if (product != NULL) + oscap_list_add(product->versions, version); + else + cpe_version_free(version); } else if (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_UPDATE_STR) == 0) { // initialization update = cpe_update_new(); update->value = (char *)xmlTextReaderGetAttribute(reader, ATTR_VALUE_STR); - oscap_list_add(version->updates, update); + if (version != NULL) + oscap_list_add(version->updates, update); + else + cpe_update_free(update); } else if (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_EDITION_STR) == 0) { // initialization edition = cpe_edition_new(); edition->value = (char *)xmlTextReaderGetAttribute(reader, ATTR_VALUE_STR); - oscap_list_add(update->editions, edition); + if (update != NULL) + oscap_list_add(update->editions, edition); + else + cpe_edition_free(edition); } else if (xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_LANGUAGE_STR) == 0) { // initialization language = cpe_language_new(); language->value = (char *)xmlTextReaderGetAttribute(reader, ATTR_VALUE_STR); - oscap_list_add(edition->languages, language); + if (edition != NULL) + oscap_list_add(edition->languages, language); + else + cpe_language_free(language); } else { oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unknown XML element withinin CPE vendor element, local name is '%s'.", xmlTextReaderConstLocalName(reader)); diff --git a/src/CPE/cpelang_priv.c b/src/CPE/cpelang_priv.c index 3ee217508d..dfda3d5e6d 100644 --- a/src/CPE/cpelang_priv.c +++ b/src/CPE/cpelang_priv.c @@ -303,8 +303,10 @@ struct cpe_platform *cpe_platform_parse(xmlTextReaderPtr reader) ret->expr = cpe_testexpr_parse(reader); } else if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) oscap_seterr(OSCAP_EFAMILY_OSCAP, "Unknown XML element in platform"); - // get the next node - xmlTextReaderNextNode(reader); + // get the next node; stop at end of document so a that is + // never closed cannot spin this loop forever. + if (xmlTextReaderNextNode(reader) != 1) + break; } return ret; } @@ -388,7 +390,12 @@ struct cpe_testexpr *cpe_testexpr_parse(xmlTextReaderPtr reader) // .. and the next node is logical-test element, we need recursive call if (!xmlStrcmp(xmlTextReaderConstLocalName(reader), TAG_LOGICAL_TEST_STR) && xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { + xmlNodePtr before = xmlTextReaderCurrentNode(reader); oscap_list_add(ret->meta.expr, cpe_testexpr_parse(reader)); + // A nested logical-test with e.g. an invalid operator returns + // without consuming its node; force progress so this loop can't spin. + if (xmlTextReaderCurrentNode(reader) == before) + xmlTextReaderNextNode(reader); if (xmlTextReaderDepth(reader) < depth) { return ret; } else if (xmlTextReaderDepth(reader) == depth) continue; diff --git a/src/DS/rds_index.c b/src/DS/rds_index.c index bb2986556b..8d20098082 100644 --- a/src/DS/rds_index.c +++ b/src/DS/rds_index.c @@ -101,7 +101,8 @@ struct rds_report_request_index* rds_index_get_report_request(struct rds_index* while (rds_report_request_index_iterator_has_more(it)) { struct rds_report_request_index* rr_index = rds_report_request_index_iterator_next(it); - if (strcmp(rds_report_request_index_get_id(rr_index), id) == 0) { + // ids may be missing (NULL) on malformed input; oscap_strcmp is NULL-safe. + if (oscap_strcmp(rds_report_request_index_get_id(rr_index), id) == 0) { ret = rr_index; break; } @@ -123,7 +124,7 @@ struct rds_asset_index* rds_index_get_asset(struct rds_index *rds, const char *i while (rds_asset_index_iterator_has_more(it)) { struct rds_asset_index *a_index = rds_asset_index_iterator_next(it); - if (strcmp(rds_asset_index_get_id(a_index), id) == 0) { + if (oscap_strcmp(rds_asset_index_get_id(a_index), id) == 0) { ret = a_index; break; } @@ -142,7 +143,7 @@ struct rds_report_index *rds_index_get_report(struct rds_index *rds, const char while (rds_report_index_iterator_has_more(it)) { struct rds_report_index *r_index = rds_report_index_iterator_next(it); - if (strcmp(rds_report_index_get_id(r_index), id) == 0) { + if (oscap_strcmp(rds_report_index_get_id(r_index), id) == 0) { ret = r_index; break; } @@ -200,20 +201,25 @@ static inline void _parse_relationships_node(struct rds_index *ret, xmlNodePtr r xmlChar *inner_ref = relationship_get_inner_ref(relationship_node); // We now only use arfvocab: but arfrel: is kept here for compatibility - if (oscap_str_startswith((const char *) type_attr, "arfvocab:") - || oscap_str_startswith((const char *) type_attr, "arfrel:")) { + if (type_attr != NULL + && (oscap_str_startswith((const char *) type_attr, "arfvocab:") + || oscap_str_startswith((const char *) type_attr, "arfrel:"))) { if (oscap_str_endswith((const char*)type_attr, ":isAbout")) { struct rds_asset_index* asset = rds_index_get_asset(ret, (const char*)inner_ref); struct rds_report_index* report = rds_index_get_report(ret, (const char*)subject_attr); - rds_asset_index_add_report_ref(asset, report); + // A relationship may reference ids that don't resolve to an asset/ + // report in this collection; skip it rather than dereferencing NULL. + if (asset != NULL && report != NULL) + rds_asset_index_add_report_ref(asset, report); } else if (oscap_str_endswith((const char*)type_attr, ":createdFor")) { struct rds_report_request_index *request = rds_index_get_report_request(ret, (const char*)inner_ref); struct rds_report_index *report = rds_index_get_report(ret, (const char*)subject_attr); // This is based on the assumption that every report has at most 1 request // it was "created for". - rds_report_index_set_request(report, request); + if (report != NULL) + rds_report_index_set_request(report, request); } else { dW("Unsupported core:relationship/@type='%s'", (const char *) type_attr); } @@ -268,6 +274,9 @@ struct rds_index *rds_index_parse(xmlTextReaderPtr reader) { if (strcmp((const char*)xmlTextReaderConstLocalName(reader), "report-request") != 0) { // TODO: warning? + // Must advance the reader, otherwise oscap_to_start_element() + // keeps returning this same node and the loop spins forever. + xmlTextReaderRead(reader); continue; } @@ -289,6 +298,9 @@ struct rds_index *rds_index_parse(xmlTextReaderPtr reader) { if (strcmp((const char*)xmlTextReaderConstLocalName(reader), "asset") != 0) { // TODO: warning? + // Must advance the reader, otherwise oscap_to_start_element() + // keeps returning this same node and the loop spins forever. + xmlTextReaderRead(reader); continue; } @@ -310,6 +322,9 @@ struct rds_index *rds_index_parse(xmlTextReaderPtr reader) { if (strcmp((const char*)xmlTextReaderConstLocalName(reader), "report") != 0) { // TODO: warning? + // Must advance the reader, otherwise oscap_to_start_element() + // keeps returning this same node and the loop spins forever. + xmlTextReaderRead(reader); continue; } @@ -355,13 +370,16 @@ int rds_index_select_report(struct rds_index *s, const char **report_id) { int ret = 1; + if (s == NULL) + return ret; + struct rds_report_index_iterator *reports_it = rds_index_get_reports(s); while (rds_report_index_iterator_has_more(reports_it)) { struct rds_report_index* report_idx = rds_report_index_iterator_next(reports_it); const char *report_idx_id = rds_report_index_get_id(report_idx); - if (!*report_id || strcmp(report_idx_id, *report_id) == 0) { + if (!*report_id || oscap_strcmp(report_idx_id, *report_id) == 0) { *report_id = report_idx_id; ret = 0; break; diff --git a/src/XCCDF/profile.c b/src/XCCDF/profile.c index a50b838aca..98392cccb0 100644 --- a/src/XCCDF/profile.c +++ b/src/XCCDF/profile.c @@ -223,9 +223,14 @@ const struct oscap_string_map XCCDF_ROLE_MAP[] = { static void xccdf_parse_remarks(xmlTextReaderPtr reader, struct oscap_list* list, int depth) { - while (oscap_to_start_element(reader, depth)) + while (oscap_to_start_element(reader, depth)) { if (xccdf_element_get(reader) == XCCDFE_REMARK) oscap_list_add(list, oscap_text_new_parse(XCCDF_TEXT_PLAIN, reader)); + else + // A non- child here would otherwise never be consumed, + // spinning oscap_to_start_element() forever; skip it to progress. + xmlTextReaderRead(reader); + } } struct xccdf_item *xccdf_profile_parse(xmlTextReaderPtr reader, struct xccdf_item *bench) @@ -241,6 +246,10 @@ struct xccdf_item *xccdf_profile_parse(xmlTextReaderPtr reader, struct xccdf_ite int depth = oscap_element_depth(reader) + 1; while (oscap_to_start_element(reader, depth)) { + // Guard against a child element that no case below consumes (which + // would spin oscap_to_start_element() on the same node forever): if an + // iteration leaves the reader on the same node, force it to advance. + xmlNodePtr _node_before = xmlTextReaderCurrentNode(reader); switch (xccdf_element_get(reader)) { case XCCDFE_SELECT:{ struct xccdf_select *sel = xccdf_select_new(); @@ -296,6 +305,8 @@ struct xccdf_item *xccdf_profile_parse(xmlTextReaderPtr reader, struct xccdf_ite xmlTextReaderConstLocalName(reader)); xmlTextReaderRead(reader); } + if (xmlTextReaderCurrentNode(reader) == _node_before) + xmlTextReaderRead(reader); } return prof;