Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-c-common for openSUSE:Factory checked in at 2026-07-21 22:59:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-common (Old) and /work/SRC/openSUSE:Factory/.aws-c-common.new.24530 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-c-common" Tue Jul 21 22:59:42 2026 rev:33 rq:1366706 version:0.14.2 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-common/aws-c-common.changes 2026-07-08 17:42:17.101488047 +0200 +++ /work/SRC/openSUSE:Factory/.aws-c-common.new.24530/aws-c-common.changes 2026-07-21 22:59:49.016518765 +0200 @@ -1,0 +2,9 @@ +Fri Jul 17 08:50:50 UTC 2026 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 0.14.2 + * CI improvements by @azkrishpy in (#1252) + * Byte Buf Helper Func Dynamic or Static by @sbSteveK in (#1253) + * Update deprecated OpenBSD CI job by @sfod in (#1255) + * XML Parser Fixes by @sbSteveK in (#1254) + +------------------------------------------------------------------- Old: ---- v0.14.1.tar.gz New: ---- v0.14.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-common.spec ++++++ --- /var/tmp/diff_new_pack.QHPZRL/_old 2026-07-21 22:59:50.256561022 +0200 +++ /var/tmp/diff_new_pack.QHPZRL/_new 2026-07-21 22:59:50.256561022 +0200 @@ -19,7 +19,7 @@ %define library_version 1.0.0 %define library_soversion 1 Name: aws-c-common -Version: 0.14.1 +Version: 0.14.2 Release: 0 Summary: Core C99 package for AWS SDK for C License: Apache-2.0 ++++++ v0.14.1.tar.gz -> v0.14.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/.github/workflows/ci.yml new/aws-c-common-0.14.2/.github/workflows/ci.yml --- old/aws-c-common-0.14.1/.github/workflows/ci.yml 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/.github/workflows/ci.yml 2026-07-09 18:53:07.000000000 +0200 @@ -6,7 +6,7 @@ - 'main' env: - BUILDER_VERSION: v0.9.92 + BUILDER_VERSION: v0.9.96 BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net BUILDER_SOURCE: releases PACKAGE_NAME: aws-c-common @@ -251,6 +251,11 @@ openbsd: runs-on: ubuntu-24.04 # unit tests hang on macos; use ubuntu instead + strategy: + fail-fast: false + matrix: + # OpenBSD only supports the two most recent releases + version: ['7.8', '7.9'] steps: - uses: aws-actions/configure-aws-credentials@v6 with: @@ -258,10 +263,10 @@ aws-region: ${{ env.AWS_DEFAULT_REGION }} - uses: actions/checkout@v6 - name: Build ${{ env.PACKAGE_NAME }} + consumers - uses: cross-platform-actions/[email protected] + uses: cross-platform-actions/[email protected] with: operating_system: openbsd - version: '7.5' + version: ${{ matrix.version }} shell: bash run: | sudo pkg_add py3-urllib3 @@ -280,7 +285,7 @@ with: submodules: true - name: Build ${{ env.PACKAGE_NAME }} + consumers - uses: cross-platform-actions/[email protected] + uses: cross-platform-actions/[email protected] with: operating_system: freebsd version: '14.1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/.github/workflows/issue-regression-labeler.yml new/aws-c-common-0.14.2/.github/workflows/issue-regression-labeler.yml --- old/aws-c-common-0.14.1/.github/workflows/issue-regression-labeler.yml 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/.github/workflows/issue-regression-labeler.yml 2026-07-09 18:53:07.000000000 +0200 @@ -24,9 +24,12 @@ - name: Manage regression label env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IS_REGRESSION: ${{ steps.check_regression.outputs.is_regression }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + REPO: ${{ github.repository }} run: | - if [ "${{ steps.check_regression.outputs.is_regression }}" == "true" ]; then - gh issue edit ${{ github.event.issue.number }} --add-label "potential-regression" -R ${{ github.repository }} + if [ "$IS_REGRESSION" == "true" ]; then + gh issue edit "$ISSUE_NUMBER" --add-label "potential-regression" -R "$REPO" else - gh issue edit ${{ github.event.issue.number }} --remove-label "potential-regression" -R ${{ github.repository }} + gh issue edit "$ISSUE_NUMBER" --remove-label "potential-regression" -R "$REPO" fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/include/aws/common/byte_buf.h new/aws-c-common-0.14.2/include/aws/common/byte_buf.h --- old/aws-c-common-0.14.1/include/aws/common/byte_buf.h 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/include/aws/common/byte_buf.h 2026-07-09 18:53:07.000000000 +0200 @@ -23,6 +23,19 @@ * Note that this structure allocates memory at the buffer pointer only. The * struct itself does not get dynamically allocated and must be either * maintained or copied to avoid losing access to the memory. + * + * GROWABILITY CONTRACT: + * - When `allocator != NULL`: the buffer owns its memory and can be grown + * via aws_byte_buf_reserve / aws_byte_buf_append_dynamic / etc. + * - When `allocator == NULL`: the buffer's memory is externally owned + * (e.g. by a buffer pool, by static storage, or by a stack frame) and + * MUST NOT be reallocated. Functions that would grow the buffer + * (aws_byte_buf_reserve, aws_byte_buf_append_dynamic, etc.) fail with + * AWS_ERROR_INVALID_ARGUMENT when called on such a buffer. + * + * Callers that may receive a buffer of either kind (e.g. from a buffer + * pool ticket) should use aws_byte_buf_append_auto, which selects + * the right append path automatically. */ struct aws_byte_buf { /* do not reorder this, this struct lines up nicely with windows buffer structures--saving us allocations.*/ @@ -415,6 +428,28 @@ int aws_byte_buf_append_dynamic(struct aws_byte_buf *to, const struct aws_byte_cursor *from); /** + * Copies `from` to `to`, selecting between static and dynamic append based + * on whether `to` has an allocator. + * + * - If `to->allocator != NULL`: uses aws_byte_buf_append_dynamic (grows + * the buffer as needed; same behavior as calling that function directly). + * - If `to->allocator == NULL`: uses aws_byte_buf_append (no grow; returns + * AWS_ERROR_DEST_COPY_TOO_SMALL if `from` does not fit in remaining + * capacity). + * + * This is intended for callers that receive a buffer from a source whose + * growability is not known at the call site — most commonly a buffer + * obtained from an aws_s3_buffer_pool ticket, where pool-backed tickets + * return a fixed-size buffer (allocator == NULL) and the default pool may + * return a growable one. + * + * `from` and `to` may be the same buffer, permitting copying a buffer + * into itself. + */ +AWS_COMMON_API +int aws_byte_buf_append_auto(struct aws_byte_buf *to, const struct aws_byte_cursor *from); + +/** * Copies `from` to `to`. If `to` is too small, the buffer will be grown appropriately and * the old contents copied over, before the new contents are appended. * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/source/byte_buf.c new/aws-c-common-0.14.2/source/byte_buf.c --- old/aws-c-common-0.14.1/source/byte_buf.c 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/source/byte_buf.c 2026-07-09 18:53:07.000000000 +0200 @@ -760,6 +760,21 @@ return s_aws_byte_buf_append_dynamic(to, from, false); } +int aws_byte_buf_append_auto(struct aws_byte_buf *to, const struct aws_byte_cursor *from) { + AWS_PRECONDITION(aws_byte_buf_is_valid(to)); + AWS_PRECONDITION(aws_byte_cursor_is_valid(from)); + + /* + * Buffers without an allocator are externally-owned (e.g. by a + * buffer pool); they cannot grow, so use the static append path. + * Buffers with an allocator can grow, so use the dynamic path. + */ + if (to->allocator != NULL) { + return aws_byte_buf_append_dynamic(to, from); + } + return aws_byte_buf_append(to, from); +} + int aws_byte_buf_append_dynamic_secure(struct aws_byte_buf *to, const struct aws_byte_cursor *from) { return s_aws_byte_buf_append_dynamic(to, from, true); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/source/xml_parser.c new/aws-c-common-0.14.2/source/xml_parser.c --- old/aws-c-common-0.14.1/source/xml_parser.c 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/source/xml_parser.c 2026-07-09 18:53:07.000000000 +0200 @@ -40,6 +40,11 @@ AWS_PRECONDITION(decl_body); AWS_PRECONDITION(node); + if (decl_body->len == 0) { + AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); + return aws_raise_error(AWS_ERROR_INVALID_XML); + } + node->is_empty = decl_body->ptr[decl_body->len - 1] == '/'; struct aws_array_list splits; @@ -159,6 +164,77 @@ return parser.error; } +/* Returns true if the byte can follow a tag name in a start or empty-element tag. */ +static bool s_is_tag_name_boundary(uint8_t c) { + return c == ' ' || c == '>' || c == '/' || c == '\t' || c == '\r' || c == '\n'; +} + +static struct aws_byte_cursor s_comment_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("<!--"); +static struct aws_byte_cursor s_comment_end = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("-->"); +static struct aws_byte_cursor s_cdata_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("<![CDATA["); +static struct aws_byte_cursor s_cdata_end = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("]]>"); +static struct aws_byte_cursor s_pi_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("<?"); +static struct aws_byte_cursor s_pi_end = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("?>"); +static struct aws_byte_cursor s_self_close_suffix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("/>"); +static struct aws_byte_cursor s_decl_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("<!"); +static struct aws_byte_cursor s_close_bracket = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(">"); +static struct aws_byte_cursor s_closing_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("</"); + +/* + * If `*input` points at a non-element construct (comment, CDATA, or PI), advances + * `*input` past the construct's closing delimiter, sets `*out_skipped = true`, and + * returns AWS_OP_SUCCESS. + * + * If `*input` does not start with a non-element construct, sets `*out_skipped = false` + * and returns AWS_OP_SUCCESS (caller should proceed with normal element handling). + * + * If the construct is unterminated (no closing delimiter found), returns AWS_OP_ERR + * and raises AWS_ERROR_INVALID_XML. + * + * WARNING: This function assumes `*input` starts with '<'. It will not produce correct + * results if called at an arbitrary position within a document. + */ +static int s_try_skip_non_element(struct aws_byte_cursor *input, bool *out_skipped) { + *out_skipped = false; + + if (input->len == 0 || input->ptr[0] != '<') { + return AWS_OP_SUCCESS; + } + + if (aws_byte_cursor_starts_with(input, &s_comment_prefix)) { + aws_byte_cursor_advance(input, s_comment_prefix.len); + struct aws_byte_cursor found; + if (aws_byte_cursor_find_exact(input, &s_comment_end, &found)) { + return aws_raise_error(AWS_ERROR_INVALID_XML); + } + aws_byte_cursor_advance(input, (found.ptr + s_comment_end.len) - input->ptr); + *out_skipped = true; + return AWS_OP_SUCCESS; + } + if (aws_byte_cursor_starts_with(input, &s_cdata_prefix)) { + aws_byte_cursor_advance(input, s_cdata_prefix.len); + struct aws_byte_cursor found; + if (aws_byte_cursor_find_exact(input, &s_cdata_end, &found)) { + return aws_raise_error(AWS_ERROR_INVALID_XML); + } + aws_byte_cursor_advance(input, (found.ptr + s_cdata_end.len) - input->ptr); + *out_skipped = true; + return AWS_OP_SUCCESS; + } + if (aws_byte_cursor_starts_with(input, &s_pi_prefix)) { + aws_byte_cursor_advance(input, s_pi_prefix.len); + struct aws_byte_cursor found; + if (aws_byte_cursor_find_exact(input, &s_pi_end, &found)) { + return aws_raise_error(AWS_ERROR_INVALID_XML); + } + aws_byte_cursor_advance(input, (found.ptr + s_pi_end.len) - input->ptr); + *out_skipped = true; + return AWS_OP_SUCCESS; + } + + return AWS_OP_SUCCESS; +} + int s_advance_to_closing_tag( struct aws_xml_parser *parser, struct aws_xml_node *node, @@ -208,45 +284,68 @@ aws_byte_buf_append(&closing_cmp_buf, &node->name); aws_byte_buf_append(&closing_cmp_buf, &close_bracket); - size_t depth_count = 1; struct aws_byte_cursor to_find_open = aws_byte_cursor_from_buf(&open_cmp_buf); struct aws_byte_cursor to_find_close = aws_byte_cursor_from_buf(&closing_cmp_buf); - struct aws_byte_cursor close_find_result; - AWS_ZERO_STRUCT(close_find_result); - do { - if (aws_byte_cursor_find_exact(&parser->doc, &to_find_close, &close_find_result)) { + + /* Single forward scan: jump between '<' characters, tracking nesting depth. */ + size_t depth = 1; + while (parser->doc.len > 0) { + const uint8_t *open = memchr(parser->doc.ptr, '<', parser->doc.len); + if (!open) { + break; + } + aws_byte_cursor_advance(&parser->doc, open - parser->doc.ptr); + + /* Skip non-element constructs (comments, CDATA, PI). */ + bool skipped = false; + if (s_try_skip_non_element(&parser->doc, &skipped)) { AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); - return aws_raise_error(AWS_ERROR_INVALID_XML); + return AWS_OP_ERR; + } + if (skipped) { + continue; } - /* if we find an opening node with the same name, before the closing tag keep going. */ - struct aws_byte_cursor open_find_result; - AWS_ZERO_STRUCT(open_find_result); - - while (parser->doc.len) { - if (!aws_byte_cursor_find_exact(&parser->doc, &to_find_open, &open_find_result)) { - if (open_find_result.ptr < close_find_result.ptr) { - size_t skip_len = open_find_result.ptr - parser->doc.ptr; - aws_byte_cursor_advance(&parser->doc, skip_len + 1); - depth_count++; - continue; + /* Check for closing tag. */ + if (aws_byte_cursor_starts_with(&parser->doc, &to_find_close)) { + depth--; + if (depth == 0) { + size_t len = parser->doc.ptr - node->doc_at_body.ptr; + if (out_body) { + *out_body = aws_byte_cursor_from_array(node->doc_at_body.ptr, len); } + aws_byte_cursor_advance(&parser->doc, to_find_close.len); + return parser->error; } - size_t skip_len = close_find_result.ptr - parser->doc.ptr; - - aws_byte_cursor_advance(&parser->doc, skip_len + closing_cmp_buf.len); - depth_count--; - break; + aws_byte_cursor_advance(&parser->doc, to_find_close.len); + continue; } - } while (depth_count > 0); - size_t len = close_find_result.ptr - node->doc_at_body.ptr; + /* Check for opening tag with same name. */ + if (aws_byte_cursor_starts_with(&parser->doc, &to_find_open)) { - if (out_body) { - *out_body = aws_byte_cursor_from_array(node->doc_at_body.ptr, len); + struct aws_byte_cursor after_open = parser->doc; + aws_byte_cursor_advance(&after_open, to_find_open.len); + + if (after_open.len > 0 && s_is_tag_name_boundary(*after_open.ptr)) { + /* Check for self-closing tag (e.g. <a/>) — does not increment depth. */ + if (!aws_byte_cursor_starts_with(&after_open, &s_self_close_suffix)) { + depth++; + } + } + /* Advance past the '<' regardless — the name boundary / self-close checks + * only determine whether depth increments; we always move forward. */ + aws_byte_cursor_advance(&parser->doc, 1); + continue; + } + + /* Some other '<' — skip past it. */ + aws_byte_cursor_advance(&parser->doc, 1); } - return parser->error; + AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); + parser->error = aws_raise_error(AWS_ERROR_INVALID_XML); + return AWS_OP_ERR; } int aws_xml_node_as_body(struct aws_xml_node *node, struct aws_byte_cursor *out_body) { @@ -275,7 +374,7 @@ size_t doc_depth = aws_array_list_length(&parser->callback_stack); if (doc_depth >= parser->max_depth) { - AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document exceeds max depth."); + AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document exceeds max depth of %zu.", parser->max_depth); aws_raise_error(AWS_ERROR_INVALID_XML); goto error; } @@ -285,37 +384,64 @@ /* look for the next node at the current level. do this until we encounter the parent node's * closing tag. */ while (!parser->error) { - const uint8_t *next_location = memchr(parser->doc.ptr, '<', parser->doc.len); + const uint8_t *open = memchr(parser->doc.ptr, '<', parser->doc.len); - if (!next_location) { + if (!open) { AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); aws_raise_error(AWS_ERROR_INVALID_XML); goto error; } - const uint8_t *end_location = memchr(parser->doc.ptr, '>', parser->doc.len); + /* Advance to the '<'. Everything leading up to the `<` is disregarded. */ + aws_byte_cursor_advance(&parser->doc, open - parser->doc.ptr); - if (!end_location || next_location >= end_location) { + /* Skip CDATA, comments, and processing instructions — they are not elements. */ + bool skipped = false; + if (s_try_skip_non_element(&parser->doc, &skipped)) { AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); - aws_raise_error(AWS_ERROR_INVALID_XML); goto error; } + if (skipped) { + continue; + } - bool parent_closed = false; + /* Handle other <! declarations (e.g. <!DOCTYPE) not covered by the helper — skip to closing >. */ + if (aws_byte_cursor_starts_with(&parser->doc, &s_decl_prefix)) { + struct aws_byte_cursor found; + if (aws_byte_cursor_find_exact(&parser->doc, &s_close_bracket, &found)) { + AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); + aws_raise_error(AWS_ERROR_INVALID_XML); + goto error; + } + aws_byte_cursor_advance(&parser->doc, (found.ptr + 1) - parser->doc.ptr); + continue; + } - if (*(next_location + 1) == '/') { - parent_closed = true; + /* parser->doc.ptr is now at '<'. Find the closing '>'. */ + if (parser->doc.len < 2) { + AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); + aws_raise_error(AWS_ERROR_INVALID_XML); + goto error; } + const uint8_t *end_location = memchr(parser->doc.ptr + 1, '>', parser->doc.len - 1); + + if (!end_location) { + AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid."); + aws_raise_error(AWS_ERROR_INVALID_XML); + goto error; + } + + bool parent_closed = aws_byte_cursor_starts_with(&parser->doc, &s_closing_prefix); + + size_t node_name_len = end_location - parser->doc.ptr; + struct aws_byte_cursor decl_body = aws_byte_cursor_from_array(parser->doc.ptr + 1, node_name_len - 1); - size_t node_name_len = end_location - next_location; aws_byte_cursor_advance(&parser->doc, end_location - parser->doc.ptr + 1); if (parent_closed) { break; } - struct aws_byte_cursor decl_body = aws_byte_cursor_from_array(next_location + 1, node_name_len - 1); - struct aws_xml_node next_node = { .parser = parser, .doc_at_body = parser->doc, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/tests/CMakeLists.txt new/aws-c-common-0.14.2/tests/CMakeLists.txt --- old/aws-c-common-0.14.1/tests/CMakeLists.txt 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/tests/CMakeLists.txt 2026-07-09 18:53:07.000000000 +0200 @@ -289,6 +289,7 @@ add_test_case(test_byte_buf_append_and_update_fail) add_test_case(test_byte_buf_append_and_update_success) add_test_case(test_byte_buf_append_dynamic) +add_test_case(test_byte_buf_append_auto) add_test_case(test_byte_buf_append_byte) add_test_case(test_byte_buf_append_lookup_success) add_test_case(test_byte_buf_append_lookup_failure) @@ -483,7 +484,7 @@ add_test_case(cpuid_test) add_test_case(xml_parser_root_with_text) -add_test_case(xml_parser_malformed_end_node_character_before_start_test) +add_test_case(xml_parser_empty_element_name_test) add_test_case(xml_parser_child_with_text) add_test_case(xml_parser_siblings_with_text) add_test_case(xml_parser_preamble_and_attributes) @@ -495,6 +496,17 @@ add_test_case(xml_parser_unescape_test) add_test_case(xml_parser_unescape_error_test) add_test_case(xml_parser_child_with_text_escaped) +add_test_case(xml_parser_prefix_collision_test) +add_test_case(xml_parser_nested_same_name_with_prefix_children_test) +add_test_case(xml_parser_comment_skipped_test) +add_test_case(xml_parser_cdata_skipped_test) +add_test_case(xml_parser_pi_skipped_test) +add_test_case(xml_parser_comment_with_gt_test) +add_test_case(xml_parser_nested_same_name_large_depth_test) +add_test_case(xml_parser_self_closing_same_name_test) +add_test_case(xml_parser_cdata_false_close_test) +add_test_case(xml_parser_comment_false_open_test) +add_test_case(xml_parser_trailing_open_bracket_test) add_test_case(test_thread_scheduler_ordering) add_test_case(test_thread_scheduler_happy_path_cancellation) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/tests/byte_buf_test.c new/aws-c-common-0.14.2/tests/byte_buf_test.c --- old/aws-c-common-0.14.1/tests/byte_buf_test.c 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/tests/byte_buf_test.c 2026-07-09 18:53:07.000000000 +0200 @@ -632,6 +632,81 @@ } AWS_TEST_CASE(test_byte_buf_append_dynamic, s_test_byte_buf_append_dynamic) +static int s_test_byte_buf_append_auto(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + uint8_t src_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + struct aws_byte_cursor src = aws_byte_cursor_from_array(src_bytes, sizeof(src_bytes)); + + /* + * Case 1: growable buffer (allocator != NULL) — append_auto must + * grow the buffer when needed, matching aws_byte_buf_append_dynamic. + */ + { + struct aws_byte_buf growable; + ASSERT_SUCCESS(aws_byte_buf_init(&growable, allocator, 4)); + ASSERT_INT_EQUALS(0, growable.len); + ASSERT_INT_EQUALS(4, growable.capacity); + + /* Source is 8 bytes; buffer capacity is 4 — must grow. */ + ASSERT_SUCCESS(aws_byte_buf_append_auto(&growable, &src)); + ASSERT_INT_EQUALS(8, growable.len); + ASSERT_TRUE(growable.capacity >= 8); + ASSERT_BIN_ARRAYS_EQUALS(src_bytes, sizeof(src_bytes), growable.buffer, growable.len); + + aws_byte_buf_clean_up(&growable); + } + + /* + * Case 2: fixed-size buffer (allocator == NULL) where the source + * fits — append_auto must succeed via the static path. + */ + { + uint8_t backing[16] = {0}; + struct aws_byte_buf fixed = aws_byte_buf_from_empty_array(backing, sizeof(backing)); + ASSERT_PTR_EQUALS(NULL, fixed.allocator); + + ASSERT_SUCCESS(aws_byte_buf_append_auto(&fixed, &src)); + ASSERT_INT_EQUALS(8, fixed.len); + ASSERT_INT_EQUALS(16, fixed.capacity); + ASSERT_BIN_ARRAYS_EQUALS(src_bytes, sizeof(src_bytes), fixed.buffer, fixed.len); + + /* No clean_up — fixed buffer is stack-backed. */ + } + + /* + * Case 3: fixed-size buffer (allocator == NULL) where the source + * does NOT fit — append_auto must fail with + * AWS_ERROR_DEST_COPY_TOO_SMALL (loud failure, no realloc attempt). + */ + { + uint8_t backing[4] = {0}; + struct aws_byte_buf too_small = aws_byte_buf_from_empty_array(backing, sizeof(backing)); + ASSERT_PTR_EQUALS(NULL, too_small.allocator); + + ASSERT_FAILS(aws_byte_buf_append_auto(&too_small, &src)); + ASSERT_INT_EQUALS(AWS_ERROR_DEST_COPY_TOO_SMALL, aws_last_error()); + ASSERT_INT_EQUALS(0, too_small.len); + ASSERT_INT_EQUALS(4, too_small.capacity); + } + + /* + * Case 4: empty source on a fixed-size buffer — should succeed + * without modifying state. + */ + { + uint8_t backing[4] = {0}; + struct aws_byte_buf fixed = aws_byte_buf_from_empty_array(backing, sizeof(backing)); + struct aws_byte_cursor empty_src = {.len = 0, .ptr = NULL}; + + ASSERT_SUCCESS(aws_byte_buf_append_auto(&fixed, &empty_src)); + ASSERT_INT_EQUALS(0, fixed.len); + } + + return 0; +} +AWS_TEST_CASE(test_byte_buf_append_auto, s_test_byte_buf_append_auto) + static uint8_t s_append_byte_array[] = {0xFF, 0xFE, 0xAB, 0x00, 0x55, 0x62}; static int s_test_byte_buf_append_byte(struct aws_allocator *allocator, void *ctx) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.1/tests/xml_parser_test.c new/aws-c-common-0.14.2/tests/xml_parser_test.c --- old/aws-c-common-0.14.1/tests/xml_parser_test.c 2026-06-24 19:58:50.000000000 +0200 +++ new/aws-c-common-0.14.2/tests/xml_parser_test.c 2026-07-09 18:53:07.000000000 +0200 @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include <aws/common/clock.h> #include <aws/common/xml_parser.h> #include <aws/testing/aws_test_harness.h> @@ -95,16 +96,16 @@ AWS_TEST_CASE(xml_parser_child_with_text, s_xml_parser_child_with_text_test) -const char *malformed_end_node_character_before_start = - "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rootNode>><child1>TestBody</child1></rootNode>"; +const char *s_empty_element_name_doc = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rootNode><><child1>TestBody</child1></rootNode>"; -static int s_xml_parser_malformed_end_node_character_before_start_test(struct aws_allocator *allocator, void *ctx) { +static int s_xml_parser_empty_element_name_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct child_text_capture capture; AWS_ZERO_STRUCT(capture); struct aws_xml_parser_options options = { - .doc = aws_byte_cursor_from_c_str(malformed_end_node_character_before_start), + .doc = aws_byte_cursor_from_c_str(s_empty_element_name_doc), .on_root_encountered = s_root_with_child, .user_data = &capture, }; @@ -113,9 +114,7 @@ return AWS_OP_SUCCESS; } -AWS_TEST_CASE( - xml_parser_malformed_end_node_character_before_start_test, - s_xml_parser_malformed_end_node_character_before_start_test) +AWS_TEST_CASE(xml_parser_empty_element_name_test, s_xml_parser_empty_element_name_test) const char *siblings_with_text = "<?xml version=\"1.0\" " @@ -617,3 +616,396 @@ } AWS_TEST_CASE(xml_parser_child_with_text_escaped, s_xml_parser_child_with_text_escaped_test) + +/* + * Verify that skipping <Owner> does not falsely match <OwnerId> as a nested same-name open. + * The open-tag search must require a name-terminator after the tag name. + */ +const char *prefix_collision_doc = "<ListBucketResult>" + "<Contents>" + "<Owner><OwnerId>abc123</OwnerId><DisplayName>test</DisplayName></Owner>" + "<Key>file.txt</Key>" + "</Contents>" + "</ListBucketResult>"; + +struct prefix_collision_capture { + struct aws_byte_cursor owner_body; + struct aws_byte_cursor key_body; +}; + +int s_prefix_collision_contents_child(struct aws_xml_node *node, void *user_data) { + struct prefix_collision_capture *capture = user_data; + struct aws_byte_cursor name = aws_xml_node_get_name(node); + struct aws_byte_cursor owner_name = aws_byte_cursor_from_c_str("Owner"); + struct aws_byte_cursor key_name = aws_byte_cursor_from_c_str("Key"); + + if (aws_byte_cursor_eq(&name, &owner_name)) { + return aws_xml_node_as_body(node, &capture->owner_body); + } else if (aws_byte_cursor_eq(&name, &key_name)) { + return aws_xml_node_as_body(node, &capture->key_body); + } + return AWS_OP_SUCCESS; +} + +int s_prefix_collision_contents(struct aws_xml_node *node, void *user_data) { + return aws_xml_node_traverse(node, s_prefix_collision_contents_child, user_data); +} + +int s_prefix_collision_root(struct aws_xml_node *node, void *user_data) { + return aws_xml_node_traverse(node, s_prefix_collision_contents, user_data); +} + +static int s_xml_parser_prefix_collision_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct prefix_collision_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(prefix_collision_doc), + .on_root_encountered = s_prefix_collision_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_owner[] = "<OwnerId>abc123</OwnerId><DisplayName>test</DisplayName>"; + const char expected_key[] = "file.txt"; + + ASSERT_BIN_ARRAYS_EQUALS( + expected_owner, sizeof(expected_owner) - 1, capture.owner_body.ptr, capture.owner_body.len); + ASSERT_BIN_ARRAYS_EQUALS(expected_key, sizeof(expected_key) - 1, capture.key_body.ptr, capture.key_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_prefix_collision_test, s_xml_parser_prefix_collision_test) + +/* + * Verify that true nested same-name elements (<a> inside <a>) and prefix-sharing + * siblings (<ab> inside <a>) both parse correctly. + */ +const char *nested_same_name_with_prefix_children_doc = "<a><a>inner</a><ab>other</ab></a>"; + +struct nested_prefix_capture { + struct aws_byte_cursor a_body; +}; + +int s_nested_prefix_root(struct aws_xml_node *node, void *user_data) { + struct nested_prefix_capture *capture = user_data; + return aws_xml_node_as_body(node, &capture->a_body); +} + +static int s_xml_parser_nested_same_name_with_prefix_children_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct nested_prefix_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(nested_same_name_with_prefix_children_doc), + .on_root_encountered = s_nested_prefix_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected[] = "<a>inner</a><ab>other</ab>"; + ASSERT_BIN_ARRAYS_EQUALS(expected, sizeof(expected) - 1, capture.a_body.ptr, capture.a_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + xml_parser_nested_same_name_with_prefix_children_test, + s_xml_parser_nested_same_name_with_prefix_children_test) + +/* + * Verify that XML comments (<!-- ... -->) are skipped during child traversal. + */ +const char *comment_in_body_doc = "<root><!-- this is a comment --><child>value</child></root>"; + +struct comment_capture { + struct aws_byte_cursor child_body; + struct aws_byte_cursor child_name; +}; + +int s_comment_child(struct aws_xml_node *node, void *user_data) { + struct comment_capture *capture = user_data; + capture->child_name = aws_xml_node_get_name(node); + return aws_xml_node_as_body(node, &capture->child_body); +} + +int s_comment_root(struct aws_xml_node *node, void *user_data) { + return aws_xml_node_traverse(node, s_comment_child, user_data); +} + +static int s_xml_parser_comment_skipped_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct comment_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(comment_in_body_doc), + .on_root_encountered = s_comment_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_name[] = "child"; + const char expected_value[] = "value"; + ASSERT_BIN_ARRAYS_EQUALS(expected_name, sizeof(expected_name) - 1, capture.child_name.ptr, capture.child_name.len); + ASSERT_BIN_ARRAYS_EQUALS( + expected_value, sizeof(expected_value) - 1, capture.child_body.ptr, capture.child_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_comment_skipped_test, s_xml_parser_comment_skipped_test) + +/* + * Verify that CDATA sections (<![CDATA[...]]>) are skipped during child traversal. + */ +const char *cdata_in_body_doc = "<root><![CDATA[<not>an</element>]]><child>value</child></root>"; + +static int s_xml_parser_cdata_skipped_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct comment_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(cdata_in_body_doc), + .on_root_encountered = s_comment_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_name[] = "child"; + const char expected_value[] = "value"; + ASSERT_BIN_ARRAYS_EQUALS(expected_name, sizeof(expected_name) - 1, capture.child_name.ptr, capture.child_name.len); + ASSERT_BIN_ARRAYS_EQUALS( + expected_value, sizeof(expected_value) - 1, capture.child_body.ptr, capture.child_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_cdata_skipped_test, s_xml_parser_cdata_skipped_test) + +/* + * Verify that processing instructions (<?...?>) are skipped during child traversal. + */ +const char *pi_in_body_doc = "<root><?xml-stylesheet type=\"text/xsl\" href=\"style.xsl\"?><child>value</child></root>"; + +static int s_xml_parser_pi_skipped_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct comment_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(pi_in_body_doc), + .on_root_encountered = s_comment_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_name[] = "child"; + const char expected_value[] = "value"; + ASSERT_BIN_ARRAYS_EQUALS(expected_name, sizeof(expected_name) - 1, capture.child_name.ptr, capture.child_name.len); + ASSERT_BIN_ARRAYS_EQUALS( + expected_value, sizeof(expected_value) - 1, capture.child_body.ptr, capture.child_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_pi_skipped_test, s_xml_parser_pi_skipped_test) + +/* + * Verify that '>' inside a comment does not terminate the comment early + * or cause subsequent content to be parsed as a sibling element. + */ +const char *comment_with_gt_doc = "<root><!-- contains > character --><child>safe</child></root>"; + +static int s_xml_parser_comment_with_gt_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct comment_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(comment_with_gt_doc), + .on_root_encountered = s_comment_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_name[] = "child"; + const char expected_value[] = "safe"; + ASSERT_BIN_ARRAYS_EQUALS(expected_name, sizeof(expected_name) - 1, capture.child_name.ptr, capture.child_name.len); + ASSERT_BIN_ARRAYS_EQUALS( + expected_value, sizeof(expected_value) - 1, capture.child_body.ptr, capture.child_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_comment_with_gt_test, s_xml_parser_comment_with_gt_test) + +/* + * Verify that parsing deeply nested same-name elements (N=20000) completes in O(N) time. + * The test asserts the parse finishes within 2 seconds. + */ +static int s_xml_parser_nested_same_name_large_depth_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + /* Build: <a><a><a>...(20000 opens)...</a></a></a>...(20000 closes) */ + const size_t depth = 20000; + struct aws_byte_buf doc_buf; + aws_byte_buf_init(&doc_buf, allocator, depth * 10); + + struct aws_byte_cursor a_open = aws_byte_cursor_from_c_str("<a>"); + struct aws_byte_cursor a_close = aws_byte_cursor_from_c_str("</a>"); + + for (size_t i = 0; i < depth; i++) { + aws_byte_buf_append_dynamic(&doc_buf, &a_open); + } + for (size_t i = 0; i < depth; i++) { + aws_byte_buf_append_dynamic(&doc_buf, &a_close); + } + + struct nested_node_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_buf(&doc_buf), + .on_root_encountered = s_nested_node, + .user_data = &capture, + }; + + uint64_t start_ns = 0; + aws_high_res_clock_get_ticks(&start_ns); + + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + uint64_t end_ns = 0; + aws_high_res_clock_get_ticks(&end_ns); + + uint64_t elapsed_ms = (end_ns - start_ns) / 1000000; + ASSERT_TRUE( + elapsed_ms < 2000, + "xml parse took %llu ms — expected < 2000 ms with depth=%zu", + (unsigned long long)elapsed_ms, + depth); + + ASSERT_TRUE(capture.node_body.len > 0); + + aws_byte_buf_clean_up(&doc_buf); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_nested_same_name_large_depth_test, s_xml_parser_nested_same_name_large_depth_test) + +/* + * Verify that self-closing tags (<a/>) inside <a> do not increment depth count. + */ +const char *self_closing_same_name_doc = "<a><a/>inner</a>"; + +static int s_xml_parser_self_closing_same_name_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct nested_node_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(self_closing_same_name_doc), + .on_root_encountered = s_nested_node, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected[] = "<a/>inner"; + ASSERT_BIN_ARRAYS_EQUALS(expected, sizeof(expected) - 1, capture.node_body.ptr, capture.node_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_self_closing_same_name_test, s_xml_parser_self_closing_same_name_test) + +/* + * Verify that a close tag inside CDATA is not treated as the real close tag. + */ +const char *cdata_false_close_doc = "<data><![CDATA[</data>]]>real</data>"; + +int s_cdata_false_close_root(struct aws_xml_node *node, void *user_data) { + struct nested_node_capture *capture = user_data; + return aws_xml_node_as_body(node, &capture->node_body); +} + +static int s_xml_parser_cdata_false_close_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct nested_node_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(cdata_false_close_doc), + .on_root_encountered = s_cdata_false_close_root, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected[] = "<![CDATA[</data>]]>real"; + ASSERT_BIN_ARRAYS_EQUALS(expected, sizeof(expected) - 1, capture.node_body.ptr, capture.node_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_cdata_false_close_test, s_xml_parser_cdata_false_close_test) + +/* + * Verify that an open tag inside a comment does not cause depth increment. + */ +const char *comment_false_open_doc = "<tag><!-- <tag> -->body</tag>"; + +static int s_xml_parser_comment_false_open_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct nested_node_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(comment_false_open_doc), + .on_root_encountered = s_nested_node, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected[] = "<!-- <tag> -->body"; + ASSERT_BIN_ARRAYS_EQUALS(expected, sizeof(expected) - 1, capture.node_body.ptr, capture.node_body.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_comment_false_open_test, s_xml_parser_comment_false_open_test) + +/* + * Verify that a document ending with a bare '<' does not crash (out-of-bounds read). + */ +static int s_xml_parser_trailing_open_bracket_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + const char *doc = "<a><"; + + struct nested_node_capture capture; + AWS_ZERO_STRUCT(capture); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(doc), + .on_root_encountered = s_nested_node, + .user_data = &capture, + }; + ASSERT_ERROR(AWS_ERROR_INVALID_XML, aws_xml_parse(allocator, &options)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_trailing_open_bracket_test, s_xml_parser_trailing_open_bracket_test)
