Version in base suite: 2.8.3-1~deb13u1 Base version: expat_2.8.3-1~deb13u1 Target version: expat_2.9.0-1~deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/e/expat/expat_2.8.3-1~deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/e/expat/expat_2.9.0-1~deb13u1.dsc .ci.sh | 2 .github/workflows/autotools-cmake.yml | 22 .github/workflows/clang-format.yml | 9 .github/workflows/clang-static-analyzer.yml | 15 .github/workflows/clang-tidy.yml | 9 .github/workflows/coverage.yml | 2 .github/workflows/coverity-scan.yml | 15 .github/workflows/cppcheck.yml | 4 .github/workflows/data/expat_config_h_cmake__expected.txt | 1 .github/workflows/data/expat_config_h_in__expected.txt | 1 .github/workflows/data/exported-symbols-unversioned.txt | 11 .github/workflows/data/exported-symbols-versioned-default.txt | 72 .github/workflows/data/exported-symbols-versioned-maximum.txt | 73 .github/workflows/data/exported-symbols-versioned.txt | 83 .github/workflows/emscripten.yml | 2 .github/workflows/expat_config_h.yml | 4 .github/workflows/fil-c.yml | 81 .github/workflows/freebsd.yml | 2 .github/workflows/fuzzing.yml | 13 .github/workflows/linux.yml | 29 .github/workflows/mingw-clang.yml | 173 + .github/workflows/musl.yml | 8 .github/workflows/perl-integration.yml | 2 .github/workflows/qemu.yml | 97 .github/workflows/scripts/check_for_missing_config_header_includes.sh | 64 .github/workflows/scripts/mass-cppcheck.sh | 2 .github/workflows/solaris.yml | 2 .github/workflows/valid-xml.yml | 16 .github/workflows/wasi_sdk.yml | 9 .github/workflows/windows-build.yml | 4 .github/workflows/zizmor.yml | 2 .mailmap | 2 README.md | 2 debian/changelog | 48 debian/libexpat1.symbols | 22 expat/CMake.README | 12 expat/CMakeLists.txt | 44 expat/Changes | 246 ++ expat/Makefile.am | 7 expat/README.md | 2 expat/apply-clang-tidy.sh | 1 expat/apply-htmltidy.sh | 53 expat/cmake/autotools/.gitignore | 1 expat/cmake/autotools/expat__macos.cmake.in | 2 expat/cmake/expat-config.cmake.in | 4 expat/configure.ac | 43 expat/coverage.sh | 16 expat/distribute.sh | 3 expat/doc/reference.html | 782 ++++++- expat/doc/xmlwf.xml | 2 expat/examples/element_declarations.c | 14 expat/examples/elements.c | 14 expat/examples/outline.c | 14 expat/expat_config.h.cmake | 4 expat/fix-xmltest-log.sh | 3 expat/fuzz/xml_lpm_fuzzer.cpp | 5 expat/fuzz/xml_parse_fuzzer.c | 16 expat/fuzz/xml_parsebuffer_fuzzer.c | 15 expat/lib/.gitignore | 1 expat/lib/Makefile.am | 4 expat/lib/expat.h | 103 - expat/lib/expat_external.h | 15 expat/lib/hash_table.h | 78 expat/lib/internal.h | 158 - expat/lib/libexpat.def.cmake | 14 expat/lib/libexpat.map.in | 17 expat/lib/random_getentropy.c | 2 expat/lib/xcs.c | 90 expat/lib/xcs.h | 44 expat/lib/xcsinc.c | 54 expat/lib/xmlparse.c | 1013 ++++++---- expat/lib/xmlrole.c | 113 - expat/lib/xmlrole.h | 14 expat/lib/xmltok.c | 395 +-- expat/lib/xmltok.h | 62 expat/lib/xmltok_impl.c | 65 expat/lib/xmltok_ns.c | 6 expat/tests/Makefile.am | 14 expat/tests/acc_tests.c | 37 expat/tests/acc_tests.h | 12 expat/tests/alloc_tests.c | 59 expat/tests/alloc_tests.h | 12 expat/tests/basic_tests.c | 599 +++++ expat/tests/basic_tests.h | 12 expat/tests/benchmark/benchmark.c | 19 expat/tests/chardata.c | 4 expat/tests/chardata.h | 10 expat/tests/common.c | 11 expat/tests/common.h | 82 expat/tests/dummy.h | 48 expat/tests/handlers.c | 115 - expat/tests/handlers.h | 43 expat/tests/hash_tests.c | 157 + expat/tests/hash_tests.h | 41 expat/tests/memcheck.c | 3 expat/tests/memcheck.h | 12 expat/tests/minicheck.h | 76 expat/tests/misc_tests.c | 170 + expat/tests/misc_tests.h | 12 expat/tests/ns_tests.h | 12 expat/tests/nsalloc_tests.c | 2 expat/tests/nsalloc_tests.h | 12 expat/tests/props_tests.c | 790 +++++++ expat/tests/props_tests.h | 41 expat/tests/runtests.c | 24 expat/tests/structdata.c | 11 expat/tests/structdata.h | 9 expat/tests/xcs_tests.c | 86 expat/tests/xcs_tests.h | 41 expat/tests/xmltest.sh | 2 expat/win32/expat.iss | 2 expat/xmlwf/Makefile.am | 3 expat/xmlwf/unixfilemap.c | 4 expat/xmlwf/xmlfile.c | 15 expat/xmlwf/xmlfile.h | 16 expat/xmlwf/xmlmime.h | 10 expat/xmlwf/xmlwf.c | 56 expat/xmlwf/xmlwf_helpgen.py | 7 expat/xmlwf/xmlwf_helpgen.sh | 8 119 files changed, 5351 insertions(+), 1802 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpks177kri/expat_2.8.3-1~deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpks177kri/expat_2.9.0-1~deb13u1.dsc: no acceptable signature found diff -Nru expat-2.8.3/.ci.sh expat-2.9.0/.ci.sh --- expat-2.8.3/.ci.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.ci.sh 2026-10-05 15:43:59.000000000 +0000 @@ -39,7 +39,7 @@ latest_brew_python3_bin="$(ls -1d /usr/local/Cellar/python/3.*/bin | sort -n | tail -n1)" export PATH="${latest_brew_python3_bin}${PATH:+:}${PATH}" elif [[ ${RUNNER_OS} = Linux ]]; then - export PATH="/usr/lib/llvm-22/bin:${PATH}" + export PATH="/usr/lib/llvm-23/bin:${PATH}" else echo "Unsupported RUNNER_OS \"${RUNNER_OS}\"." >&2 exit 1 diff -Nru expat-2.8.3/.github/workflows/autotools-cmake.yml expat-2.9.0/.github/workflows/autotools-cmake.yml --- expat-2.8.3/.github/workflows/autotools-cmake.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/autotools-cmake.yml 2026-10-05 15:43:59.000000000 +0000 @@ -67,17 +67,6 @@ -DCMAKE_EXE_LINKER_FLAGS=-Wl,--no-undefined-version -DCMAKE_MODULE_LINKER_FLAGS=-Wl,--no-undefined-version -DCMAKE_SHARED_LINKER_FLAGS=-Wl,--no-undefined-version - - os: ubuntu-24.04 - configure_args: >- - --enable-xml-attr-info - --enable-symbol-versioning - LDFLAGS=-Wl,--no-undefined-version - cmake_args: >- - -DEXPAT_ATTR_INFO=ON - -DEXPAT_SYMBOL_VERSIONING=ON - -DCMAKE_EXE_LINKER_FLAGS=-Wl,--no-undefined-version - -DCMAKE_MODULE_LINKER_FLAGS=-Wl,--no-undefined-version - -DCMAKE_SHARED_LINKER_FLAGS=-Wl,--no-undefined-version defaults: run: shell: bash @@ -91,7 +80,7 @@ if: "${{ runner.os == 'Linux' }}" run: |- set -x - sudo apt-get update + sudo timeout 10m apt-get update sudo timeout 10m apt-get install --yes --no-install-recommends -V \ cmake \ docbook2x \ @@ -102,9 +91,11 @@ if: "${{ runner.os == 'macOS' }}" run: | brew uninstall cmake + brew update --auto-update brew install \ autoconf \ automake \ + bzip3 \ cmake \ docbook2x \ libtool \ @@ -194,16 +185,11 @@ - name: (Linux except MinGW) Check for identical exported symbols from both build systems if: "${{ runner.os == 'Linux' && ! contains(matrix.configure_args, 'mingw') }}" env: - maximum: "${{ contains(matrix.configure_args, '--enable-xml-attr-info') && 'true' || 'false' }}" symbol_versioning: "${{ contains(matrix.configure_args, '--enable-symbol-versioning') && 'true' || 'false' }}" run: | list_shared_library_symbols_sh="${GITHUB_WORKSPACE}"/.github/workflows/scripts/list-shared-library-symbols.sh if ${symbol_versioning} ; then - if ${maximum} ; then - exported_symbols_txt="${GITHUB_WORKSPACE}"/.github/workflows/data/exported-symbols-versioned-maximum.txt - else - exported_symbols_txt="${GITHUB_WORKSPACE}"/.github/workflows/data/exported-symbols-versioned-default.txt - fi + exported_symbols_txt="${GITHUB_WORKSPACE}"/.github/workflows/data/exported-symbols-versioned.txt else exported_symbols_txt="${GITHUB_WORKSPACE}"/.github/workflows/data/exported-symbols-unversioned.txt fi diff -Nru expat-2.8.3/.github/workflows/clang-format.yml expat-2.9.0/.github/workflows/clang-format.yml --- expat-2.8.3/.github/workflows/clang-format.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/clang-format.yml 2026-10-05 15:43:59.000000000 +0000 @@ -50,17 +50,16 @@ with: persist-credentials: false - - name: Install clang-format 22 + - name: Install clang-format 23 run: |- set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-22 main" - sudo apt-get update # due to new repository + sudo timeout 10m add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-23 main" sudo timeout 10m apt-get install --yes --no-install-recommends -V \ - clang-format-22 \ + clang-format-23 \ moreutils - echo /usr/lib/llvm-22/bin >>"${GITHUB_PATH}" + echo /usr/lib/llvm-23/bin >>"${GITHUB_PATH}" - name: Run clang-format run: | diff -Nru expat-2.8.3/.github/workflows/clang-static-analyzer.yml expat-2.9.0/.github/workflows/clang-static-analyzer.yml --- expat-2.8.3/.github/workflows/clang-static-analyzer.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/clang-static-analyzer.yml 2026-10-05 15:43:59.000000000 +0000 @@ -49,17 +49,16 @@ with: persist-credentials: false - - name: Install Clang 22 (including scan-build) + - name: Install Clang 23 (including scan-build) run: |- set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-22 main" - sudo apt-get update # due to new repository + sudo timeout 10m add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-23 main" sudo timeout 10m apt-get install --yes --no-install-recommends -V \ - clang-22 \ - clang-tools-22 - echo /usr/lib/llvm-22/bin >>"${GITHUB_PATH}" + clang-23 \ + clang-tools-23 + echo /usr/lib/llvm-23/bin >>"${GITHUB_PATH}" - name: Build using scan-build run: | @@ -79,10 +78,12 @@ scan-build \ -o html/ \ + --exclude "${PWD}"/build/CMakeFiles/ \ + --exclude /usr/ \ $(sed 's,^,-enable-checker ,' <<<"${enabled_checkers}") \ sh -c 'cmake -S expat/ -B build/ && make -C build -j$(nproc) VERBOSE=1' - rmdir html || false 'The report directory is non-empty, i.e. Clang Static Analyzer found an issue! Please download and inspect the zip file artifact attached to this CI run. Thanks!' + ! ls -l html/*/report-*.html 2>/dev/null || false 'Clang Static Analyzer found an issue! Please download and inspect the zip file artifact attached to this CI run. Thanks!' - name: Store scan-build report if: always() diff -Nru expat-2.8.3/.github/workflows/clang-tidy.yml expat-2.9.0/.github/workflows/clang-tidy.yml --- expat-2.8.3/.github/workflows/clang-tidy.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/clang-tidy.yml 2026-10-05 15:43:59.000000000 +0000 @@ -50,16 +50,15 @@ with: persist-credentials: false - - name: Install clang-tidy 22 + - name: Install clang-tidy 23 run: |- set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-22 main" - sudo apt-get update # due to new repository + sudo timeout 10m add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-23 main" sudo timeout 10m apt-get install --yes --no-install-recommends -V \ - clang-tidy-22 - echo /usr/lib/llvm-22/bin >>"${GITHUB_PATH}" + clang-tidy-23 + echo /usr/lib/llvm-23/bin >>"${GITHUB_PATH}" - name: Run clang-tidy run: | diff -Nru expat-2.8.3/.github/workflows/coverage.yml expat-2.9.0/.github/workflows/coverage.yml --- expat-2.8.3/.github/workflows/coverage.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/coverage.yml 2026-10-05 15:43:59.000000000 +0000 @@ -61,7 +61,7 @@ # Unlock 32bit architecture (for Wine further down) sudo dpkg --add-architecture i386 # for wine32 - sudo apt-get update # due to new architecture + sudo timeout 10m apt-get update # due to new architecture sudo timeout 10m apt-get install --yes --no-install-recommends -V \ cmake \ diff -Nru expat-2.8.3/.github/workflows/coverity-scan.yml expat-2.9.0/.github/workflows/coverity-scan.yml --- expat-2.8.3/.github/workflows/coverity-scan.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/coverity-scan.yml 2026-10-05 15:43:59.000000000 +0000 @@ -65,18 +65,17 @@ tee "${GITHUB_OUTPUT}" <<< "version=$(git describe --tags | sed -e 's,^R_,,' -e 's,_,.,g')" tee -a "${GITHUB_OUTPUT}" <<< "git_commit=$(git rev-parse HEAD)" - - name: Install Clang 22 + - name: Install Clang 23 run: |- set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-22 main" - sudo apt-get update # due to new repository + sudo timeout 10m add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-23 main" sudo timeout 10m apt-get install --yes --no-install-recommends -V \ - clang-22 \ - libclang-rt-22-dev \ - llvm-22 - echo /usr/lib/llvm-22/bin >>"${GITHUB_PATH}" + clang-23 \ + libclang-rt-23-dev \ + llvm-23 + echo /usr/lib/llvm-23/bin >>"${GITHUB_PATH}" - name: Install build dependencies run: |- @@ -109,7 +108,7 @@ ) cmake "${args[@]}" -S . -B . - - uses: vapier/coverity-scan-action@2068473c7bdf8c2fb984a6a40ae76ee7facd7a85 # v1.8.0 + - uses: vapier/coverity-scan-action@1b6fd4eaba6651aa354c9ea7f48caa1710b4e12e # v1.9.0 with: email: ${{ secrets.COVERITY_SCAN_EMAIL }} token: ${{ secrets.COVERITY_SCAN_TOKEN }} diff -Nru expat-2.8.3/.github/workflows/cppcheck.yml expat-2.9.0/.github/workflows/cppcheck.yml --- expat-2.8.3/.github/workflows/cppcheck.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/cppcheck.yml 2026-10-05 15:43:59.000000000 +0000 @@ -53,7 +53,7 @@ - name: Build and install Cppcheck (less than 2 minutes) run: | - commit=e73bf44c3e49686b7495fab352d03a6c6075516b # 2.21.0 + commit=a436ca35ed1887bee789765122b65ed2d7a7e045 # 2.22.0 cmake_args=( -G Ninja # for faster builds @@ -80,6 +80,6 @@ [[ $(type -P cppcheck) == /usr/local/bin/cppcheck ]] cppcheck --version - - name: Run Cppcheck (less than 6 minutes) + - name: Run Cppcheck (less than 11 minutes) run: | exec .github/workflows/scripts/mass-cppcheck.sh diff -Nru expat-2.8.3/.github/workflows/data/expat_config_h_cmake__expected.txt expat-2.9.0/.github/workflows/data/expat_config_h_cmake__expected.txt --- expat-2.8.3/.github/workflows/data/expat_config_h_cmake__expected.txt 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/data/expat_config_h_cmake__expected.txt 2026-10-05 15:43:59.000000000 +0000 @@ -29,7 +29,6 @@ PACKAGE_VERSION STDC_HEADERS WORDS_BIGENDIAN -XML_ATTR_INFO XML_CONTEXT_BYTES XML_DEV_URANDOM XML_DTD diff -Nru expat-2.8.3/.github/workflows/data/expat_config_h_in__expected.txt expat-2.9.0/.github/workflows/data/expat_config_h_in__expected.txt --- expat-2.8.3/.github/workflows/data/expat_config_h_in__expected.txt 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/data/expat_config_h_in__expected.txt 2026-10-05 15:43:59.000000000 +0000 @@ -34,7 +34,6 @@ STDC_HEADERS VERSION WORDS_BIGENDIAN -XML_ATTR_INFO XML_CONTEXT_BYTES XML_DEV_URANDOM XML_DTD diff -Nru expat-2.8.3/.github/workflows/data/exported-symbols-unversioned.txt expat-2.9.0/.github/workflows/data/exported-symbols-unversioned.txt --- expat-2.8.3/.github/workflows/data/exported-symbols-unversioned.txt 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/data/exported-symbols-unversioned.txt 2026-10-05 15:43:59.000000000 +0000 @@ -7,14 +7,22 @@ XML_GetBase XML_GetBuffer XML_GetCurrentByteCount +XML_GetCurrentByteCount64 XML_GetCurrentByteIndex +XML_GetCurrentByteIndex64 XML_GetCurrentColumnNumber +XML_GetCurrentColumnNumber64 XML_GetCurrentLineNumber +XML_GetCurrentLineNumber64 XML_GetErrorCode XML_GetFeatureList XML_GetIdAttributeIndex XML_GetInputContext +XML_GetInputContext64 XML_GetParsingStatus +XML_GetPropertyBool +XML_GetPropertyDouble +XML_GetPropertyUInt64 XML_GetSpecifiedAttributeCount XML_MemFree XML_MemMalloc @@ -56,6 +64,9 @@ XML_SetNotStandaloneHandler XML_SetParamEntityParsing XML_SetProcessingInstructionHandler +XML_SetPropertyBool +XML_SetPropertyDouble +XML_SetPropertyUInt64 XML_SetReparseDeferralEnabled XML_SetReturnNSTriplet XML_SetSkippedEntityHandler diff -Nru expat-2.8.3/.github/workflows/data/exported-symbols-versioned-default.txt expat-2.9.0/.github/workflows/data/exported-symbols-versioned-default.txt --- expat-2.8.3/.github/workflows/data/exported-symbols-versioned-default.txt 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/data/exported-symbols-versioned-default.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -XML_DefaultCurrent@@LIBEXPAT_1.0.0 -XML_ErrorString@@LIBEXPAT_1.0.0 -XML_ExpatVersionInfo@@LIBEXPAT_1.95.3 -XML_ExpatVersion@@LIBEXPAT_1.95.0 -XML_ExternalEntityParserCreate@@LIBEXPAT_1.0.0 -XML_FreeContentModel@@LIBEXPAT_1.95.6 -XML_GetBase@@LIBEXPAT_1.0.0 -XML_GetBuffer@@LIBEXPAT_1.0.0 -XML_GetCurrentByteCount@@LIBEXPAT_1.1.0 -XML_GetCurrentByteIndex@@LIBEXPAT_1.0.0 -XML_GetCurrentColumnNumber@@LIBEXPAT_1.0.0 -XML_GetCurrentLineNumber@@LIBEXPAT_1.0.0 -XML_GetErrorCode@@LIBEXPAT_1.0.0 -XML_GetFeatureList@@LIBEXPAT_1.95.5 -XML_GetIdAttributeIndex@@LIBEXPAT_1.95.0 -XML_GetInputContext@@LIBEXPAT_1.95.0 -XML_GetParsingStatus@@LIBEXPAT_1.95.8 -XML_GetSpecifiedAttributeCount@@LIBEXPAT_1.1.0 -XML_MemFree@@LIBEXPAT_1.95.6 -XML_MemMalloc@@LIBEXPAT_1.95.6 -XML_MemRealloc@@LIBEXPAT_1.95.6 -XML_ParseBuffer@@LIBEXPAT_1.0.0 -XML_Parse@@LIBEXPAT_1.0.0 -XML_ParserCreate@@LIBEXPAT_1.0.0 -XML_ParserCreate_MM@@LIBEXPAT_1.95.0 -XML_ParserCreateNS@@LIBEXPAT_1.1.0 -XML_ParserFree@@LIBEXPAT_1.0.0 -XML_ParserReset@@LIBEXPAT_1.95.3 -XML_ResumeParser@@LIBEXPAT_1.95.8 -XML_SetAllocTrackerActivationThreshold@@LIBEXPAT_2.7.2 -XML_SetAllocTrackerMaximumAmplification@@LIBEXPAT_2.7.2 -XML_SetAttlistDeclHandler@@LIBEXPAT_1.95.0 -XML_SetBase@@LIBEXPAT_1.0.0 -XML_SetBillionLaughsAttackProtectionActivationThreshold@@LIBEXPAT_2.4.0 -XML_SetBillionLaughsAttackProtectionMaximumAmplification@@LIBEXPAT_2.4.0 -XML_SetCdataSectionHandler@@LIBEXPAT_1.1.0 -XML_SetCharacterDataHandler@@LIBEXPAT_1.0.0 -XML_SetCommentHandler@@LIBEXPAT_1.1.0 -XML_SetDefaultHandlerExpand@@LIBEXPAT_1.1.0 -XML_SetDefaultHandler@@LIBEXPAT_1.0.0 -XML_SetDoctypeDeclHandler@@LIBEXPAT_1.95.0 -XML_SetElementDeclHandler@@LIBEXPAT_1.95.0 -XML_SetElementHandler@@LIBEXPAT_1.0.0 -XML_SetEncoding@@LIBEXPAT_1.1.0 -XML_SetEndCdataSectionHandler@@LIBEXPAT_1.95.0 -XML_SetEndDoctypeDeclHandler@@LIBEXPAT_1.95.0 -XML_SetEndElementHandler@@LIBEXPAT_1.95.0 -XML_SetEndNamespaceDeclHandler@@LIBEXPAT_1.95.0 -XML_SetEntityDeclHandler@@LIBEXPAT_1.95.0 -XML_SetExternalEntityRefHandlerArg@@LIBEXPAT_1.1.0 -XML_SetExternalEntityRefHandler@@LIBEXPAT_1.0.0 -XML_SetHashSalt16Bytes@@LIBEXPAT_2.8.0 -XML_SetHashSalt@@LIBEXPAT_2.1.0 -XML_SetNamespaceDeclHandler@@LIBEXPAT_1.1.0 -XML_SetNotationDeclHandler@@LIBEXPAT_1.0.0 -XML_SetNotStandaloneHandler@@LIBEXPAT_1.1.0 -XML_SetParamEntityParsing@@LIBEXPAT_1.95.0 -XML_SetProcessingInstructionHandler@@LIBEXPAT_1.0.0 -XML_SetReparseDeferralEnabled@@LIBEXPAT_2.6.0 -XML_SetReturnNSTriplet@@LIBEXPAT_1.95.0 -XML_SetSkippedEntityHandler@@LIBEXPAT_1.95.4 -XML_SetStartCdataSectionHandler@@LIBEXPAT_1.95.0 -XML_SetStartDoctypeDeclHandler@@LIBEXPAT_1.95.0 -XML_SetStartElementHandler@@LIBEXPAT_1.95.0 -XML_SetStartNamespaceDeclHandler@@LIBEXPAT_1.95.0 -XML_SetUnknownEncodingHandler@@LIBEXPAT_1.0.0 -XML_SetUnparsedEntityDeclHandler@@LIBEXPAT_1.0.0 -XML_SetUserData@@LIBEXPAT_1.0.0 -XML_SetXmlDeclHandler@@LIBEXPAT_1.95.0 -XML_StopParser@@LIBEXPAT_1.95.8 -XML_UseForeignDTD@@LIBEXPAT_1.95.5 -XML_UseParserAsHandlerArg@@LIBEXPAT_1.0.0 diff -Nru expat-2.8.3/.github/workflows/data/exported-symbols-versioned-maximum.txt expat-2.9.0/.github/workflows/data/exported-symbols-versioned-maximum.txt --- expat-2.8.3/.github/workflows/data/exported-symbols-versioned-maximum.txt 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/data/exported-symbols-versioned-maximum.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -XML_DefaultCurrent@@LIBEXPAT_1.0.0 -XML_ErrorString@@LIBEXPAT_1.0.0 -XML_ExpatVersionInfo@@LIBEXPAT_1.95.3 -XML_ExpatVersion@@LIBEXPAT_1.95.0 -XML_ExternalEntityParserCreate@@LIBEXPAT_1.0.0 -XML_FreeContentModel@@LIBEXPAT_1.95.6 -XML_GetAttributeInfo@@LIBEXPAT_2.1.0 -XML_GetBase@@LIBEXPAT_1.0.0 -XML_GetBuffer@@LIBEXPAT_1.0.0 -XML_GetCurrentByteCount@@LIBEXPAT_1.1.0 -XML_GetCurrentByteIndex@@LIBEXPAT_1.0.0 -XML_GetCurrentColumnNumber@@LIBEXPAT_1.0.0 -XML_GetCurrentLineNumber@@LIBEXPAT_1.0.0 -XML_GetErrorCode@@LIBEXPAT_1.0.0 -XML_GetFeatureList@@LIBEXPAT_1.95.5 -XML_GetIdAttributeIndex@@LIBEXPAT_1.95.0 -XML_GetInputContext@@LIBEXPAT_1.95.0 -XML_GetParsingStatus@@LIBEXPAT_1.95.8 -XML_GetSpecifiedAttributeCount@@LIBEXPAT_1.1.0 -XML_MemFree@@LIBEXPAT_1.95.6 -XML_MemMalloc@@LIBEXPAT_1.95.6 -XML_MemRealloc@@LIBEXPAT_1.95.6 -XML_ParseBuffer@@LIBEXPAT_1.0.0 -XML_Parse@@LIBEXPAT_1.0.0 -XML_ParserCreate@@LIBEXPAT_1.0.0 -XML_ParserCreate_MM@@LIBEXPAT_1.95.0 -XML_ParserCreateNS@@LIBEXPAT_1.1.0 -XML_ParserFree@@LIBEXPAT_1.0.0 -XML_ParserReset@@LIBEXPAT_1.95.3 -XML_ResumeParser@@LIBEXPAT_1.95.8 -XML_SetAllocTrackerActivationThreshold@@LIBEXPAT_2.7.2 -XML_SetAllocTrackerMaximumAmplification@@LIBEXPAT_2.7.2 -XML_SetAttlistDeclHandler@@LIBEXPAT_1.95.0 -XML_SetBase@@LIBEXPAT_1.0.0 -XML_SetBillionLaughsAttackProtectionActivationThreshold@@LIBEXPAT_2.4.0 -XML_SetBillionLaughsAttackProtectionMaximumAmplification@@LIBEXPAT_2.4.0 -XML_SetCdataSectionHandler@@LIBEXPAT_1.1.0 -XML_SetCharacterDataHandler@@LIBEXPAT_1.0.0 -XML_SetCommentHandler@@LIBEXPAT_1.1.0 -XML_SetDefaultHandlerExpand@@LIBEXPAT_1.1.0 -XML_SetDefaultHandler@@LIBEXPAT_1.0.0 -XML_SetDoctypeDeclHandler@@LIBEXPAT_1.95.0 -XML_SetElementDeclHandler@@LIBEXPAT_1.95.0 -XML_SetElementHandler@@LIBEXPAT_1.0.0 -XML_SetEncoding@@LIBEXPAT_1.1.0 -XML_SetEndCdataSectionHandler@@LIBEXPAT_1.95.0 -XML_SetEndDoctypeDeclHandler@@LIBEXPAT_1.95.0 -XML_SetEndElementHandler@@LIBEXPAT_1.95.0 -XML_SetEndNamespaceDeclHandler@@LIBEXPAT_1.95.0 -XML_SetEntityDeclHandler@@LIBEXPAT_1.95.0 -XML_SetExternalEntityRefHandlerArg@@LIBEXPAT_1.1.0 -XML_SetExternalEntityRefHandler@@LIBEXPAT_1.0.0 -XML_SetHashSalt16Bytes@@LIBEXPAT_2.8.0 -XML_SetHashSalt@@LIBEXPAT_2.1.0 -XML_SetNamespaceDeclHandler@@LIBEXPAT_1.1.0 -XML_SetNotationDeclHandler@@LIBEXPAT_1.0.0 -XML_SetNotStandaloneHandler@@LIBEXPAT_1.1.0 -XML_SetParamEntityParsing@@LIBEXPAT_1.95.0 -XML_SetProcessingInstructionHandler@@LIBEXPAT_1.0.0 -XML_SetReparseDeferralEnabled@@LIBEXPAT_2.6.0 -XML_SetReturnNSTriplet@@LIBEXPAT_1.95.0 -XML_SetSkippedEntityHandler@@LIBEXPAT_1.95.4 -XML_SetStartCdataSectionHandler@@LIBEXPAT_1.95.0 -XML_SetStartDoctypeDeclHandler@@LIBEXPAT_1.95.0 -XML_SetStartElementHandler@@LIBEXPAT_1.95.0 -XML_SetStartNamespaceDeclHandler@@LIBEXPAT_1.95.0 -XML_SetUnknownEncodingHandler@@LIBEXPAT_1.0.0 -XML_SetUnparsedEntityDeclHandler@@LIBEXPAT_1.0.0 -XML_SetUserData@@LIBEXPAT_1.0.0 -XML_SetXmlDeclHandler@@LIBEXPAT_1.95.0 -XML_StopParser@@LIBEXPAT_1.95.8 -XML_UseForeignDTD@@LIBEXPAT_1.95.5 -XML_UseParserAsHandlerArg@@LIBEXPAT_1.0.0 diff -Nru expat-2.8.3/.github/workflows/data/exported-symbols-versioned.txt expat-2.9.0/.github/workflows/data/exported-symbols-versioned.txt --- expat-2.8.3/.github/workflows/data/exported-symbols-versioned.txt 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/.github/workflows/data/exported-symbols-versioned.txt 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,83 @@ +XML_DefaultCurrent@@LIBEXPAT_1.0.0 +XML_ErrorString@@LIBEXPAT_1.0.0 +XML_ExpatVersionInfo@@LIBEXPAT_1.95.3 +XML_ExpatVersion@@LIBEXPAT_1.95.0 +XML_ExternalEntityParserCreate@@LIBEXPAT_1.0.0 +XML_FreeContentModel@@LIBEXPAT_1.95.6 +XML_GetBase@@LIBEXPAT_1.0.0 +XML_GetBuffer@@LIBEXPAT_1.0.0 +XML_GetCurrentByteCount64@@LIBEXPAT_2.9.0 +XML_GetCurrentByteCount@@LIBEXPAT_1.1.0 +XML_GetCurrentByteIndex64@@LIBEXPAT_2.9.0 +XML_GetCurrentByteIndex@@LIBEXPAT_1.0.0 +XML_GetCurrentColumnNumber64@@LIBEXPAT_2.9.0 +XML_GetCurrentColumnNumber@@LIBEXPAT_1.0.0 +XML_GetCurrentLineNumber64@@LIBEXPAT_2.9.0 +XML_GetCurrentLineNumber@@LIBEXPAT_1.0.0 +XML_GetErrorCode@@LIBEXPAT_1.0.0 +XML_GetFeatureList@@LIBEXPAT_1.95.5 +XML_GetIdAttributeIndex@@LIBEXPAT_1.95.0 +XML_GetInputContext64@@LIBEXPAT_2.9.0 +XML_GetInputContext@@LIBEXPAT_1.95.0 +XML_GetParsingStatus@@LIBEXPAT_1.95.8 +XML_GetPropertyBool@@LIBEXPAT_2.9.0 +XML_GetPropertyDouble@@LIBEXPAT_2.9.0 +XML_GetPropertyUInt64@@LIBEXPAT_2.9.0 +XML_GetSpecifiedAttributeCount@@LIBEXPAT_1.1.0 +XML_MemFree@@LIBEXPAT_1.95.6 +XML_MemMalloc@@LIBEXPAT_1.95.6 +XML_MemRealloc@@LIBEXPAT_1.95.6 +XML_ParseBuffer@@LIBEXPAT_1.0.0 +XML_Parse@@LIBEXPAT_1.0.0 +XML_ParserCreate@@LIBEXPAT_1.0.0 +XML_ParserCreate_MM@@LIBEXPAT_1.95.0 +XML_ParserCreateNS@@LIBEXPAT_1.1.0 +XML_ParserFree@@LIBEXPAT_1.0.0 +XML_ParserReset@@LIBEXPAT_1.95.3 +XML_ResumeParser@@LIBEXPAT_1.95.8 +XML_SetAllocTrackerActivationThreshold@@LIBEXPAT_2.7.2 +XML_SetAllocTrackerMaximumAmplification@@LIBEXPAT_2.7.2 +XML_SetAttlistDeclHandler@@LIBEXPAT_1.95.0 +XML_SetBase@@LIBEXPAT_1.0.0 +XML_SetBillionLaughsAttackProtectionActivationThreshold@@LIBEXPAT_2.4.0 +XML_SetBillionLaughsAttackProtectionMaximumAmplification@@LIBEXPAT_2.4.0 +XML_SetCdataSectionHandler@@LIBEXPAT_1.1.0 +XML_SetCharacterDataHandler@@LIBEXPAT_1.0.0 +XML_SetCommentHandler@@LIBEXPAT_1.1.0 +XML_SetDefaultHandlerExpand@@LIBEXPAT_1.1.0 +XML_SetDefaultHandler@@LIBEXPAT_1.0.0 +XML_SetDoctypeDeclHandler@@LIBEXPAT_1.95.0 +XML_SetElementDeclHandler@@LIBEXPAT_1.95.0 +XML_SetElementHandler@@LIBEXPAT_1.0.0 +XML_SetEncoding@@LIBEXPAT_1.1.0 +XML_SetEndCdataSectionHandler@@LIBEXPAT_1.95.0 +XML_SetEndDoctypeDeclHandler@@LIBEXPAT_1.95.0 +XML_SetEndElementHandler@@LIBEXPAT_1.95.0 +XML_SetEndNamespaceDeclHandler@@LIBEXPAT_1.95.0 +XML_SetEntityDeclHandler@@LIBEXPAT_1.95.0 +XML_SetExternalEntityRefHandlerArg@@LIBEXPAT_1.1.0 +XML_SetExternalEntityRefHandler@@LIBEXPAT_1.0.0 +XML_SetHashSalt16Bytes@@LIBEXPAT_2.8.0 +XML_SetHashSalt@@LIBEXPAT_2.1.0 +XML_SetNamespaceDeclHandler@@LIBEXPAT_1.1.0 +XML_SetNotationDeclHandler@@LIBEXPAT_1.0.0 +XML_SetNotStandaloneHandler@@LIBEXPAT_1.1.0 +XML_SetParamEntityParsing@@LIBEXPAT_1.95.0 +XML_SetProcessingInstructionHandler@@LIBEXPAT_1.0.0 +XML_SetPropertyBool@@LIBEXPAT_2.9.0 +XML_SetPropertyDouble@@LIBEXPAT_2.9.0 +XML_SetPropertyUInt64@@LIBEXPAT_2.9.0 +XML_SetReparseDeferralEnabled@@LIBEXPAT_2.6.0 +XML_SetReturnNSTriplet@@LIBEXPAT_1.95.0 +XML_SetSkippedEntityHandler@@LIBEXPAT_1.95.4 +XML_SetStartCdataSectionHandler@@LIBEXPAT_1.95.0 +XML_SetStartDoctypeDeclHandler@@LIBEXPAT_1.95.0 +XML_SetStartElementHandler@@LIBEXPAT_1.95.0 +XML_SetStartNamespaceDeclHandler@@LIBEXPAT_1.95.0 +XML_SetUnknownEncodingHandler@@LIBEXPAT_1.0.0 +XML_SetUnparsedEntityDeclHandler@@LIBEXPAT_1.0.0 +XML_SetUserData@@LIBEXPAT_1.0.0 +XML_SetXmlDeclHandler@@LIBEXPAT_1.95.0 +XML_StopParser@@LIBEXPAT_1.95.8 +XML_UseForeignDTD@@LIBEXPAT_1.95.5 +XML_UseParserAsHandlerArg@@LIBEXPAT_1.0.0 diff -Nru expat-2.8.3/.github/workflows/emscripten.yml expat-2.9.0/.github/workflows/emscripten.yml --- expat-2.8.3/.github/workflows/emscripten.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/emscripten.yml 2026-10-05 15:43:59.000000000 +0000 @@ -52,7 +52,7 @@ - name: Install build dependencies run: |- set -x - sudo apt-get update + sudo timeout 10m apt-get update sudo timeout 10m apt-get install --yes --no-install-recommends -V \ cmake \ emscripten \ diff -Nru expat-2.8.3/.github/workflows/expat_config_h.yml expat-2.9.0/.github/workflows/expat_config_h.yml --- expat-2.8.3/.github/workflows/expat_config_h.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/expat_config_h.yml 2026-10-05 15:43:59.000000000 +0000 @@ -65,3 +65,7 @@ <(sort < .github/workflows/data/expat_config_h_in__expected.txt) \ <(sort < .github/workflows/data/expat_config_h_cmake__expected.txt) \ || true + + - name: Check for missing expat_config.h includes + run: | + exec .github/workflows/scripts/check_for_missing_config_header_includes.sh diff -Nru expat-2.8.3/.github/workflows/fil-c.yml expat-2.9.0/.github/workflows/fil-c.yml --- expat-2.8.3/.github/workflows/fil-c.yml 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/.github/workflows/fil-c.yml 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,81 @@ +# __ __ _ +# ___\ \/ /_ __ __ _| |_ +# / _ \\ /| '_ \ / _` | __| +# | __// \| |_) | (_| | |_ +# \___/_/\_\ .__/ \__,_|\__| +# |_| XML parser +# +# Copyright (c) 2026 Sebastian Pipping +# Licensed under the MIT license: +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + +name: Build and test with Fil-C + +on: + pull_request: + push: + schedule: + - cron: '0 2 * * 5' # Every Friday at 2am + workflow_dispatch: + +permissions: + contents: read + +jobs: + fil_c: + name: Build and test with Fil-C + timeout-minutes: 15 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install Fil-C toolchain + run: |- + version=0.686 # of 2026-10-05 + sha256sum=60bfbe8ee63d7e462394aa8d5e44fee675de892bcf800d9cc80d86378cad6b07 + + dirname="filc-${version}-linux-$(uname -m)" + tarball="${dirname}.tar.xz" + set -x + wget --progress=dot:giga "https://github.com/pizlonator/fil-c/releases/download/v${version}/${tarball}" + sha256sum --check <<<"${sha256sum} ${dirname}.tar.xz" + tar xf "${tarball}" + ( cd "${dirname}" && ./setup.sh ) + echo "${PWD}/${dirname}/build/bin" >>"${GITHUB_PATH}" + + - name: Build using Fil-C + run: | + cmake_args=( + -DCMAKE_C_COMPILER=filcc + -DCMAKE_CXX_COMPILER=filc++ + -DCMAKE_{C,CXX}_FLAGS='-march=native -O2 -pipe -Wall -Wextra -pedantic -Wno-overlength-strings' + -DEXPAT_BUILD_DOCS=OFF + -DEXPAT_WARNINGS_AS_ERRORS=ON + ) + set -x + cmake "${cmake_args[@]}" -S expat/ -B build/ + make -C build -j$(nproc) VERBOSE=1 all + + - name: Run the test suite + run: | + make -C build/ test run-xmltest diff -Nru expat-2.8.3/.github/workflows/freebsd.yml expat-2.9.0/.github/workflows/freebsd.yml --- expat-2.8.3/.github/workflows/freebsd.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/freebsd.yml 2026-10-05 15:43:59.000000000 +0000 @@ -50,7 +50,7 @@ with: persist-credentials: false - - uses: vmactions/freebsd-vm@77ed28d336d03fe19a3f4f7266c1d2c4714dd79d # v1.5.2 + - uses: vmactions/freebsd-vm@a2f9a41fa97f6848b8c3b791087dfcdaa5b473ff # v1.5.8 with: release: "15.0" usesh: true diff -Nru expat-2.8.3/.github/workflows/fuzzing.yml expat-2.9.0/.github/workflows/fuzzing.yml --- expat-2.8.3/.github/workflows/fuzzing.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/fuzzing.yml 2026-10-05 15:43:59.000000000 +0000 @@ -58,18 +58,17 @@ with: persist-credentials: false - - name: Install Clang 22 + - name: Install Clang 23 run: |- set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-22 main" - sudo apt-get update # due to new repository + sudo timeout 10m add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-23 main" sudo timeout 10m apt-get install --yes --no-install-recommends -V \ - clang-22 \ - libclang-rt-22-dev \ - llvm-22 - echo /usr/lib/llvm-22/bin >>"${GITHUB_PATH}" + clang-23 \ + libclang-rt-23-dev \ + llvm-23 + echo /usr/lib/llvm-23/bin >>"${GITHUB_PATH}" - name: Install build dependencies run: |- diff -Nru expat-2.8.3/.github/workflows/linux.yml expat-2.9.0/.github/workflows/linux.yml --- expat-2.8.3/.github/workflows/linux.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/linux.yml 2026-10-05 15:43:59.000000000 +0000 @@ -57,8 +57,6 @@ - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address - MODE: qa-sh - FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS=-DEXPAT_ATTR_INFO=ON - - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS=-DEXPAT_CONTEXT_BYTES=0 - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS="-DEXPAT_DTD=OFF -DEXPAT_GE=ON" @@ -67,8 +65,6 @@ - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS=-DEXPAT_LARGE_SIZE=ON - MODE: qa-sh - FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS=-DEXPAT_MIN_SIZE=ON - - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS=-DEXPAT_NS=OFF - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=cfi @@ -81,11 +77,9 @@ - MODE: qa-sh FLAT_ENV: CC=gcc CXX=g++ LD=ld QA_PROCESSOR=gcov CMAKE_ARGS=-D_EXPAT_M32=ON - MODE: qa-sh - FLAT_ENV: CC=gcc CXX=g++ LD=ld QA_PROCESSOR=gcov CMAKE_ARGS="-D_EXPAT_M32=ON -DEXPAT_ATTR_INFO=ON" + FLAT_ENV: CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ LD=i686-w64-mingw32-ld QA_PROCESSOR=gcov CMAKE_ARGS="-DCMAKE_SYSTEM_NAME=Windows -DWIN32=ON -DMINGW=ON" - MODE: qa-sh - FLAT_ENV: CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ LD=i686-w64-mingw32-ld QA_PROCESSOR=gcov CMAKE_ARGS="-DCMAKE_SYSTEM_NAME=Windows -DWIN32=ON -DMINGW=ON -DEXPAT_ATTR_INFO=ON" - - MODE: qa-sh - FLAT_ENV: CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ LD=i686-w64-mingw32-ld QA_PROCESSOR=gcov CMAKE_ARGS="-DCMAKE_SYSTEM_NAME=Windows -DWIN32=ON -DMINGW=ON -DEXPAT_ATTR_INFO=ON -DEXPAT_CHAR_TYPE=wchar_t" + FLAT_ENV: CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ LD=i686-w64-mingw32-ld QA_PROCESSOR=gcov CMAKE_ARGS="-DCMAKE_SYSTEM_NAME=Windows -DWIN32=ON -DMINGW=ON -DEXPAT_CHAR_TYPE=wchar_t" runs-on: ubuntu-24.04 env: CFLAGS: -g3 -pipe @@ -101,7 +95,7 @@ # Install 32bit Wine sudo dpkg --add-architecture i386 # for wine32 - sudo apt-get update # due to new architecture + sudo timeout 10m apt-get update # due to new architecture sudo timeout 10m apt-get install --yes --no-install-recommends -V \ mingw-w64 \ retry \ @@ -114,13 +108,12 @@ set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-22 main" - sudo apt-get update # due to new repository - # NOTE: Please note the version-specific ${PATH} extension for Clang adding /usr/lib/llvm-22/bin in .ci.sh + sudo timeout 10m add-apt-repository "deb https://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-23 main" + # NOTE: Please note the version-specific ${PATH} extension for Clang adding /usr/lib/llvm-23/bin in .ci.sh sudo timeout 10m apt-get install --yes --no-install-recommends -V \ - clang-22 \ - libclang-rt-22-dev \ - llvm-22 + clang-23 \ + libclang-rt-23-dev \ + llvm-23 - name: Install build dependencies (common) run: |- @@ -136,5 +129,11 @@ - name: Perform check "${{ matrix.MODE }}" env: MODE: ${{ matrix.MODE }} + drop_warning: "${{ contains(matrix.FLAT_ENV, '-DEXPAT_LARGE_SIZE=ON') && 'true' || 'false' }}" run: | + if ${drop_warning} ; then + sed 's|^# *warning|// &|' -i.bak expat/lib/expat_external.h + rm expat/lib/expat_external.h.bak + fi + env ${{ matrix.FLAT_ENV }} ./.ci.sh diff -Nru expat-2.8.3/.github/workflows/mingw-clang.yml expat-2.9.0/.github/workflows/mingw-clang.yml --- expat-2.8.3/.github/workflows/mingw-clang.yml 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/.github/workflows/mingw-clang.yml 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,173 @@ +# __ __ _ +# ___\ \/ /_ __ __ _| |_ +# / _ \\ /| '_ \ / _` | __| +# | __// \| |_) | (_| | |_ +# \___/_/\_\ .__/ \__,_|\__| +# |_| XML parser +# +# Copyright (c) 2026 Sebastian Pipping +# Copyright (c) 2026 Jeff McJunkin +# Licensed under the MIT license: +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + +name: Compile and test with Clang-based MinGW + +on: + pull_request: + push: + schedule: + - cron: '0 2 * * 5' # Every Friday at 2am + workflow_dispatch: + +permissions: + contents: read + +jobs: + mingw_clang: + name: Compile and test with Clang-based MinGW (${{ matrix.mingw_arch }}, ${{ matrix.name }}) + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + include: + - name: char + cmake_args: -DEXPAT_CHAR_TYPE=char + mingw_arch: i686 + wine_arch: win32 + wine_packages: wine32:i386 wine32-preloader:i386 + - name: wchar_t + cmake_args: -DEXPAT_CHAR_TYPE=wchar_t + mingw_arch: x86_64 + wine_arch: win64 + wine_package: wine64 wine64-preloader + runs-on: ubuntu-26.04 + container: + # We use Debian trixie here because combination "Clang MinGW ASan Wine" + # is broken on Ubuntu. + image: debian:trixie-slim + env: + # Job containers run as root, but the workspace bind mount is runner-owned. + # Wine rejects a prefix below a directory owned by another user. + WINEPREFIX: /tmp/libexpat-wineprefix + # This avoids + # error: XDG_RUNTIME_DIR is invalid or not set in the environment. + # from Wine from inside the container. + XDG_RUNTIME_DIR: /run/user/0 + # This unblocks 32bit wineserver; without it, we would get this error: + # sock_init: ERROR in sock_check_pollhup() + # wineserver: socket: Function not implemented + options: >- + --security-opt apparmor=unconfined + --security-opt seccomp=unconfined + defaults: + run: + shell: "bash -e {0}" # because the default is only POSIX shell + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install build dependencies (everything but MinGW) + env: + DEBIAN_FRONTEND: noninteractive + wine_packages: ${{ matrix.wine_packages }} + run: |- + set -x + if [[ ${wine_packages} =~ :i386 ]]; then + dpkg --add-architecture i386 + fi + timeout 10m apt-get update + timeout 10m apt-get install --yes --no-install-recommends -V \ + ca-certificates \ + cmake \ + make \ + python3 \ + retry \ + unzip \ + wget \ + wine \ + ${wine_packages} \ + xz-utils + + - name: Install build dependencies (Clang-based MinGW toolchain) + run: |- + version=20260922 # with LLVM/Clang 23.1.2 + sha256sum=bb7bb7654b33d5aa8712acb837c963b2e0c56352560c76105270a3268c665c21 + + dirname="llvm-mingw-${version}-ucrt-ubuntu-22.04-x86_64" + tarball="${dirname}.tar.xz" + set -x + wget \ + --progress=dot:giga \ + "https://github.com/mstorsjo/llvm-mingw/releases/download/${version}/${tarball}" + sha256sum --check <<<"${sha256sum} ${tarball}" + tar xf "${tarball}" + echo "${PWD}/${dirname}/bin" >>"${GITHUB_PATH}" + + - name: Configure + env: + cmake_args: ${{ matrix.cmake_args }} + wine_arch: ${{ matrix.wine_arch }} + run: |- + set -u + args=( + -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-toolchain-${wine_arch}.cmake + + -DCMAKE_{C,CXX}_FLAGS='-Wall -Wextra -pedantic -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer' + -DCMAKE_{EXE,MODULE,SHARED}_LINKER_FLAGS='-g -fsanitize=address,undefined' + + -DEXPAT_BUILD_DOCS=OFF + -DEXPAT_BUILD_TESTS=ON + -DEXPAT_WARNINGS_AS_ERRORS=ON + + ${cmake_args} + ) + set -x + cmake "${args[@]}" -S expat/ -B build/ + + - name: Build + run: |- + set -x + make -C build -j$(nproc) VERBOSE=1 + + - name: Run unit tests (takes about 3 minutes) + env: + retry: retry --times 5 -- # mitigate Wine crashing randomly in CI + mingw_arch: ${{ matrix.mingw_arch }} + WINEDEBUG: -all # to reduce noise + run: |- + set -x + + # Make Wine find the needed DLL files: + # - libc++.dll + # - libclang_rt.asan_dynamic-*.dll + # - libexpat*.dll + # - libunwind.dll + export WINEPATH="$(winepath -w build);$(winepath -w llvm-mingw-*-ucrt-ubuntu-*/${mingw_arch}-w64-mingw32/bin)" + + make -C build CTEST_OUTPUT_ON_FAILURE=1 test + + # Bypass `run-xmltest` on 64bit because it takes forever, + # needs more debugging + if [[ ${mingw_arch} != x86_64 ]]; then + ${retry} make -C build run-xmltest + fi diff -Nru expat-2.8.3/.github/workflows/musl.yml expat-2.9.0/.github/workflows/musl.yml --- expat-2.8.3/.github/workflows/musl.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/musl.yml 2026-10-05 15:43:59.000000000 +0000 @@ -52,7 +52,7 @@ - name: Install build dependencies run: |- set -x - sudo apt-get update + sudo timeout 10m apt-get update sudo timeout 10m apt-get install --yes --no-install-recommends -V \ cmake \ musl-tools @@ -61,7 +61,7 @@ run: | cmake_args=( -DCMAKE_C_COMPILER=musl-gcc - -DCMAKE_CXX_COMPILER=g++ # musl-tools does not support C++ + -DCMAKE_CXX_COMPILER=false # musl-tools does not support C++ -DCMAKE_C_FLAGS='-O1 -pipe -Wall -Wextra -pedantic -Wno-overlength-strings' -DCMAKE_CXX_FLAGS='-O0 -pipe' -DEXPAT_BUILD_DOCS=OFF @@ -70,3 +70,7 @@ set -x cmake "${cmake_args[@]}" -S expat/ -B build/ make -C build -j$(nproc) VERBOSE=1 all + + - name: Run the test suite + run: | + make -C build/ test run-xmltest diff -Nru expat-2.8.3/.github/workflows/perl-integration.yml expat-2.9.0/.github/workflows/perl-integration.yml --- expat-2.8.3/.github/workflows/perl-integration.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/perl-integration.yml 2026-10-05 15:43:59.000000000 +0000 @@ -59,7 +59,7 @@ if: "${{ matrix.xml_parser_commit != '7096c4e45a62a23837382cf822d8c2dab780a3a1' }}" run: | set -x - sudo apt-get update + sudo timeout 10m apt-get update sudo timeout 10m apt-get install --yes --no-install-recommends -V \ libfile-sharedir-install-perl \ libfile-sharedir-perl diff -Nru expat-2.8.3/.github/workflows/qemu.yml expat-2.9.0/.github/workflows/qemu.yml --- expat-2.8.3/.github/workflows/qemu.yml 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/.github/workflows/qemu.yml 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,97 @@ +# __ __ _ +# ___\ \/ /_ __ __ _| |_ +# / _ \\ /| '_ \ / _` | __| +# | __// \| |_) | (_| | |_ +# \___/_/\_\ .__/ \__,_|\__| +# |_| XML parser +# +# Copyright (c) 2026 Matteo Forzan +# Licensed under the MIT license: +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + +name: Build and test with QEMU + +on: + pull_request: + push: + schedule: + - cron: '0 2 * * 5' # Every Friday at 2am + workflow_dispatch: + +permissions: + contents: read + +jobs: + qemu: + name: Build and test on ${{ matrix.platform }} (emulated) + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + platform: + - riscv64 # little-endian + - s390x # big-endian + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + with: + platforms: ${{ matrix.platform }} + + - name: Build and run the test suite on ${{ matrix.platform }} in a container + env: + platform: ${{ matrix.platform }} + run: | + docker run \ + --platform "linux/${platform}" \ + --rm \ + --volume "${PWD}:/workspace" \ + --workdir /workspace \ + debian:trixie-slim \ + bash -c ' + set -e -u -x + + uname -m + + timeout 10m apt-get update + timeout 10m apt-get install --yes --no-install-recommends -V \ + ca-certificates \ + cmake \ + g++ \ + gcc \ + make \ + python3 \ + unzip \ + wget + + cmake_args=( + -DCMAKE_{C,CXX}_FLAGS="-O1 -pipe -Wall -Wextra -pedantic -Wno-overlength-strings" + -DEXPAT_BUILD_DOCS=OFF + -DEXPAT_WARNINGS_AS_ERRORS=ON + ) + cmake "${cmake_args[@]}" -S expat/ -B build/ + make -C build -j$(nproc) VERBOSE=1 all + + make -C build/ test run-xmltest + ' diff -Nru expat-2.8.3/.github/workflows/scripts/check_for_missing_config_header_includes.sh expat-2.9.0/.github/workflows/scripts/check_for_missing_config_header_includes.sh --- expat-2.8.3/.github/workflows/scripts/check_for_missing_config_header_includes.sh 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/.github/workflows/scripts/check_for_missing_config_header_includes.sh 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,64 @@ +#! /usr/bin/env bash +# __ __ _ +# ___\ \/ /_ __ __ _| |_ +# / _ \\ /| '_ \ / _` | __| +# | __// \| |_) | (_| | |_ +# \___/_/\_\ .__/ \__,_|\__| +# |_| XML parser +# +# Copyright (c) 2026 Sebastian Pipping +# Licensed under the MIT license: +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + +set -e -u -o pipefail + +macros="$(cat .github/workflows/data/expat_config_h_cmake__expected.txt \ + .github/workflows/data/expat_config_h_in__expected.txt \ + | sort -u \ + | grep -v \ + -e __func__ \ + -e const)" +pattern=$(echo -n "${macros}" | tr '\n' '|') + +# NOTE: Making expat.h include expat_config.h now would pull all those macros into user code +# and potentially break things for them +# The internal headers are fine without an include to expat_config.h, as long as the +# file including them includes expat_config.h prior +files_expected="$(git grep -l -w -E "${pattern}" -- expat/lib/ expat/tests/ expat/xmlwf/ \ + | grep -v -F \ + -e expat/lib/expat.h \ + -e expat/lib/internal.h \ + -e expat/lib/xmlrole.h \ + -e expat/lib/xmltok.h \ + -e expat/lib/xmltok_impl.c \ + -e expat/lib/xmltok_ns.c)" +files_actual="$(git grep -l '^ *# *include.*expat_config.h' -- expat/lib/ expat/tests/ expat/xmlwf/)" + +files_missing_include="$(grep -v -F -f <(echo "${files_actual}") <<<"${files_expected}" || true)" + +if [[ -n "${files_missing_include}" ]]; then + for filename in ${files_missing_include}; do + echo "[${filename}]" + git -c color.ui=always grep -h -n -E "${pattern}" -- ${filename} | sed 's,^, ,' + done + + exit 1 +fi diff -Nru expat-2.8.3/.github/workflows/scripts/mass-cppcheck.sh expat-2.9.0/.github/workflows/scripts/mass-cppcheck.sh --- expat-2.8.3/.github/workflows/scripts/mass-cppcheck.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/scripts/mass-cppcheck.sh 2026-10-05 15:43:59.000000000 +0000 @@ -46,6 +46,8 @@ '-D__has_feature(x)=1' '-DEXPAT_FMT_SIZE_T(x)=%llu' '-DEXPAT_FMT_ULL(x)=%llu' + '-DPRId64="lld"' + '-DPRIu64="llu"' -DXML_CONTEXT_BYTES=${xml_context_bytes} -DXML_GE=${xml_ge} ) diff -Nru expat-2.8.3/.github/workflows/solaris.yml expat-2.9.0/.github/workflows/solaris.yml --- expat-2.8.3/.github/workflows/solaris.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/solaris.yml 2026-10-05 15:43:59.000000000 +0000 @@ -49,7 +49,7 @@ with: persist-credentials: false - - uses: vmactions/solaris-vm@315163f088b66e55bbcc45928bd224d4973b2312 # v1.3.8 + - uses: vmactions/solaris-vm@87d3c436511cef802cd1637f86f73b1a97715c8c # v1.4.1 with: release: "11.4-gcc" # with GCC 14 as of 2026-01-11 usesh: true diff -Nru expat-2.8.3/.github/workflows/valid-xml.yml expat-2.9.0/.github/workflows/valid-xml.yml --- expat-2.8.3/.github/workflows/valid-xml.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/valid-xml.yml 2026-10-05 15:43:59.000000000 +0000 @@ -55,7 +55,7 @@ tidy_version: 5.8.0 run: |- set -x - sudo apt-get update + sudo timeout 10m apt-get update sudo timeout 10m apt-get install --yes --no-install-recommends -V \ docbook \ libxml2-utils \ @@ -68,20 +68,10 @@ - name: Enforce clean XML run: | - tidy_args=( - --indent 1 - --tidy-mark no - --vertical-space yes - --wrap 90 - --write-back yes - ) set -x -o pipefail - # Re-indent XHTML - tidy "${tidy_args[@]}" expat/doc/reference.html - - # Drop trailing whitespace - sed 's, \+$,,' -i.bak expat/doc/reference.html + # Re-format XHTML + ./expat/apply-htmltidy.sh # Replace leading tabs by spaces expand -i -t 2 expat/doc/xmlwf.xml | sponge expat/doc/xmlwf.xml diff -Nru expat-2.8.3/.github/workflows/wasi_sdk.yml expat-2.9.0/.github/workflows/wasi_sdk.yml --- expat-2.8.3/.github/workflows/wasi_sdk.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/wasi_sdk.yml 2026-10-05 15:43:59.000000000 +0000 @@ -52,12 +52,15 @@ - name: Install build dependencies env: - wasi_sdk_major: "33" + wasi_sdk_major: "34" wasi_sdk_minor: "0" + wasi_sdk_sha256sum: "09f37a478107bf30381e0d8922ac678ebe998de59e574f96830f08e520908d4a" run: |- set -x -u - wget "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${wasi_sdk_major}/wasi-sdk-${wasi_sdk_major}.${wasi_sdk_minor}-x86_64-linux.deb" - sudo timeout 10m apt-get install --yes "./wasi-sdk-${wasi_sdk_major}.${wasi_sdk_minor}-x86_64-linux.deb" + wasi_sdk_deb="wasi-sdk-${wasi_sdk_major}.${wasi_sdk_minor}-$(uname -m)-linux.deb" + wget --progress=dot:giga "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${wasi_sdk_major}/${wasi_sdk_deb}" + sha256sum --check <<<"${wasi_sdk_sha256sum} ${wasi_sdk_deb}" + sudo timeout 10m apt-get install --yes "./${wasi_sdk_deb}" - name: Build using WASI SDK run: | diff -Nru expat-2.8.3/.github/workflows/windows-build.yml expat-2.9.0/.github/workflows/windows-build.yml --- expat-2.8.3/.github/workflows/windows-build.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/windows-build.yml 2026-10-05 15:43:59.000000000 +0000 @@ -64,11 +64,11 @@ expat_sln: expat.sln - runs-on: windows-11-arm cmake_build_type: Debug - cmake_generator: Visual Studio 17 2022 + cmake_generator: Visual Studio 18 2026 cmake_platform: ARM64 expat_char_type: wchar_t expat_dll: libexpatwd.dll - expat_sln: expat.sln + expat_sln: expat.slnx - runs-on: windows-2025-vs2026 cmake_build_type: Debug cmake_generator: Visual Studio 18 2026 diff -Nru expat-2.8.3/.github/workflows/zizmor.yml expat-2.9.0/.github/workflows/zizmor.yml --- expat-2.8.3/.github/workflows/zizmor.yml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.github/workflows/zizmor.yml 2026-10-05 15:43:59.000000000 +0000 @@ -48,6 +48,6 @@ with: persist-credentials: false - - uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 + - uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4 with: advanced-security: false diff -Nru expat-2.8.3/.mailmap expat-2.9.0/.mailmap --- expat-2.8.3/.mailmap 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/.mailmap 2026-10-05 15:43:59.000000000 +0000 @@ -8,6 +8,7 @@ Hanno Böck Hanno Böck <990588+hannob@users.noreply.github.com> Haris Hussain +Huang Wenbin James Clark José Gutiérrez de la Concha Joyce Brum @@ -16,6 +17,7 @@ Kartik Kenchi Kishore Kunche Martin Ettl +Matthew Wozniczka Owain Davies Rhodri James Rolf Eike Beer diff -Nru expat-2.8.3/README.md expat-2.9.0/README.md --- expat-2.8.3/README.md 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/README.md 2026-10-05 15:43:59.000000000 +0000 @@ -17,7 +17,7 @@ > Thank you! :heart: :pray: -# Expat, Release 2.8.3 +# Expat, Release 2.9.0 This is Expat, a C99 library for parsing [XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by diff -Nru expat-2.8.3/debian/changelog expat-2.9.0/debian/changelog --- expat-2.8.3/debian/changelog 2026-08-13 02:05:43.000000000 +0000 +++ expat-2.9.0/debian/changelog 2026-10-11 13:25:11.000000000 +0000 @@ -1,9 +1,51 @@ -expat (2.8.3-1~deb13u1) trixie-security; urgency=high +expat (2.9.0-1~deb13u1) trixie-security; urgency=high * Non-maintainer upload by the Security Team. - * Upload 2.8.3 to trixie-security. + * Rebuild for trixie-security - -- Aron Xu Thu, 13 Aug 2026 10:05:43 +0800 + -- Salvatore Bonaccorso Sun, 11 Oct 2026 15:25:11 +0200 + +expat (2.9.0-1) unstable; urgency=high + + * New upstream release: + - fixes CVE-2026-77214: validate parameter len against available buffer + capacity in XML_ParseBuffer(), + - fixes CVE-2026-102633: integer overflow in function expat_realloc() + on 32bit platforms. + * Update libexpat1 symbols. + + -- Laszlo Boszormenyi (GCS) Mon, 05 Oct 2026 22:04:46 +0200 + +expat (2.8.5-2) unstable; urgency=high + + * Backport upstream fixes for CVE-2026-102633: replace assert with runtime + overflow check in expat_realloc (closes: #1149507). + + -- Laszlo Boszormenyi (GCS) Wed, 30 Sep 2026 16:38:52 +0200 + +expat (2.8.5-1) unstable; urgency=medium + + * New upstream release. + + -- Laszlo Boszormenyi (GCS) Sun, 27 Sep 2026 08:45:04 +0200 + +expat (2.8.4-2) unstable; urgency=high + + * Backport upstream fixes for CVE-2026-93990: fails to validate low + surrogates following high surrogates in UTF-16 input (closes: #1148665). + + -- Laszlo Boszormenyi (GCS) Tue, 22 Sep 2026 06:48:48 +0200 + +expat (2.8.4-1) unstable; urgency=high + + * New upstream release: + - fixes CVE-2026-66046 and CVE-2026-76641: fix quadratic runtime from + "attribute isCdata lookups" that allowed denial of service attacks, + - fixes CVE-2026-76957: protect custom encoding callbacks from parser + re-entry, + - fixes CVE-2026-76956: fix inverted getentropy() return handling. + + -- Laszlo Boszormenyi (GCS) Mon, 31 Aug 2026 18:48:23 +0200 expat (2.8.3-1) unstable; urgency=high diff -Nru expat-2.8.3/debian/libexpat1.symbols expat-2.9.0/debian/libexpat1.symbols --- expat-2.8.3/debian/libexpat1.symbols 2026-04-25 05:19:30.000000000 +0000 +++ expat-2.9.0/debian/libexpat1.symbols 2026-10-11 13:24:58.000000000 +0000 @@ -7,15 +7,23 @@ XML_FreeContentModel@Base 2.0.1 XML_GetBase@Base 2.0.1 XML_GetBuffer@Base 2.0.1 + XML_GetCurrentByteCount64@Base 2.9.0 XML_GetCurrentByteCount@Base 2.0.1 + XML_GetCurrentByteIndex64@Base 2.9.0 XML_GetCurrentByteIndex@Base 2.0.1 + XML_GetCurrentColumnNumber64@Base 2.9.0 XML_GetCurrentColumnNumber@Base 2.0.1 + XML_GetCurrentLineNumber64@Base 2.9.0 XML_GetCurrentLineNumber@Base 2.0.1 XML_GetErrorCode@Base 2.0.1 XML_GetFeatureList@Base 2.0.1 XML_GetIdAttributeIndex@Base 2.0.1 + XML_GetInputContext64@Base 2.9.0 XML_GetInputContext@Base 2.0.1 XML_GetParsingStatus@Base 2.0.1 + XML_GetPropertyBool@Base 2.9.0 + XML_GetPropertyDouble@Base 2.9.0 + XML_GetPropertyUInt64@Base 2.9.0 XML_GetSpecifiedAttributeCount@Base 2.0.1 XML_MemFree@Base 2.0.1 XML_MemMalloc@Base 2.0.1 @@ -57,6 +65,9 @@ XML_SetNotationDeclHandler@Base 2.0.1 XML_SetParamEntityParsing@Base 2.0.1 XML_SetProcessingInstructionHandler@Base 2.0.1 + XML_SetPropertyBool@Base 2.9.0 + XML_SetPropertyDouble@Base 2.9.0 + XML_SetPropertyUInt64@Base 2.9.0 XML_SetReparseDeferralEnabled@Base 2.6.0 XML_SetReturnNSTriplet@Base 2.0.1 XML_SetSkippedEntityHandler@Base 2.0.1 @@ -80,15 +91,23 @@ XML_FreeContentModel@Base 2.0.1 XML_GetBase@Base 2.0.1 XML_GetBuffer@Base 2.0.1 + XML_GetCurrentByteCount64@Base 2.9.0 XML_GetCurrentByteCount@Base 2.0.1 + XML_GetCurrentByteIndex64@Base 2.9.0 XML_GetCurrentByteIndex@Base 2.0.1 + XML_GetCurrentColumnNumber64@Base 2.9.0 XML_GetCurrentColumnNumber@Base 2.0.1 + XML_GetCurrentLineNumber64@Base 2.9.0 XML_GetCurrentLineNumber@Base 2.0.1 XML_GetErrorCode@Base 2.0.1 XML_GetFeatureList@Base 2.0.1 XML_GetIdAttributeIndex@Base 2.0.1 + XML_GetInputContext64@Base 2.9.0 XML_GetInputContext@Base 2.0.1 XML_GetParsingStatus@Base 2.0.1 + XML_GetPropertyBool@Base 2.9.0 + XML_GetPropertyDouble@Base 2.9.0 + XML_GetPropertyUInt64@Base 2.9.0 XML_GetSpecifiedAttributeCount@Base 2.0.1 XML_MemFree@Base 2.0.1 XML_MemMalloc@Base 2.0.1 @@ -130,6 +149,9 @@ XML_SetNotationDeclHandler@Base 2.0.1 XML_SetParamEntityParsing@Base 2.0.1 XML_SetProcessingInstructionHandler@Base 2.0.1 + XML_SetPropertyBool@Base 2.9.0 + XML_SetPropertyDouble@Base 2.9.0 + XML_SetPropertyUInt64@Base 2.9.0 XML_SetReparseDeferralEnabled@Base 2.6.0 XML_SetReturnNSTriplet@Base 2.0.1 XML_SetSkippedEntityHandler@Base 2.0.1 diff -Nru expat-2.8.3/expat/CMake.README expat-2.9.0/expat/CMake.README --- expat-2.8.3/expat/CMake.README 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/CMake.README 2026-10-05 15:43:59.000000000 +0000 @@ -3,25 +3,25 @@ The cmake based buildsystem for expat works on Windows (cygwin, mingw, Visual Studio) and should work on all other platform cmake supports. -Assuming ~/expat-2.8.3 is the source directory of expat, add a subdirectory +Assuming ~/expat-2.9.0 is the source directory of expat, add a subdirectory build and change into that directory: -~/expat-2.8.3$ mkdir build && cd build -~/expat-2.8.3/build$ +~/expat-2.9.0$ mkdir build && cd build +~/expat-2.9.0/build$ From that directory, call cmake first, then call make, make test and make install in the usual way: -~/expat-2.8.3/build$ cmake .. +~/expat-2.9.0/build$ cmake .. -- The C compiler identification is GNU -- The CXX compiler identification is GNU .... -- Configuring done -- Generating done --- Build files have been written to: /home/patrick/expat-2.8.3/build +-- Build files have been written to: /home/patrick/expat-2.9.0/build If you want to specify the install location for your files, append -DCMAKE_INSTALL_PREFIX=/your/install/path to the cmake call. -~/expat-2.8.3/build$ make && make test && make install +~/expat-2.9.0/build$ make && make test && make install Scanning dependencies of target expat [ 5%] Building C object CMakeFiles/expat.dir/lib/xmlparse.c.o [ 11%] Building C object CMakeFiles/expat.dir/lib/xmlrole.c.o diff -Nru expat-2.8.3/expat/CMakeLists.txt expat-2.9.0/expat/CMakeLists.txt --- expat-2.8.3/expat/CMakeLists.txt 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/CMakeLists.txt 2026-10-05 15:43:59.000000000 +0000 @@ -43,7 +43,7 @@ project(expat VERSION - 2.8.3 + 2.9.0 LANGUAGES C ) @@ -179,11 +179,11 @@ mark_as_advanced(EXPAT_WITH_SYS_GETRANDOM) endif() expat_shy_set(EXPAT_CHAR_TYPE "char" CACHE STRING "Character type to use (char|ushort|wchar_t) [default=char]") -expat_shy_set(EXPAT_ATTR_INFO OFF CACHE BOOL "Define to allow retrieving the byte offsets for attribute names and values") +expat_shy_set(EXPAT_ATTR_INFO OFF CACHE BOOL "FEATURE REMOVED") mark_as_advanced(EXPAT_ATTR_INFO) -expat_shy_set(EXPAT_LARGE_SIZE OFF CACHE BOOL "Make XML_GetCurrent* functions return <(unsigned) long long> rather than <(unsigned) long>") +expat_shy_set(EXPAT_LARGE_SIZE OFF CACHE BOOL "[DEPRECATED] Make XML_GetCurrent* functions return <(unsigned) long long> rather than <(unsigned) long>") mark_as_advanced(EXPAT_LARGE_SIZE) -expat_shy_set(EXPAT_MIN_SIZE OFF CACHE BOOL "Get a smaller (but slower) parser (in particular avoid multiple copies of the tokenizer)") +expat_shy_set(EXPAT_MIN_SIZE OFF CACHE BOOL "FEATURE REMOVED") mark_as_advanced(EXPAT_MIN_SIZE) if(MSVC OR _EXPAT_HELP) expat_shy_set(EXPAT_MSVC_STATIC_CRT OFF CACHE BOOL "Use /MT flag (static CRT) when compiling in MSVC") @@ -212,6 +212,14 @@ message(SEND_ERROR "Please either enable option EXPAT_GE (recommended) or disable EXPAT_DTD also.") endif() +if(EXPAT_ATTR_INFO) + message(FATAL_ERROR "Feature -DEXPAT_ATTR_INFO=ON has been removed. Please open a GitHub issue if you need it back.") +endif() + +if(EXPAT_MIN_SIZE) + message(FATAL_ERROR "Feature -DEXPAT_MIN_SIZE=ON has been removed. Please open a GitHub issue if you need it back.") +endif() + if(MSVC) # For the three types of MSVC version values, please see: # - https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html @@ -230,12 +238,6 @@ message(WARNING "Please use ${_EXPAT_MSVC_SUPPORTED_DISPLAY} or later. Thank you!") endif() endif() - - # Stop MSVC from trying to decode the source files using - # the Windows system code page encoding that could e.g. be Windows-936 - # on a Chinese Windows system rather than UTF-8. - # https://devblogs.microsoft.com/cppblog/new-options-for-managing-character-sets-in-the-microsoft-cc-compiler/ - add_compile_options(/source-charset:utf-8) endif() macro(_expat_copy_bool_int source_ref dest_ref) @@ -250,10 +252,6 @@ add_definitions(-DXML_LARGE_SIZE) endif() -if(EXPAT_MIN_SIZE) - add_definitions(-DXML_MIN_SIZE) -endif() - if(EXPAT_CHAR_TYPE STREQUAL "char") set(_EXPAT_UNICODE OFF) set(_EXPAT_UNICODE_WCHAR_T OFF) @@ -312,11 +310,9 @@ evaluate_detection_results(EXPAT_WITH_SYS_GETRANDOM HAVE_SYSCALL_GETRANDOM "syscall SYS_getrandom" "Syscall SYS_getrandom") endif() -_expat_copy_bool_int(EXPAT_ATTR_INFO XML_ATTR_INFO) _expat_copy_bool_int(EXPAT_DTD XML_DTD) _expat_copy_bool_int(EXPAT_GE XML_GE) _expat_copy_bool_int(EXPAT_LARGE_SIZE XML_LARGE_SIZE) -_expat_copy_bool_int(EXPAT_MIN_SIZE XML_MIN_SIZE) _expat_copy_bool_int(EXPAT_NS XML_NS) if(NOT WIN32) _expat_copy_bool_int(EXPAT_DEV_URANDOM XML_DEV_URANDOM) @@ -411,11 +407,11 @@ # C library # set(_EXPAT_C_SOURCES + lib/xcs.c lib/xmlparse.c lib/xmlrole.c lib/xmltok.c # NOTE: ISO C forbids an empty translation unit -# lib/xcsinc.c # lib/xmltok_impl.c # lib/xmltok_ns.c ) @@ -486,7 +482,6 @@ endmacro() _expat_def_file_toggle(_EXPAT_DTD_OR_GE _EXPAT_COMMENT_DTD_OR_GE) - _expat_def_file_toggle(EXPAT_ATTR_INFO _EXPAT_COMMENT_ATTR_INFO) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lib/libexpat.def.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def") set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.def") @@ -505,7 +500,6 @@ endmacro() _expat_map_file_toggle(_EXPAT_DTD_OR_GE _EXPAT_COMMENT_DTD_OR_GE) - _expat_map_file_toggle(EXPAT_ATTR_INFO _EXPAT_COMMENT_ATTR_INFO) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lib/libexpat.map.in" "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.map") set(_EXPAT_EXTRA_SOURCES ${_EXPAT_EXTRA_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/lib/libexpat.map") @@ -553,9 +547,9 @@ set_property(TARGET expat PROPERTY ${build_type_upper}_POSTFIX ${EXPAT_${build_type_upper}_POSTFIX}) endforeach() -set(LIBCURRENT 13) # sync -set(LIBREVISION 3) # with -set(LIBAGE 12) # configure.ac! +set(LIBCURRENT 14) # sync +set(LIBREVISION 0) # with +set(LIBAGE 13 ) # configure.ac! math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}") if(NOT WIN32) @@ -657,6 +651,7 @@ # if(EXPAT_BUILD_TOOLS) set(xmlwf_SRCS + lib/xcs.c xmlwf/codepage.c xmlwf/readfilemap.c xmlwf/xmlfile.c @@ -732,13 +727,16 @@ tests/common.c tests/dummy.c tests/handlers.c + tests/hash_tests.c tests/memcheck.c tests/minicheck.c tests/misc_tests.c tests/ns_tests.c tests/nsalloc_tests.c + tests/props_tests.c tests/runtests.c tests/structdata.c + tests/xcs_tests.c ${_EXPAT_C_SOURCES} ) @@ -1062,12 +1060,10 @@ message(STATUS "") message(STATUS " Features") message(STATUS " // Advanced options, changes not advised") -message(STATUS " Attributes info .......... ${EXPAT_ATTR_INFO}") message(STATUS " Context bytes ............ ${EXPAT_CONTEXT_BYTES}") message(STATUS " DTD support .............. ${EXPAT_DTD}") message(STATUS " General entities ......... ${EXPAT_GE}") message(STATUS " Large size ............... ${EXPAT_LARGE_SIZE}") -message(STATUS " Minimum size ............. ${EXPAT_MIN_SIZE}") message(STATUS " Namespace support ........ ${EXPAT_NS}") message(STATUS "") message(STATUS " Entropy sources") diff -Nru expat-2.8.3/expat/Changes expat-2.9.0/expat/Changes --- expat-2.8.3/expat/Changes 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/Changes 2026-10-05 15:43:59.000000000 +0000 @@ -5,16 +5,242 @@ \___/_/\_\ .__/ \__,_|\__| |_| XML parser -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! Expat has UNFIXED SECURITY ISSUES! !! -!! Please see https://github.com/libexpat/libexpat/issues/1160 for details. !! -!! !! -!! Starting 2026-08-01, for up to six months my work maintaining libexpat !! -!! will be funded by the City of Munich as part of their !! -!! Open Source Sabbatical (https://opensource.muenchen.de/sabbatical.html) !! -!! — thank you! !! -!! Sebastian Pipping -- Berlin, 2026-08-03 !! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +/////////////////////////////////////////////////////////////////////////////// +// Expat has UNFIXED SECURITY ISSUES! // +// Please see https://github.com/libexpat/libexpat/issues/1160 for details. // +// // +// Starting 2026-08-01, for up to six months my work maintaining libexpat // +// will be funded by the City of Munich as part of their // +// Open Source Sabbatical (https://opensource.muenchen.de/sabbatical.html) // +// — thank you! // +// // +// If your business relies on Expat beyond January 2027, please consider // +// funding the maintenance of Expat to ensure its health and security for // +// you and others. Thank you! // +// // +// Sebastian Pipping -- Berlin, 2026-09-22 // +/////////////////////////////////////////////////////////////////////////////// + +Release 2.9.0 Mon October 5 2026 + Security fixes: + #1392 CVE-2026-102633 -- Integer overflow in function + expat_realloc on 32bit platforms + #1393 CVE-2026-77214 -- Validate parameter `len` against available + buffer capacity in XML_ParseBuffer + + Bug fixes: + #1387 lib: Handle OOM when copying encodingName in XML_ParserReset + + New features: + #1327 lib: Introduce new "Properties API" to get and set + scalar properties for a single parser instance. + There are six new functions: + - XML_GetPropertyBool + - XML_GetPropertyDouble + - XML_GetPropertyUInt64 + - XML_SetPropertyBool + - XML_SetPropertyDouble + - XML_SetPropertyUInt64 + And two new enums: + - XML_Prop_Error + - XML_Parser_Property + #1323 lib: Introduce five new 64bit location API functions: + - XML_GetCurrentByteCount64 + - XML_GetCurrentByteIndex64 + - XML_GetCurrentColumnNumber64 + - XML_GetCurrentLineNumber64 + - XML_GetInputContext64 + These five functions closely mirror their predecessors + but are not prone to 32bit integer wrap-around. + + Other changes: + #1391 docs: Document the scope of function XML_GetErrorCode + #1395 docs: Document length expectations for XML_ParseBuffer + #1394 lib: Call unknown encoding release before parser teardown + #1370 #1373 lib: Drop (disabled-by-default) attribute info feature + These things are now gone: + - Public function XML_GetAttributeInfo + - Public struct XML_AttrInfo + - Macro XML_ATTR_INFO + These things are now causing build errors: + - Configure option --enable-xml-attr-info + - CMake option -DEXPAT_ATTR_INFO=ON + #1379 #1382 lib: Drop (disabled-by-default) minimum size feature + These things are now gone: + - Macro XML_MIN_SIZE + These things are now causing build errors: + - Configuring with -DXML_MIN_SIZE for CPPFLAGS/CFLAGS + - CMake option -DEXPAT_MIN_SIZE=ON + #1323 #1411 lib: Deprecate macro XML_LARGE_SIZE (use the new 64bit + location API functions instead please) + #1323 lib: Deprecate five location API functions that are cursed + with integer wrap-around: + - XML_GetCurrentByteCount + - XML_GetCurrentByteIndex + - XML_GetCurrentColumnNumber + - XML_GetCurrentLineNumber + - XML_GetInputContext + #1399 lib: Guard against out-of-handler use of XML_DefaultCurrent + #1400 lib: Use size_t for bytesAllocated and peakBytesAllocated + #1401 #1402 .. + #1404 #1405 lib|xmlwf|tests: Unify use of xcslen, xcscmp and xcsncmp + #1406 xmlwf: Add missing `#include "expat.h"` + #1389 #1396 Version info bumped from 13:5:12 (libexpat*.so.1.12.5) + to 14:0:13 (libexpat*.so.1.13.0); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #1386 CI: Bump MinGW Clang from 23.1.1 to 23.1.2 + #1410 CI: Bump Fil-C from 0.685 to 0.686 + #1388 CI: Adapt to breaking changes regarding windows-11-arm image + #1403 CI: Limit package cache download runtime + + Special thanks to: + Bénédikt Tran + Berkay Eren Ürün + Fabian Wahle (Hap Security) + Filippo Tedeschi + Junki Lee + Matthew Fernandez + Scott Moore + Wade Sparks III + and + City of Munich Open Source Sabbatical + VulnCheck + +Release 2.8.5 Tue September 22 2026 + Security fixes: + #1282 CVE-2026-93990 -- Reject high surrogates not followed by a + low surrogate during UTF-16 decoding; previously, malformed + UTF-16 could be smuggled into the application using Expat + and could cause arbitrary damage there, depending on how + malformed UTF-16 was handled inside the application; + validation was not their job but Expat's. This is similar + to past vulnerability CVE-2022-25235. + Upstream CVSS 3.1 vector: + AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (CVSS score: 9.8) + + Bug fixes: + #1346 lib: Fix OOM-related memory leak on a failed overflow check + #1371 lib: Fix memory alignment for architectures with 128bit + pointers like CHERI-RISC-V + #1367 xmlwf: Handle errors when closing output files + + Other changes: + #1354 lib: Reject an XML declaration version other than `1.[0-9]+` + (which is less strict than XML 1.0r4 (fourth edition) + and matches XML 1.0r5 (fifth edition)) + #1362 lib: Make Clang, GCC and MSVC warn about use of function + XML_SetHashSalt that is deprecated since Expat 2.8.0 + #1357 lib: Drop internal macros FASTCALL, PTRCALL, PTRFASTCALL + #1367 xmlwf: Document that with `-k` the last error determines the + xmlwf exit code in `--help` output + #1367 xmlwf: Make exit code 3 documentation match exit code 2 more + closely in `--help` output + #1352 #1353 CMake|Windows: Refrain from adding `/source-charset:utf-8` + for MSVC + #1366 #1374 Autotools: Be explicit about the minimum required version of + GNU Automake, currently version 1.13 of 2012-12-28 + #1351 Autotools|macOS: Sync CMake templates with CMake 4.4.3 + #1349 Replace some internal use of XML_Bool with standard bool + #1364 tests: Propagate xmltest.sh failures via exit status + #1360 tests|xmlwf: Add `#include "expat_config.h"` where missing + #1355 tests: Start covering hash table operation + #1350 #1369 tests: Drop __cplusplus leftovers + #1378 tests: Fix tail pointer when unlinking the last tracked + allocation + #1376 docs: Emphasize that XML_StopParser is not immediate + #1381 docs: Sync XML_FeatureEnum value list in doc/reference.html + #1356 #1361 Version info bumped from 13:4:12 (libexpat*.so.1.12.4) + to 13:5:12 (libexpat*.so.1.12.5); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #1347 Add missing .gitignore entries + #1360 CI: Detect missing `#include "expat_config.h"` + #1368 CI: Bump MinGW Clang from 23.0.1 to 23.1.1 + #1377 CI: Bump Fil-C from 0.684 to 0.685 + #1380 CI: Bump Cppcheck from 2.21.0 to 2.22.0 + #1372 CI: Extract helper script `apply-htmltidy.sh` + #1366 #1374 Autotools: Start to also produce .tar.bz3 release tarballs + + Special thanks to: + Afonso Januário + Braian Plaku + Florian Schmaus + Huang Wenbin + Kamila Szewczyk + Kartik Kenchi + Leo Camus + Matthew Fernandez + Stan Ulbrych + and + City of Munich Open Source Sabbatical + +Release 2.8.4 Mon August 31 2026 + Security fixes: + #1321 #1331 CVE-2026-66046, CVE-2026-76641 -- Fix quadratic runtime from + "attribute isCdata lookups" that allowed denial of service + attacks through moderately sized crafted XML input + (CWE-407). + The vulnerability is closely related to past CVE-2026-45186 + that was fixed with Expat 2.8.1. + Please note that a layer of compression around XML can + significantly reduce the minimum attack payload size. + Upstream CVSS 3.1 vector: + AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (CVSS score: 7.5) + (Note the "AV:N" for network/remote.) + #1322 CVE-2026-76957 -- Protect custom encoding callbacks from + parser re-entry. The vulnerability is closely related to + past issues CVE-2026-50219, CVE-2026-56131 and + CVE-2026-56412 that were all fixed with Expat 2.8.2. + #1326 CVE-2026-76956 -- Fix inverted getentropy() return handling + Allows for hash flooding denial of services in + configurations where getentropy is configured or detected + as the only high quality entropy extractor. + Upstream CVSS 3.1 vector: + AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H (CVSS score: 5.9) + (Note the "AV:N" for network/remote.) + + Other changes: + #1332 #1333 CMake: Only add `/source-charset:utf-8` when `/utf-8` is not + present + #1315 lib: Resolve (currently unreachable) undefined behavior from + overshifting a signed int to the left + #1325 #1334 lib: Support read-only hash table lookup with keys that are + not zero-terminated + #1340 lib: Use a C99 bool for `ENTITY.open` + #1319 Fix typo in comment + #1320 Sync file headers + #1328 #1329 Version info bumped from 13:3:12 (libexpat*.so.1.12.3) + to 13:4:12 (libexpat*.so.1.12.4); see https://verbump.de/ + for what these numbers do + + Infrastructure: + #1317 #1335 CI: Cover compilation and execution with Fil-C + #1337 CI: Cover compilation and execution on riscv64 + #1338 CI: Cover compilation and execution with Clang-based MinGW + #1339 CI: Cover compilation and execution on (big-endian) s390x + #1316 CI: Run test suite with musl, also + #1336 CI: Bump WASI SDK from 33 to 34 + #1345 CI: Bump Clang from 22 to 23 + + Special thanks to: + Alberto Maschietto + Alexander Bluhm + Berkay Eren Ürün + Darren Carreras + Fabian Wahle (Hap Security) + Matteo Forzan + Matthew Fernandez + Sorrashut Kaewtaworn + Wade Sparks III + Zeyou Liu + and + City of Munich Open Source Sabbatical + Moonshot AI + VulnCheck + Z.ai Release 2.8.3 Mon August 10 2026 Security fixes: diff -Nru expat-2.8.3/expat/Makefile.am expat-2.9.0/expat/Makefile.am --- expat-2.8.3/expat/Makefile.am 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/Makefile.am 2026-10-05 15:43:59.000000000 +0000 @@ -33,13 +33,6 @@ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE # USE OR OTHER DEALINGS IN THE SOFTWARE. -AUTOMAKE_OPTIONS = \ - dist-bzip2 \ - dist-lzip \ - dist-xz \ - foreign \ - subdir-objects - ACLOCAL_AMFLAGS = -I m4 LIBTOOLFLAGS = --verbose diff -Nru expat-2.8.3/expat/README.md expat-2.9.0/expat/README.md --- expat-2.8.3/expat/README.md 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/README.md 2026-10-05 15:43:59.000000000 +0000 @@ -17,7 +17,7 @@ > Thank you! :heart: :pray: -# Expat, Release 2.8.3 +# Expat, Release 2.9.0 This is Expat, a C99 library for parsing [XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by diff -Nru expat-2.8.3/expat/apply-clang-tidy.sh expat-2.9.0/expat/apply-clang-tidy.sh --- expat-2.8.3/expat/apply-clang-tidy.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/apply-clang-tidy.sh 2026-10-05 15:43:59.000000000 +0000 @@ -64,7 +64,6 @@ -Ilib/ -DENCODING_FOR_FUZZING=UTF-8 - -DXML_ATTR_INFO -DXML_CLANG_TIDY -DXML_DTD -DXML_GE diff -Nru expat-2.8.3/expat/apply-htmltidy.sh expat-2.9.0/expat/apply-htmltidy.sh --- expat-2.8.3/expat/apply-htmltidy.sh 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/apply-htmltidy.sh 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,53 @@ +#! /usr/bin/env bash +# __ __ _ +# ___\ \/ /_ __ __ _| |_ +# / _ \\ /| '_ \ / _` | __| +# | __// \| |_) | (_| | |_ +# \___/_/\_\ .__/ \__,_|\__| +# |_| XML parser +# +# Copyright (c) 2017-2026 Sebastian Pipping +# Licensed under the MIT license: +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the +# following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + +set -e -u -o pipefail + +cd "$(dirname "$(type -P "$0")")" + +PS4=$'\n+ ' + +tidy_args=( + --indent 1 + --tidy-mark no + --vertical-space yes + --wrap 90 + --write-back yes +) + +set -x + +# Re-indent XHTML +tidy "${tidy_args[@]}" doc/reference.html + +# Drop trailing whitespace +sed 's, \+$,,' -i.bak doc/reference.html + +rm doc/reference.html.bak diff -Nru expat-2.8.3/expat/cmake/autotools/.gitignore expat-2.9.0/expat/cmake/autotools/.gitignore --- expat-2.8.3/expat/cmake/autotools/.gitignore 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/cmake/autotools/.gitignore 2026-10-05 15:43:59.000000000 +0000 @@ -1,2 +1,3 @@ +/expat.cmake /expat-config-version.cmake /expat-noconfig.cmake diff -Nru expat-2.8.3/expat/cmake/autotools/expat__macos.cmake.in expat-2.9.0/expat/cmake/autotools/expat__macos.cmake.in --- expat-2.8.3/expat/cmake/autotools/expat__macos.cmake.in 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/cmake/autotools/expat__macos.cmake.in 2026-10-05 15:43:59.000000000 +0000 @@ -7,7 +7,7 @@ message(FATAL_ERROR "CMake >= 2.8.12 required") endif() cmake_policy(PUSH) -cmake_policy(VERSION 2.8.12...4.1) +cmake_policy(VERSION 2.8.12...4.2) #---------------------------------------------------------------- # Generated CMake target import file. #---------------------------------------------------------------- diff -Nru expat-2.8.3/expat/cmake/expat-config.cmake.in expat-2.9.0/expat/cmake/expat-config.cmake.in --- expat-2.8.3/expat/cmake/expat-config.cmake.in 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/cmake/expat-config.cmake.in 2026-10-05 15:43:59.000000000 +0000 @@ -43,10 +43,10 @@ set(expat_${_NAME}_FOUND ${_AVAILABE}) endmacro() -expat_register_component(attr_info @EXPAT_ATTR_INFO@) +expat_register_component(attr_info OFF) expat_register_component(dtd @EXPAT_DTD@) expat_register_component(large_size @EXPAT_LARGE_SIZE@) -expat_register_component(min_size @EXPAT_MIN_SIZE@) +expat_register_component(min_size OFF) expat_register_component(ns @EXPAT_NS@) if(@EXPAT_CONTEXT_BYTES@) diff -Nru expat-2.8.3/expat/configure.ac expat-2.9.0/expat/configure.ac --- expat-2.8.3/expat/configure.ac 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/configure.ac 2026-10-05 15:43:59.000000000 +0000 @@ -74,7 +74,21 @@ AC_CONFIG_AUX_DIR([conftools]) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST -AM_INIT_AUTOMAKE + +dnl NOTE: Macro `AM_OPTIONAL_AUTOMAKE` is available with Automake >=1.19 +dnl and serves as a proxy for robust Automake >=1.19 detection here. +dnl `dist-bzip3` was introduced with Automake 1.18 already but there +dnl is no robust way of checking precisely for Automake >=1.18. +dnl Automake >=1.13 is needed for (1) our use of LOG_DRIVER and +dnl (2) making it work without asking for parallel tests, explicitly. +m4_ifdef([AM_OPTIONAL_AUTOMAKE], [ + AM_INIT_AUTOMAKE([1.19 dist-bzip2 dist-bzip3 dist-lzip dist-xz foreign subdir-objects]) + + dnl NOTE: Without this dummy call in the file the `m4_ifdef` above never hits true. + AM_OPTIONAL_AUTOMAKE([]) +], [ + AM_INIT_AUTOMAKE([1.13 dist-bzip2 dist-lzip dist-xz foreign subdir-objects]) +]) AM_MAINTAINER_MODE([enable]) # to allow argument --disable-maintainer-mode @@ -89,9 +103,9 @@ dnl If the API changes incompatibly set LIBAGE back to 0 dnl -LIBCURRENT=13 # sync -LIBREVISION=3 # with -LIBAGE=12 # CMakeLists.txt! +LIBCURRENT=14 # sync +LIBREVISION=0 # with +LIBAGE=13 # CMakeLists.txt! AC_CONFIG_HEADERS([expat_config.h]) AH_TOP([#ifndef EXPAT_CONFIG_H @@ -212,9 +226,11 @@ dnl Note: Prefix "_INTERNAL_" here means exclusive use inside of file configure.ac AM_CONDITIONAL([UNICODE], [echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_UNICODE >/dev/null]) AM_CONDITIONAL([_INTERNAL_UNICODE_WCHAR_T], [echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_UNICODE_WCHAR_T >/dev/null]) -AM_CONDITIONAL([_INTERNAL_MIN_SIZE], [echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_MIN_SIZE >/dev/null]) AM_CONDITIONAL([_INTERNAL_LARGE_SIZE], [echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_LARGE_SIZE >/dev/null]) +AS_IF([echo -- "${CPPFLAGS}${CFLAGS}" | ${FGREP} XML_MIN_SIZE >/dev/null], + [AC_MSG_ERROR([Feature XML_MIN_SIZE has been removed. Please open a GitHub issue if you need it back.])]) + LT_LIB_M AC_ARG_WITH([arc4random], @@ -414,13 +430,11 @@ AC_ARG_ENABLE([xml-attr-info], - [AS_HELP_STRING([--enable-xml-attr-info], - [Enable retrieving the byte offsets for attribute names and values @<:@default=no@:>@])], + [], [], [enable_xml_attr_info=no]) AS_IF([test "x${enable_xml_attr_info}" = "xyes"], - [AC_DEFINE([XML_ATTR_INFO], 1, - [Define to allow retrieving the byte offsets for attribute names and values.])]) + [AC_MSG_ERROR([Feature --enable-xml-attr-info has been removed. Please open a GitHub issue if you need it back.])]) AC_ARG_ENABLE([xml-context], AS_HELP_STRING([--enable-xml-context @<:@COUNT@:>@], @@ -481,21 +495,12 @@ dnl NOTE: The *_TRUE variables read here are Automake conditionals dnl that are either set to "" when enabled or to "#" when disabled dnl (because they are used to dynamically comment out certain things) -AS_IF([test "x${enable_xml_attr_info}" = xyes], - [EXPAT_ATTR_INFO=ON - _EXPAT_COMMENT_ATTR_INFO=" "], - [EXPAT_ATTR_INFO=OFF - _EXPAT_COMMENT_ATTR_INFO="#"]) -AC_SUBST([_EXPAT_COMMENT_ATTR_INFO]) EXPAT_DTD=ON _EXPAT_COMMENT_DTD_OR_GE=" " AC_SUBST([_EXPAT_COMMENT_DTD_OR_GE]) AS_IF([test "x${_INTERNAL_LARGE_SIZE_TRUE}" = x], [EXPAT_LARGE_SIZE=ON], [EXPAT_LARGE_SIZE=OFF]) -AS_IF([test "x${_INTERNAL_MIN_SIZE_TRUE}" = x], - [EXPAT_MIN_SIZE=ON], - [EXPAT_MIN_SIZE=OFF]) EXPAT_NS=ON AS_IF([test "x${enable_xml_context}" != xno], [EXPAT_CONTEXT_BYTES=${enable_xml_context}], @@ -511,10 +516,8 @@ SO_MAJOR="$(expr "${LIBCURRENT}" - "${LIBAGE}")" SO_MINOR="${LIBAGE}" SO_PATCH="${LIBREVISION}" -AC_SUBST([EXPAT_ATTR_INFO]) AC_SUBST([EXPAT_DTD]) AC_SUBST([EXPAT_LARGE_SIZE]) -AC_SUBST([EXPAT_MIN_SIZE]) AC_SUBST([EXPAT_NS]) AC_SUBST([EXPAT_CONTEXT_BYTES]) AC_SUBST([EXPAT_CHAR_TYPE]) diff -Nru expat-2.8.3/expat/coverage.sh expat-2.9.0/expat/coverage.sh --- expat-2.8.3/expat/coverage.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/coverage.sh 2026-10-05 15:43:59.000000000 +0000 @@ -54,11 +54,6 @@ char_part=__char fi - local xml_attr_part= - if ${xml_attr_info_enabled}; then - xml_attr_part=__attr_info - fi - local m32_part= if ${with_m32}; then m32_part=__m32 @@ -74,7 +69,7 @@ dtd_part=__dtd fi - echo "build__${version}__xml_context_${xml_context}${mingw_part}${char_part}${ge_part}${dtd_part}${xml_attr_part}${m32_part}" + echo "build__${version}__xml_context_${xml_context}${mingw_part}${char_part}${ge_part}${dtd_part}${m32_part}" } @@ -89,9 +84,6 @@ ${unicode_enabled} \ && cmake_args+=( -DEXPAT_CHAR_TYPE=wchar_t ) - ${xml_attr_info_enabled} \ - && cmake_args+=( -DEXPAT_ATTR_INFO=ON ) - if [[ ${xml_context} -eq 0 ]]; then cmake_args+=( -DEXPAT_CONTEXT_BYTES=OFF ) else @@ -315,10 +307,8 @@ continue fi - for xml_attr_info_enabled in true false ; do - for xml_context in 0 1024 ; do - _build_case - done + for xml_context in 0 1024 ; do + _build_case done done done diff -Nru expat-2.8.3/expat/distribute.sh expat-2.9.0/expat/distribute.sh --- expat-2.8.3/expat/distribute.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/distribute.sh 2026-10-05 15:43:59.000000000 +0000 @@ -7,7 +7,7 @@ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # -# Copyright (c) 2018-2019 Sebastian Pipping +# Copyright (c) 2018-2026 Sebastian Pipping # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining @@ -43,6 +43,7 @@ extensions=( gz bz2 + bz3 lz xz ) diff -Nru expat-2.8.3/expat/doc/reference.html expat-2.9.0/expat/doc/reference.html --- expat-2.8.3/expat/doc/reference.html 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/doc/reference.html 2026-10-05 15:43:59.000000000 +0000 @@ -53,7 +53,7 @@

- The Expat XML Parser Release 2.8.3 + The Expat XML Parser Release 2.9.0

@@ -309,22 +309,76 @@
  • XML_GetCurrentByteIndex + (deprecated) +
  • + +
  • + XML_GetCurrentByteIndex64
  • XML_GetCurrentLineNumber + (deprecated) +
  • + +
  • + XML_GetCurrentLineNumber64
  • XML_GetCurrentColumnNumber + (deprecated) +
  • + +
  • + XML_GetCurrentColumnNumber64
  • XML_GetCurrentByteCount + (deprecated) +
  • + +
  • + XML_GetCurrentByteCount64
  • - XML_GetInputContext + XML_GetInputContext (deprecated) +
  • + +
  • + XML_GetInputContext64 +
  • + + + +
  • + Properties / Security Tuning +
  • @@ -392,10 +446,6 @@
  • - XML_GetAttributeInfo -
  • - -
  • XML_SetEncoding
  • @@ -751,19 +801,59 @@
    - XML_LARGE_SIZE + XML_LARGE_SIZE (deprecated)
    - If defined, causes the XML_Size and XML_Index integer - types to be at least 64 bits in size. This is intended to support processing of - very large input streams, where the return values of XML_GetCurrentByteIndex, XML_GetCurrentLineNumber and - XML_GetCurrentColumnNumber could - overflow. It may not be supported by all compilers, and is turned off by - default. +

    + If defined, causes the XML_Size and XML_Index + integer types to be at least 64 bits in size. This is intended to support + processing of very large input streams, where the return values of + XML_GetCurrentByteIndex, + XML_GetCurrentLineNumber + and XML_GetCurrentColumnNumber could + overflow. It may not be supported by all compilers, and is turned off by + default. +

    + +

    + Starting with Expat 2.9.0, there are 64bit versions of these functions, so + that relying on (non-default and uncommon) macro XML_LARGE_SIZE + is no longer needed. These functions are: +

    + + + +

    + And there are two more that XML_LARGE_SIZE was not covering: +

    + +
    @@ -790,16 +880,6 @@ the code that calls it; this is required to get all the right MSVC magic annotations correct. This is ignored on other platforms. - -
    - XML_ATTR_INFO -
    - -
    - If defined, makes the additional function XML_GetAttributeInfo available for reporting - attribute byte offsets. -

    @@ -1708,6 +1788,17 @@

    + Note: It is the responsibility of the application code to pass + a value for len that does not exceed the length of the remaining + unparsed XML content that was previously copied into the buffer obtained from + XML_GetBuffer. The documentation of + XML_GetBuffer below has an example of typical use of functions XML_GetBuffer and XML_ParseBuffer in tandem. +

    + +

    Negative values for len are rejected since Expat 2.6.3.

    @@ -1763,12 +1854,16 @@

    - Stops parsing, causing XML_Parse or - XML_ParseBuffer to return. Must be - called from within a call-back handler, except when aborting (when - resumable is XML_FALSE) an already suspended parser. - Some call-backs may still follow because they would otherwise get lost, - including + Stops parsing as soon as possible, causing XML_Parse or XML_ParseBuffer to return. Must be called from + within a call-back handler, except when aborting (when resumable + is XML_FALSE) an already suspended parser. +

    + +

    + Note: Some call-backs may still follow because they would + otherwise get lost, including

      @@ -2885,7 +2980,40 @@ XML_GetErrorCode(XML_Parser p);
      - Return what type of error has occurred. +

      + Return what type of error has occurred inside the main Expat parsing machinery. +

      + +

      + Only the following functions record the type of error for retrivial via + XML_GetErrorCode: +

      + +

      @@ -2903,13 +3031,18 @@

    - XML_GetCurrentByteIndex + XML_GetCurrentByteIndex (deprecated)

     XML_Index XMLCALL
     XML_GetCurrentByteIndex(XML_Parser p);
     
    +

    + Note: This function is deprecated, please use function XML_GetCurrentByteIndex64, instead. +

    +
    Return the byte offset of the position. This always corresponds to the values returned by XML_LARGE_SIZE.

    +

    + XML_GetCurrentByteIndex64 +

    + +
    +/* Added in Expat 2.9.0. */
    +int64_t XMLCALL
    +XML_GetCurrentByteIndex64(XML_Parser p);
    +
    +
    + Return the byte offset of the position. This always corresponds to the values + returned by XML_GetCurrentLineNumber64 and + XML_GetCurrentColumnNumber64. +
    +

    - XML_GetCurrentLineNumber + XML_GetCurrentLineNumber (deprecated)

     XML_Size XMLCALL
     XML_GetCurrentLineNumber(XML_Parser p);
     
    +

    + Note: This function is deprecated, please use function XML_GetCurrentLineNumber64, instead. +

    +
    Return the line number of the position. The first line is reported as 1. @@ -2944,14 +3099,33 @@ "#XML_LARGE_SIZE">XML_LARGE_SIZE.

    +

    + XML_GetCurrentLineNumber64 +

    + +
    +/* Added in Expat 2.9.0. */
    +uint64_t XMLCALL
    +XML_GetCurrentLineNumber64(XML_Parser p);
    +
    +
    + Return the line number of the position. The first line is reported as + 1. +
    +

    - XML_GetCurrentColumnNumber + XML_GetCurrentColumnNumber (deprecated)

     XML_Size XMLCALL
     XML_GetCurrentColumnNumber(XML_Parser p);
     
    +

    + Note: This function is deprecated, please use function XML_GetCurrentColumnNumber64, instead. +

    +
    Return the offset, from the beginning of the current line, of the position. The first column is reported as 0. @@ -2964,14 +3138,54 @@ "#XML_LARGE_SIZE">XML_LARGE_SIZE.

    +

    + XML_GetCurrentColumnNumber64 +

    + +
    +/* Added in Expat 2.9.0. */
    +uint64_t XMLCALL
    +XML_GetCurrentColumnNumber64(XML_Parser p);
    +
    +
    + Return the offset, from the beginning of the current line, of the + position. The first column is reported as 0. +
    +

    - XML_GetCurrentByteCount + XML_GetCurrentByteCount (deprecated)

     int XMLCALL
     XML_GetCurrentByteCount(XML_Parser p);
     
    +

    + Note: This function is deprecated, please use function XML_GetCurrentByteCount64, instead. +

    + +
    + Return the number of bytes in the current event. Returns 0 if the + event is inside a reference to an internal entity and for the end-tag event for + empty element tags (the later can be used to distinguish empty-element tags from + empty elements using separate start and end tags). +
    + +

    + Note: Type int is known to wrap around (on both + 32bit platforms and 64bit platforms). +

    + +

    + XML_GetCurrentByteCount64 +

    + +
    +/* Added in Expat 2.9.0. */
    +uint64_t XMLCALL
    +XML_GetCurrentByteCount64(XML_Parser p);
    +
    Return the number of bytes in the current event. Returns 0 if the event is inside a reference to an internal entity and for the end-tag event for @@ -2980,7 +3194,7 @@

    - XML_GetInputContext + XML_GetInputContext (deprecated)

    @@ -2989,6 +3203,11 @@
                         int *offset,
                         int *size);
     
    +

    + Note: This function is deprecated, please use function XML_GetInputContext64, instead. +

    +

    Returns the parser's input buffer, sets the integer pointed at by @@ -3013,6 +3232,295 @@ If XML_CONTEXT_BYTES is zero, this will always return NULL.

    + +

    + Note: Type int is known to wrap around (on both + 32bit platforms and 64bit platforms). +

    +
    + +

    + XML_GetInputContext64 +

    + +
    +/* Added in Expat 2.9.0. */
    +const char * XMLCALL
    +XML_GetInputContext64(XML_Parser p,
    +                    int64_t *offset,
    +                    zint64_t *size);
    +
    +
    +

    + Returns the parser's input buffer, sets the integer pointed at by + offset to the offset within this buffer of the current parse + position, and set the integer pointed at by size to the size of + the returned buffer. +

    + +

    + This should only be called from within a handler during an active parse and the + returned buffer should only be referred to from within the handler that made + the call. This input buffer contains the untranslated bytes of the input. +

    + +

    + Only a limited amount of context is kept, so if the event triggering a call + spans over a very large amount of input, the actual parse position may be + before the beginning of the buffer. +

    + +

    + If XML_CONTEXT_BYTES is zero, this will always return + NULL. +

    +
    + +

    + Properties / Security Tuning +

    + +

    + Expat 2.9.0 introduced a new Properties API. Think of properties as the control + knobs of a parser. The key motivations behind its introduction are: +

    + +
      +
    • to allow Expat to add more properties — both security-related and non- + security ones — in the future (without need to also add new related getter and + setter functions for each of them as was the case in the past), +
    • + +
    • to allow users to read (not just to set) the current values, and +
    • + +
    • to allow users to distinuish the different sources of error beyond a mere + "did not work". +
    • +
    + +

    + There is one setter function and one getter function — a pair — per supported + data type: +

    + + + +

    + They all share these two key enums: +

    + +

    + XML_Parser_Property +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Parser_Property {
    +#  if XML_GE == 1
    +  XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD = 1,   // of type `uint64_t`
    +  XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION = 2,  // of type `double`
    +  XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD = 3,  // of type `uint64_t`
    +  XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION = 4, // of type `double`
    +#  endif
    +  XML_PROP_REPARSE_DEFERRAL_ENABLED = 5, // of type `XML_Bool`
    +  /* more properties upcoming here */
    +};
    +

    + XML_Prop_Error +

    + +
    +/* Added in 2.9.0. */
    +enum XML_Prop_Error {
    +  XML_PROP_ERROR_NONE = 0, // i.e. success
    +
    +  XML_PROP_ERROR_INVALID_KEY = 1,   // i.e. the property is not known at all
    +  XML_PROP_ERROR_INVALID_TYPE = 2,  // i.e. the property is known but expects a
    +                                    // different type (so a different
    +                                    // getter/setter would need to be called)
    +  XML_PROP_ERROR_INVALID_VALUE = 3, // i.e. the value is invalid (with a setter
    +                                    // function) or a NULL-pointer (with a
    +                                    // getter function)
    +
    +  XML_PROP_ERROR_PARSER_NULL = 4,     // i.e. the parser argument is NULL
    +  XML_PROP_ERROR_PARSER_NOT_ROOT = 5, // i.e. the parser is not a root parser
    +                                      // but a subparser (and a root parser is
    +                                      // needed)
    +
    +  /* potentially more error codes upcoming here */
    +};
    +

    + Let us look at these functions in more detail: +

    + +

    + XML_GetPropertyBool +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Prop_Error
    +XML_GetPropertyBool(XML_Parser parser, enum XML_Parser_Property property,
    +                    XML_Bool *value);
    +
    +
    +

    + Writes the current value of boolean property property of parser + parser to output variable value. Supported properties + are: +

    + +
      +
    • + XML_PROP_REPARSE_DEFERRAL_ENABLED — whether reparse deferral is active or not +
    • +
    + +

    + Both parameters parser and value must be + non-NULL. On success XML_PROP_ERROR_NONE is returned; + on error one of the other variants of XML_Prop_Error is returned. +

    + +

    + Note: There may be more error codes introduced into XML_Prop_Error in the future, so user code must be robust + towards error codes from the future at runtime that were not known at compile + time. +

    +
    + +

    + XML_GetPropertyDouble +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Prop_Error
    +XML_GetPropertyDouble(XML_Parser parser, enum XML_Parser_Property property,
    +                      double *value);
    +
    +
    +

    + Writes the current value of floating point property property of + parser parser to output variable value. Supported + properties are: +

    + +
      +
    • + XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION — the maximum + tolerated amplification with the allocation tracker +
    • + +
    • + XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION — the maximum + tolerated amplification with billion laughs + attack protection +
    • +
    + +

    + Both parameters parser and value must be + non-NULL. On success XML_PROP_ERROR_NONE is returned; + on error one of the other variants of XML_Prop_Error is returned. +

    + +

    + Note: There may be more error codes introduced into XML_Prop_Error in the future, so user code must be robust + towards error codes from the future at runtime that were not known at compile + time. +

    +
    + +

    + XML_GetPropertyUInt64 +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Prop_Error
    +XML_GetPropertyUInt64(XML_Parser parser, enum XML_Parser_Property property,
    +                      uint64_t *value);
    +
    +
    +

    + Writes the current value of integral property property of parser + parser to output variable value. Supported properties + are: +

    + + + +

    + Both parameters parser and value must be + non-NULL. On success XML_PROP_ERROR_NONE is returned; + on error one of the other variants of XML_Prop_Error is returned. +

    + +

    + Note: There may be more error codes introduced into XML_Prop_Error in the future, so user code must be robust + towards error codes from the future at runtime that were not known at compile + time. +

    @@ -3020,6 +3528,138 @@ "billion-laughs" name="billion-laughs">

    +

    + XML_SetPropertyBool +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Prop_Error
    +XML_SetPropertyBool(XML_Parser parser, enum XML_Parser_Property property,
    +                    XML_Bool value);
    +
    +
    +

    + Applies the given value value to boolean property + property of parser parser. Supported properties are: +

    + +
      +
    • + XML_PROP_REPARSE_DEFERRAL_ENABLED — whether reparse deferral is active or not +
    • +
    + +

    + Parameter parser must be non-NULL. On success + XML_PROP_ERROR_NONE is returned; on error one of the other + variants of XML_Prop_Error is returned. +

    + +

    + Note: There may be more error codes introduced into XML_Prop_Error in the future, so user code must be robust + towards error codes from the future at runtime that were not known at compile + time. +

    +
    + +

    + XML_SetPropertyDouble +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Prop_Error
    +XML_SetPropertyDouble(XML_Parser parser, enum XML_Parser_Property property,
    +                      double value);
    +
    +
    +

    + Applies the given value value to floating point property + property of parser parser. Supported properties are: +

    + +
      +
    • + XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION — the maximum + tolerated amplification with the allocation tracker +
    • + +
    • + XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION — the maximum + tolerated amplification with billion laughs + attack protection +
    • +
    + +

    + Value value may or may not be cast to float + internally. +

    + +

    + Parameter parser must be non-NULL. On success + XML_PROP_ERROR_NONE is returned; on error one of the other + variants of XML_Prop_Error is returned. +

    + +

    + Note: There may be more error codes introduced into XML_Prop_Error in the future, so user code must be robust + towards error codes from the future at runtime that were not known at compile + time. +

    +
    + +

    + XML_SetPropertyUInt64 +

    + +
    +/* Added in Expat 2.9.0. */
    +enum XML_Prop_Error
    +XML_SetPropertyUInt64(XML_Parser parser, enum XML_Parser_Property property,
    +                      uint64_t value);
    +
    +
    +

    + Applies the given value value to integral property + property of parser parser. Supported properties are: +

    + + + +

    + Parameter parser must be non-NULL. On success + XML_PROP_ERROR_NONE is returned; on error one of the other + variants of XML_Prop_Error is returned. +

    + +

    + Note: There may be more error codes introduced into XML_Prop_Error in the future, so user code must be robust + towards error codes from the future at runtime that were not known at compile + time. +

    +
    +

    XML_SetBillionLaughsAttackProtectionMaximumAmplification

    @@ -3401,38 +4041,6 @@ means the current call.
    -

    - XML_GetAttributeInfo -

    - -
    -const XML_AttrInfo * XMLCALL
    -XML_GetAttributeInfo(XML_Parser parser);
    -
    - -
    -typedef struct {
    -  XML_Index  nameStart;  /* Offset to beginning of the attribute name. */
    -  XML_Index  nameEnd;    /* Offset after the attribute name's last byte. */
    -  XML_Index  valueStart; /* Offset to beginning of the attribute value. */
    -  XML_Index  valueEnd;   /* Offset after the attribute value's last byte. */
    -} XML_AttrInfo;
    -
    -
    - Returns an array of XML_AttrInfo structures for the attribute/value - pairs passed in the last call to the XML_StartElementHandler that - were specified in the start-tag rather than defaulted. Each attribute/value pair - counts as 1; thus the number of entries in the array is - XML_GetSpecifiedAttributeCount(parser) / 2. -
    - -

    - Note: Type XML_Index is known to wrap around on - 32bit platforms and 64bit Windows unless Expat has been compiled with - (non-default and uncommon) macro XML_LARGE_SIZE. -

    -

    XML_SetEncoding

    @@ -3491,6 +4099,11 @@ XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt); +

    + Note: This function is deprecated, please use function XML_SetHashSalt16Bytes, instead. +

    +
    Sets the hash salt to use for internal hash calculations. Helps in preventing DoS attacks based on predicting hash function behavior. In order to have an effect @@ -3498,15 +4111,13 @@ called after XML_Parse or XML_ParseBuffer or when parser is NULL.

    - Note: Function XML_SetHashSalt is - deprecated. Please use function XML_SetHashSalt16Bytes instead for better - security. XML_SetHashSalt only provides 4 to 8 bytes of entropy - (depending on the size of type unsigned long) while the SipHash - implementation used by Expat can leverage up to 16 bytes of entropy — at least - twice as much. Function Note: Function XML_SetHashSalt only provides 4 to 8 bytes + of entropy (depending on the size of type unsigned long) while the + SipHash implementation used by Expat can leverage up to 16 bytes of entropy — + at least twice as much. Function XML_SetHashSalt16Bytes of Expat >=2.8.0 - (and where backported) matches the amount of entropy supported by SipHash. + (and where backported) with better security matches the amount of entropy + supported by SipHash.

    @@ -3706,11 +4317,24 @@ XML_FEATURE_UNICODE_WCHAR_T, XML_FEATURE_DTD, XML_FEATURE_CONTEXT_BYTES, + /* Added in Expat 1.95.5, removed in Expat 2.9.0. */ XML_FEATURE_MIN_SIZE, XML_FEATURE_SIZEOF_XML_CHAR, XML_FEATURE_SIZEOF_XML_LCHAR, + /* Added in Expat 2.0.0. */ XML_FEATURE_NS, - XML_FEATURE_LARGE_SIZE + /* Added in Expat 2.0.1. */ + XML_FEATURE_LARGE_SIZE, + /* Added in Expat 2.1.0, removed in Expat 2.9.0. */ + XML_FEATURE_ATTR_INFO, + /* Added in Expat 2.4.0. */ + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + /* Added in Expat 2.6.0. */ + XML_FEATURE_GE, + /* Added in Expat 2.7.2. */ + XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, }; typedef struct { diff -Nru expat-2.8.3/expat/doc/xmlwf.xml expat-2.9.0/expat/doc/xmlwf.xml --- expat-2.8.3/expat/doc/xmlwf.xml 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/doc/xmlwf.xml 2026-10-05 15:43:59.000000000 +0000 @@ -21,7 +21,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [ Scott"> Bronson"> - August 10, 2026"> + October 5, 2026"> 1"> bronson@rinspin.com"> diff -Nru expat-2.8.3/expat/examples/element_declarations.c expat-2.9.0/expat/examples/element_declarations.c --- expat-2.8.3/expat/examples/element_declarations.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/examples/element_declarations.c 2026-10-05 15:43:59.000000000 +0000 @@ -12,7 +12,7 @@ Copyright (c) 2001-2003 Fred L. Drake, Jr. Copyright (c) 2004-2006 Karl Waclawek Copyright (c) 2005-2007 Steven Solie - Copyright (c) 2016-2024 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 Zhongyuan Zhou Copyright (c) 2024 Hanno Böck @@ -41,17 +41,12 @@ SPDX-License-Identifier: MIT */ +#include // PRIu64 #include #include #include #include -#ifdef XML_LARGE_SIZE -# define XML_FMT_INT_MOD "ll" -#else -# define XML_FMT_INT_MOD "l" -#endif - #ifdef XML_UNICODE_WCHAR_T # define XML_FMT_STR "ls" #else @@ -225,9 +220,8 @@ if (errorCode == XML_ERROR_ABORTED) { errorCode = XML_ERROR_NO_MEMORY; } - fprintf(stderr, - "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", - XML_GetCurrentLineNumber(parser), XML_ErrorString(errorCode)); + fprintf(stderr, "Parse error at line %" PRIu64 ":\n%" XML_FMT_STR "\n", + XML_GetCurrentLineNumber64(parser), XML_ErrorString(errorCode)); XML_ParserFree(parser); return 1; } diff -Nru expat-2.8.3/expat/examples/elements.c expat-2.9.0/expat/examples/elements.c --- expat-2.8.3/expat/examples/elements.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/examples/elements.c 2026-10-05 15:43:59.000000000 +0000 @@ -14,7 +14,7 @@ Copyright (c) 2001-2003 Fred L. Drake, Jr. Copyright (c) 2004-2006 Karl Waclawek Copyright (c) 2005-2007 Steven Solie - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 Zhongyuan Zhou Licensed under the MIT license: @@ -41,15 +41,10 @@ SPDX-License-Identifier: MIT */ +#include // PRIu64 #include #include -#ifdef XML_LARGE_SIZE -# define XML_FMT_INT_MOD "ll" -#else -# define XML_FMT_INT_MOD "l" -#endif - #ifdef XML_UNICODE_WCHAR_T # define XML_FMT_STR "ls" #else @@ -109,9 +104,8 @@ done = feof(stdin); if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) { - fprintf(stderr, - "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", - XML_GetCurrentLineNumber(parser), + fprintf(stderr, "Parse error at line %" PRIu64 ":\n%" XML_FMT_STR "\n", + XML_GetCurrentLineNumber64(parser), XML_ErrorString(XML_GetErrorCode(parser))); XML_ParserFree(parser); return 1; diff -Nru expat-2.8.3/expat/examples/outline.c expat-2.9.0/expat/examples/outline.c --- expat-2.8.3/expat/examples/outline.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/examples/outline.c 2026-10-05 15:43:59.000000000 +0000 @@ -12,7 +12,7 @@ Copyright (c) 2001-2003 Fred L. Drake, Jr. Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2006 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Licensed under the MIT license: @@ -38,15 +38,10 @@ SPDX-License-Identifier: MIT */ +#include // PRIu64 #include #include -#ifdef XML_LARGE_SIZE -# define XML_FMT_INT_MOD "ll" -#else -# define XML_FMT_INT_MOD "l" -#endif - #ifdef XML_UNICODE_WCHAR_T # define XML_FMT_STR "ls" #else @@ -112,9 +107,8 @@ done = feof(stdin); if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) { - fprintf(stderr, - "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", - XML_GetCurrentLineNumber(parser), + fprintf(stderr, "Parse error at line %" PRIu64 ":\n%" XML_FMT_STR "\n", + XML_GetCurrentLineNumber64(parser), XML_ErrorString(XML_GetErrorCode(parser))); XML_ParserFree(parser); return 1; diff -Nru expat-2.8.3/expat/expat_config.h.cmake expat-2.9.0/expat/expat_config.h.cmake --- expat-2.8.3/expat/expat_config.h.cmake 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/expat_config.h.cmake 2026-10-05 15:43:59.000000000 +0000 @@ -89,10 +89,6 @@ /* whether byteorder is bigendian */ #cmakedefine WORDS_BIGENDIAN -/* Define to allow retrieving the byte offsets for attribute names and values. - */ -#cmakedefine XML_ATTR_INFO - /* Define to specify how much context to retain around the current parse point, 0 to disable. */ # define XML_CONTEXT_BYTES @XML_CONTEXT_BYTES@ diff -Nru expat-2.8.3/expat/fix-xmltest-log.sh expat-2.9.0/expat/fix-xmltest-log.sh --- expat-2.8.3/expat/fix-xmltest-log.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/fix-xmltest-log.sh 2026-10-05 15:43:59.000000000 +0000 @@ -38,6 +38,9 @@ -e '# convert DOS line endings to Unix without resorting to dos2unix' \ -e $'s/\r//' \ \ + -e '# Filter out "unhandled instruction" lines from AddressSanitizer' \ + -e '/^==[0-9]\+==interception_win: unhandled instruction at /d' \ + \ -e 's/^wine: Call .* msvcrt\.dll\._wperror, aborting$/ibm49i02.dtd: No such file or directory/' \ \ -e '/^wine: /d' \ diff -Nru expat-2.8.3/expat/fuzz/xml_lpm_fuzzer.cpp expat-2.9.0/expat/fuzz/xml_lpm_fuzzer.cpp --- expat-2.8.3/expat/fuzz/xml_lpm_fuzzer.cpp 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/fuzz/xml_lpm_fuzzer.cpp 2026-10-05 15:43:59.000000000 +0000 @@ -8,6 +8,7 @@ Copyright (c) 2022 Mark Brand Copyright (c) 2025 Sebastian Pipping + Copyright (c) 2026 Braian Plaku Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -383,7 +384,9 @@ void InitializeParser(XML_Parser parser) { XML_SetUserData(parser, (void *)parser); - XML_SetHashSalt(parser, 0x41414141); + const uint8_t entropy[16] = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}; + XML_SetHashSalt16Bytes(parser, entropy); XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetElementDeclHandler(parser, ElementDeclHandler); diff -Nru expat-2.8.3/expat/fuzz/xml_parse_fuzzer.c expat-2.9.0/expat/fuzz/xml_parse_fuzzer.c --- expat-2.8.3/expat/fuzz/xml_parse_fuzzer.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/fuzz/xml_parse_fuzzer.c 2026-10-05 15:43:59.000000000 +0000 @@ -17,6 +17,7 @@ #include #include // for INT_MAX #include +#include #include "expat.h" #include "siphash.h" @@ -34,7 +35,8 @@ #endif // 16-byte deterministic hash key. -static unsigned char hash_key[16] = "FUZZING IS FUN!"; +static unsigned char hash_key_1[16] = "FUZZING IS FUN?"; +static unsigned char hash_key_2[16] = "FUZZING IS FUN!"; static void XMLCALL start(void *userData, const XML_Char *name, const XML_Char **atts) { @@ -59,8 +61,14 @@ static void ParseOneInput(XML_Parser p, const uint8_t *data, size_t size) { // Set the hash salt using siphash to generate a deterministic hash. - struct sipkey *key = sip_keyof(hash_key); - XML_SetHashSalt(p, (unsigned long)siphash24(data, size, key)); + // The salt is 16 bytes and siphash24 produces 8, so the input is hashed + // under two keys. + const uint64_t first = siphash24(data, size, sip_keyof(hash_key_1)); + const uint64_t second = siphash24(data, size, sip_keyof(hash_key_2)); + uint8_t entropy[16]; + memcpy(entropy, &first, sizeof(first)); + memcpy(entropy + sizeof(first), &second, sizeof(second)); + XML_SetHashSalt16Bytes(p, entropy); (void)sip24_valid; XML_SetUserData(p, p); @@ -71,7 +79,7 @@ if (XML_Parse(p, (const XML_Char *)data, (int)size, 1) == XML_STATUS_ERROR) { XML_ErrorString(XML_GetErrorCode(p)); } - XML_GetCurrentLineNumber(p); + XML_GetCurrentLineNumber64(p); if (size % 2) { XML_ParserReset(p, NULL); } diff -Nru expat-2.8.3/expat/fuzz/xml_parsebuffer_fuzzer.c expat-2.9.0/expat/fuzz/xml_parsebuffer_fuzzer.c --- expat-2.8.3/expat/fuzz/xml_parsebuffer_fuzzer.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/fuzz/xml_parsebuffer_fuzzer.c 2026-10-05 15:43:59.000000000 +0000 @@ -35,7 +35,8 @@ #endif // 16-byte deterministic hash key. -static unsigned char hash_key[16] = "FUZZING IS FUN!"; +static unsigned char hash_key_1[16] = "FUZZING IS FUN?"; +static unsigned char hash_key_2[16] = "FUZZING IS FUN!"; static void XMLCALL start(void *userData, const XML_Char *name, const XML_Char **atts) { @@ -60,8 +61,14 @@ static void ParseOneInput(XML_Parser p, const uint8_t *data, size_t size) { // Set the hash salt using siphash to generate a deterministic hash. - struct sipkey *key = sip_keyof(hash_key); - XML_SetHashSalt(p, (unsigned long)siphash24(data, size, key)); + // The salt is 16 bytes and siphash24 produces 8, so the input is hashed + // under two keys. + const uint64_t first = siphash24(data, size, sip_keyof(hash_key_1)); + const uint64_t second = siphash24(data, size, sip_keyof(hash_key_2)); + uint8_t entropy[16]; + memcpy(entropy, &first, sizeof(first)); + memcpy(entropy + sizeof(first), &second, sizeof(second)); + XML_SetHashSalt16Bytes(p, entropy); (void)sip24_valid; XML_SetUserData(p, p); @@ -80,7 +87,7 @@ if (XML_ParseBuffer(p, (int)size, 1) == XML_STATUS_ERROR) { XML_ErrorString(XML_GetErrorCode(p)); } - XML_GetCurrentLineNumber(p); + XML_GetCurrentLineNumber64(p); if (size % 2) { XML_ParserReset(p, NULL); } diff -Nru expat-2.8.3/expat/lib/.gitignore expat-2.9.0/expat/lib/.gitignore --- expat-2.8.3/expat/lib/.gitignore 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/.gitignore 2026-10-05 15:43:59.000000000 +0000 @@ -1,3 +1,4 @@ +/libexpat.map Makefile .libs *.lo diff -Nru expat-2.8.3/expat/lib/Makefile.am expat-2.9.0/expat/lib/Makefile.am --- expat-2.8.3/expat/lib/Makefile.am 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/Makefile.am 2026-10-05 15:43:59.000000000 +0000 @@ -53,6 +53,7 @@ endif libexpat_la_SOURCES = \ + xcs.c \ xmlparse.c \ xmltok.c \ xmlrole.c @@ -115,6 +116,7 @@ expat_external.h \ expat.h \ fallthrough.h \ + hash_table.h \ iasciitab.h \ internal.h \ latin1tab.h \ @@ -124,7 +126,7 @@ siphash.h \ utf8tab.h \ winconfig.h \ - xcsinc.c \ + xcs.h \ xmlrole.h \ xmltok.h \ xmltok_impl.c \ diff -Nru expat-2.8.3/expat/lib/expat.h expat-2.9.0/expat/lib/expat.h --- expat-2.8.3/expat/lib/expat.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/expat.h 2026-10-05 15:43:59.000000000 +0000 @@ -20,6 +20,7 @@ Copyright (c) 2023 Sony Corporation / Snild Dolkow Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> Copyright (c) 2025 Matthew Fernandez + Copyright (c) 2026 Braian Plaku Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -47,7 +48,7 @@ #ifndef Expat_INCLUDED # define Expat_INCLUDED 1 -# include // for uint8_t +# include // for int64_t, uint8_t, uint64_t # include # include "expat_external.h" @@ -139,6 +140,26 @@ XML_ERROR_NOT_STARTED, }; +/* Added in 2.9.0. */ +enum XML_Prop_Error { + XML_PROP_ERROR_NONE = 0, // i.e. success + + XML_PROP_ERROR_INVALID_KEY = 1, // i.e. the property is not known at all + XML_PROP_ERROR_INVALID_TYPE = 2, // i.e. the property is known but expects a + // different type (so a different + // getter/setter would need to be called) + XML_PROP_ERROR_INVALID_VALUE = 3, // i.e. the value is invalid (with a setter + // function) or a NULL-pointer (with a + // getter function) + + XML_PROP_ERROR_PARSER_NULL = 4, // i.e. the parser argument is NULL + XML_PROP_ERROR_PARSER_NOT_ROOT = 5, // i.e. the parser is not a root parser + // but a subparser (and a root parser is + // needed) + + /* potentially more error codes upcoming here */ +}; + enum XML_Content_Type { XML_CTYPE_EMPTY = 1, XML_CTYPE_ANY, @@ -756,29 +777,6 @@ XMLPARSEAPI(int) XML_GetIdAttributeIndex(XML_Parser parser); -# ifdef XML_ATTR_INFO -/* Source file byte offsets for the start and end of attribute names and values. - The value indices are exclusive of surrounding quotes; thus in a UTF-8 source - file an attribute value of "blah" will yield: - info->valueEnd - info->valueStart = 4 bytes. -*/ -typedef struct { - XML_Index nameStart; /* Offset to beginning of the attribute name. */ - XML_Index nameEnd; /* Offset after the attribute name's last byte. */ - XML_Index valueStart; /* Offset to beginning of the attribute value. */ - XML_Index valueEnd; /* Offset after the attribute value's last byte. */ -} XML_AttrInfo; - -/* Returns an array of XML_AttrInfo structures for the attribute/value pairs - passed in last call to the XML_StartElementHandler that were specified - in the start-tag rather than defaulted. Each attribute/value pair counts - as 1; thus the number of entries in the array is - XML_GetSpecifiedAttributeCount(parser) / 2. -*/ -XMLPARSEAPI(const XML_AttrInfo *) -XML_GetAttributeInfo(XML_Parser parser); -# endif - /* Parses some input. Returns XML_STATUS_ERROR if a fatal error is detected. The last call to XML_Parse must have isFinal true; len may be zero for this call (or any other). @@ -921,7 +919,9 @@ Returns 1 if successful, 0 when called after parsing has started. Note: If parser == NULL, the function will do nothing and return 0. DEPRECATED since Expat 2.8.0. + Please use XML_SetHashSalt16Bytes instead. */ +XML_ATTR_DEPRECATED("please use XML_SetHashSalt16Bytes instead") XMLPARSEAPI(int) XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt); @@ -961,15 +961,24 @@ return 0 to indicate an error. Note: XML_GetCurrentByteIndex returns -1 to indicate an error. */ +XML_ATTR_DEPRECATED("please use XML_GetCurrentLineNumber64 instead") XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(uint64_t) XML_GetCurrentLineNumber64(XML_Parser parser); +XML_ATTR_DEPRECATED("please use XML_GetCurrentColumnNumber64 instead") XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(uint64_t) XML_GetCurrentColumnNumber64(XML_Parser parser); +XML_ATTR_DEPRECATED("please use XML_GetCurrentByteIndex64 instead") XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); +XMLPARSEAPI(int64_t) XML_GetCurrentByteIndex64(XML_Parser parser); /* Return the number of bytes in the current event. Returns 0 if the event is in an internal entity. */ +XML_ATTR_DEPRECATED("please use XML_GetCurrentByteCount64 instead") XMLPARSEAPI(int) XML_GetCurrentByteCount(XML_Parser parser); +XMLPARSEAPI(uint64_t) +XML_GetCurrentByteCount64(XML_Parser parser); /* If XML_CONTEXT_BYTES is >=1, returns the input buffer, sets the integer pointed to by offset to the offset within this buffer @@ -981,8 +990,11 @@ NOTE: The character pointer returned should not be used outside the handler that makes the call. */ +XML_ATTR_DEPRECATED("please use XML_GetInputContext64 instead") XMLPARSEAPI(const char *) XML_GetInputContext(XML_Parser parser, int *offset, int *size); +XMLPARSEAPI(const char *) +XML_GetInputContext64(XML_Parser parser, int64_t *offset, uint64_t *size); /* For backwards compatibility with previous versions. */ # define XML_GetErrorLineNumber XML_GetCurrentLineNumber @@ -1037,11 +1049,15 @@ XML_FEATURE_UNICODE_WCHAR_T, XML_FEATURE_DTD, XML_FEATURE_CONTEXT_BYTES, + /* Added in Expat 1.95.5, removed in Expat 2.9.0. */ XML_FEATURE_MIN_SIZE, XML_FEATURE_SIZEOF_XML_CHAR, XML_FEATURE_SIZEOF_XML_LCHAR, + /* Added in Expat 2.0.0. */ XML_FEATURE_NS, + /* Added in Expat 2.0.1. */ XML_FEATURE_LARGE_SIZE, + /* Added in Expat 2.1.0, removed in Expat 2.9.0. */ XML_FEATURE_ATTR_INFO, /* Added in Expat 2.4.0. */ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, @@ -1063,6 +1079,41 @@ XMLPARSEAPI(const XML_Feature *) XML_GetFeatureList(void); +/* Added in Expat 2.9.0. */ +enum XML_Parser_Property { +# if defined(XML_TESTING) + XML_PROP_INVALID = 0, +# endif +# if XML_GE == 1 + XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD = 1, // of type `uint64_t` + XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION = 2, // of type `double` + XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD = 3, // of type `uint64_t` + XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION = 4, // of type `double` +# endif + XML_PROP_REPARSE_DEFERRAL_ENABLED = 5, // of type `XML_Bool` + /* more properties upcoming here */ +}; + +/* Added in Expat 2.9.0. */ +XMLPARSEAPI(enum XML_Prop_Error) +XML_GetPropertyBool(XML_Parser parser, enum XML_Parser_Property property, + XML_Bool *value); +XMLPARSEAPI(enum XML_Prop_Error) +XML_GetPropertyDouble(XML_Parser parser, enum XML_Parser_Property property, + double *value); +XMLPARSEAPI(enum XML_Prop_Error) +XML_GetPropertyUInt64(XML_Parser parser, enum XML_Parser_Property property, + uint64_t *value); +XMLPARSEAPI(enum XML_Prop_Error) +XML_SetPropertyBool(XML_Parser parser, enum XML_Parser_Property property, + XML_Bool value); +XMLPARSEAPI(enum XML_Prop_Error) +XML_SetPropertyDouble(XML_Parser parser, enum XML_Parser_Property property, + double value); +XMLPARSEAPI(enum XML_Prop_Error) +XML_SetPropertyUInt64(XML_Parser parser, enum XML_Parser_Property property, + uint64_t value); + # if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1) /* Added in Expat 2.4.0 for XML_DTD defined and * added in Expat 2.6.0 for XML_GE == 1. */ @@ -1095,8 +1146,8 @@ See https://semver.org */ # define XML_MAJOR_VERSION 2 -# define XML_MINOR_VERSION 8 -# define XML_MICRO_VERSION 3 +# define XML_MINOR_VERSION 9 +# define XML_MICRO_VERSION 0 # ifdef __cplusplus } diff -Nru expat-2.8.3/expat/lib/expat_external.h expat-2.9.0/expat/lib/expat_external.h --- expat-2.8.3/expat/lib/expat_external.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/expat_external.h 2026-10-05 15:43:59.000000000 +0000 @@ -16,6 +16,7 @@ Copyright (c) 2017 Rhodri James Copyright (c) 2018 Yury Gribov Copyright (c) 2026 Matthew Fernandez + Copyright (c) 2026 Braian Plaku Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -125,6 +126,17 @@ # define XML_ATTR_ALLOC_SIZE(x) # endif +/* Marks a function that Expat still provides but that callers should move off + of. */ +# if defined(__clang__) || defined(__GNUC__) +# define XML_ATTR_DEPRECATED(message) \ + __attribute__((__deprecated__(message))) +# elif defined(_MSC_VER) +# define XML_ATTR_DEPRECATED(message) __declspec(deprecated(message)) +# else +# define XML_ATTR_DEPRECATED(message) // empty i.e. no deprecation +# endif + # define XMLPARSEAPI(type) XMLIMPORT type XMLCALL # ifdef __cplusplus @@ -154,6 +166,9 @@ # endif /* XML_UNICODE */ # ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +# if defined(__clang__) || defined(__GNUC__) +# warning Macro XML_LARGE_SIZE is deprecated, please use the 64bit location API functions instead. +# endif typedef long long XML_Index; typedef unsigned long long XML_Size; # else diff -Nru expat-2.8.3/expat/lib/hash_table.h expat-2.9.0/expat/lib/hash_table.h --- expat-2.8.3/expat/lib/hash_table.h 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/lib/hash_table.h 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,78 @@ +/* Hash table related internal API + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#if ! defined(HASH_TABLE_H) +# define HASH_TABLE_H 1 + +# include "expat.h" // for XML_Bool, XML_Parser +# include "internal.h" // for XML_NONTESTING_STATIC + +# include // for size_t + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + XML_Parser parser; +} HASH_TABLE; + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +XML_NONTESTING_STATIC NAMED *lookupWithLength(XML_Parser parser, + HASH_TABLE *table, KEY name, + size_t nameLen, + size_t createSize); +XML_NONTESTING_STATIC NAMED *lookup(XML_Parser parser, HASH_TABLE *table, + KEY name, size_t createSize); + +XML_NONTESTING_STATIC void hashTableInit(HASH_TABLE *table, XML_Parser parser); +XML_NONTESTING_STATIC void hashTableClear(HASH_TABLE *table); +XML_NONTESTING_STATIC void hashTableDestroy(HASH_TABLE *table); +XML_NONTESTING_STATIC void hashTableIterInit(HASH_TABLE_ITER *iter, + const HASH_TABLE *table); +XML_NONTESTING_STATIC NAMED *hashTableIterNext(HASH_TABLE_ITER *iter); + +XML_NONTESTING_STATIC XML_Bool keyeq(KEY s1, size_t s1len, KEY s2); +XML_NONTESTING_STATIC size_t keylen(KEY s); + +#endif // ! defined(HASH_TABLE_H) diff -Nru expat-2.8.3/expat/lib/internal.h expat-2.9.0/expat/lib/internal.h --- expat-2.8.3/expat/lib/internal.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/internal.h 2026-10-05 15:43:59.000000000 +0000 @@ -6,13 +6,6 @@ The following calling convention macros are defined for frequently called functions: - FASTCALL - Used for those internal functions that have a simple - body and a low number of arguments and local variables. - - PTRCALL - Used for functions called though function pointers. - - PTRFASTCALL - Like PTRCALL, but for low number of arguments. - inline - Used for selected internal functions for which inlining may improve performance on some platforms. @@ -33,6 +26,9 @@ Copyright (c) 2019 David Loffredo Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> + Copyright (c) 2026 Matthew Wozniczka + Copyright (c) 2026 Braian Plaku + Copyright (c) 2026 Florian Schmaus Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -57,132 +53,102 @@ SPDX-License-Identifier: MIT */ -#if defined(__GNUC__) && defined(__i386__) && ! defined(__MINGW32__) -/* We'll use this version by default only where we know it helps. - - regparm() generates warnings on Solaris boxes. See SF bug #692878. - - Instability reported with egcs on a RedHat Linux 7.3. - Let's comment out: - #define FASTCALL __attribute__((stdcall, regparm(3))) - and let's try this: -*/ -# define FASTCALL __attribute__((regparm(3))) -# define PTRFASTCALL __attribute__((regparm(3))) -#endif - -/* Using __fastcall seems to have an unexpected negative effect under - MS VC++, especially for function pointers, so we won't use it for - now on that platform. It may be reconsidered for a future release - if it can be made more effective. - Likely reason: __fastcall on Windows is like stdcall, therefore - the compiler cannot perform stack optimizations for call clusters. -*/ - -/* Make sure all of these are defined if they aren't already. */ +#if ! defined(INTERNAL_H) +# define INTERNAL_H 1 -#ifndef FASTCALL -# define FASTCALL -#endif - -#ifndef PTRCALL -# define PTRCALL -#endif - -#ifndef PTRFASTCALL -# define PTRFASTCALL -#endif - -#ifndef XML_MIN_SIZE -# if ! defined(__cplusplus) && ! defined(inline) +# if ! defined(inline) # ifdef __GNUC__ # define inline __inline # endif /* __GNUC__ */ # endif -#endif /* XML_MIN_SIZE */ -#ifdef __cplusplus -# define inline inline -#else # ifndef inline # define inline # endif -#endif -#include // ULONG_MAX -#include // size_t +# if ! defined(XML_NONTESTING_STATIC) +# if defined(XML_TESTING) +# define XML_NONTESTING_STATIC // empty i.e. not static +# else +# define XML_NONTESTING_STATIC static +# endif +# endif -#if defined(_WIN32) \ - && (! defined(__USE_MINGW_ANSI_STDIO) \ - || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0)) -# define EXPAT_FMT_LLX(midpart) "%" midpart "I64x" -# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u" -# if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW -# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d" -# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u" +# include // ULONG_MAX +# include // size_t + +# if defined(_WIN32) \ + && (! defined(__USE_MINGW_ANSI_STDIO) \ + || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0)) +# define EXPAT_FMT_LLX(midpart) "%" midpart "I64x" +# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u" +# if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u" +# else +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d" +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u" +# endif # else -# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d" -# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u" +# include // PRIdPTR, PRIuPTR +# define EXPAT_FMT_LLX(midpart) "%" midpart "llx" +# define EXPAT_FMT_ULL(midpart) "%" midpart "llu" +# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart PRIdPTR +# define EXPAT_FMT_SIZE_T(midpart) "%" midpart PRIuPTR +# endif + +# ifndef UNUSED_P +# define UNUSED_P(p) (void)p # endif -#else -# include // PRIdPTR, PRIuPTR -# define EXPAT_FMT_LLX(midpart) "%" midpart "llx" -# define EXPAT_FMT_ULL(midpart) "%" midpart "llu" -# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart PRIdPTR -# define EXPAT_FMT_SIZE_T(midpart) "%" midpart PRIuPTR -#endif - -#ifndef UNUSED_P -# define UNUSED_P(p) (void)p -#endif /* NOTE BEGIN If you ever patch these defaults to greater values for non-attack XML payload in your environment, please file a bug report with libexpat. Thank you! */ -#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \ - 100.0f -#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ - 8388608 // 8 MiB, 2^23 - -#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f -#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ - 67108864 // 64 MiB, 2^26 +# define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \ + 100.0f +# define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ + 8388608 // 8 MiB, 2^23 + +# define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f +# define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ + 67108864 // 64 MiB, 2^26 // NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would // have to take sizeof(long double) into account -#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member -#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t)) +union expat_align { + long long l; + void *p; +}; +# define EXPAT_MALLOC_ALIGNMENT sizeof(union expat_align) +# define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t)) /* NOTE END */ -#include "expat.h" // so we can use type XML_Parser below - -#ifdef __cplusplus -extern "C" { -#endif +# include "expat.h" // so we can use type XML_Parser below void _INTERNAL_trim_to_complete_utf8_characters(const char *from, const char **fromLimRef); -#if defined(XML_GE) && XML_GE == 1 +# if defined(XML_GE) && XML_GE == 1 unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); const char *unsignedCharToPrintable(unsigned char c); -#endif +# endif extern -#if ! defined(XML_TESTING) +# if ! defined(XML_TESTING) const -#endif +# endif XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c -#if defined(XML_TESTING) +# if defined(XML_TESTING) + +int xmlSetHashSalt(XML_Parser parser, unsigned long hash_salt); + void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); void expat_free(XML_Parser parser, void *ptr, int sourceLine); void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); extern unsigned int g_bytesScanned; // used for testing only -#endif +# endif -#ifdef __cplusplus -} -#endif +#endif // not defined INTERNAL_H diff -Nru expat-2.8.3/expat/lib/libexpat.def.cmake expat-2.9.0/expat/lib/libexpat.def.cmake --- expat-2.8.3/expat/lib/libexpat.def.cmake 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/libexpat.def.cmake 2026-10-05 15:43:59.000000000 +0000 @@ -72,7 +72,7 @@ XML_ResumeParser @64 XML_GetParsingStatus @65 ; added with version 2.1.0 -@_EXPAT_COMMENT_ATTR_INFO@ XML_GetAttributeInfo @66 +; XML_GetAttributeInfo @66 XML_SetHashSalt @67 ; internal @68 removed with version 2.3.1 ; added with version 2.4.0 @@ -85,3 +85,15 @@ @_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73 ; added with version 2.8.0 XML_SetHashSalt16Bytes @74 +; added with version 2.9.0 + XML_GetCurrentByteCount64 @75 + XML_GetCurrentByteIndex64 @76 + XML_GetCurrentColumnNumber64 @77 + XML_GetCurrentLineNumber64 @78 + XML_GetInputContext64 @79 + XML_GetPropertyBool @80 + XML_GetPropertyDouble @81 + XML_GetPropertyUInt64 @82 + XML_SetPropertyBool @83 + XML_SetPropertyDouble @84 + XML_SetPropertyUInt64 @85 diff -Nru expat-2.8.3/expat/lib/libexpat.map.in expat-2.9.0/expat/lib/libexpat.map.in --- expat-2.8.3/expat/lib/libexpat.map.in 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/libexpat.map.in 2026-10-05 15:43:59.000000000 +0000 @@ -97,7 +97,7 @@ LIBEXPAT_2.1.0 { global: -@_EXPAT_COMMENT_ATTR_INFO@ XML_GetAttributeInfo; +# XML_GetAttributeInfo; XML_SetHashSalt; } LIBEXPAT_1.95.8; @@ -122,3 +122,18 @@ global: XML_SetHashSalt16Bytes; } LIBEXPAT_2.7.2; + +LIBEXPAT_2.9.0 { + global: + XML_GetCurrentByteCount64; + XML_GetCurrentByteIndex64; + XML_GetCurrentColumnNumber64; + XML_GetCurrentLineNumber64; + XML_GetInputContext64; + XML_GetPropertyBool; + XML_GetPropertyDouble; + XML_GetPropertyUInt64; + XML_SetPropertyBool; + XML_SetPropertyDouble; + XML_SetPropertyUInt64; +} LIBEXPAT_2.8.0; diff -Nru expat-2.8.3/expat/lib/random_getentropy.c expat-2.9.0/expat/lib/random_getentropy.c --- expat-2.8.3/expat/lib/random_getentropy.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/random_getentropy.c 2026-10-05 15:43:59.000000000 +0000 @@ -54,7 +54,7 @@ bool writeRandomBytes_getentropy(void *target, size_t count) { errno = 0; - const bool success = getentropy(target, count); + const bool success = (getentropy(target, count) == 0); // MSan does not understand `getentropy`, so explain its effects if (success) MSAN_UNPOISON(target, count); diff -Nru expat-2.8.3/expat/lib/xcs.c expat-2.9.0/expat/lib/xcs.c --- expat-2.8.3/expat/lib/xcs.c 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/lib/xcs.c 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,90 @@ +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2022-2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#include "xcs.h" + +#if defined(XML_UNICODE) +# if defined(XML_UNICODE_WCHAR_T) +# include // for wcscmp, wcslen, wcsncmp +# endif +#else +# include // for strcmp, strlen, strncmp +#endif + +size_t +xcslen(const XML_Char *s) { +#ifdef XML_UNICODE +# ifdef XML_UNICODE_WCHAR_T + return wcslen(s); +# else + // XML_Char is unsigned short + size_t len = 0; + while (s[len]) { + len++; + } + return len; +# endif +#else + return strlen(s); +#endif +} + +int +xcscmp(const XML_Char *a, const XML_Char *b) { +#if defined(XML_UNICODE) +# if defined(XML_UNICODE_WCHAR_T) + return wcscmp(a, b); +# else + for (; a[0] && b[0] && a[0] == b[0]; a++, b++) + ; + return a[0] - b[0]; +# endif +#else + return strcmp(a, b); +#endif +} + +int +xcsncmp(const XML_Char *a, const XML_Char *b, size_t len) { +#if defined(XML_UNICODE) +# if defined(XML_UNICODE_WCHAR_T) + return wcsncmp(a, b, len); +# else + for (; len > 0 && a[0] && b[0] && a[0] == b[0]; len--, a++, b++) { + } + return (len == 0) ? 0 : (a[0] - b[0]); +# endif +#else + return strncmp(a, b, len); +#endif +} diff -Nru expat-2.8.3/expat/lib/xcs.h expat-2.9.0/expat/lib/xcs.h --- expat-2.8.3/expat/lib/xcs.h 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/lib/xcs.h 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,44 @@ +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#if ! defined(EXPAT_XCS_H) + +# include // size_t +# include "expat_external.h" // for XML_Char + +extern size_t xcslen(const XML_Char *s); + +extern int xcscmp(const XML_Char *a, const XML_Char *b); +extern int xcsncmp(const XML_Char *a, const XML_Char *b, size_t len); + +#endif // ! defined(EXPAT_XCS_H) diff -Nru expat-2.8.3/expat/lib/xcsinc.c expat-2.9.0/expat/lib/xcsinc.c --- expat-2.8.3/expat/lib/xcsinc.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xcsinc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/* This file is included from other .c files! - __ __ _ - ___\ \/ /_ __ __ _| |_ - / _ \\ /| '_ \ / _` | __| - | __// \| |_) | (_| | |_ - \___/_/\_\ .__/ \__,_|\__| - |_| XML parser - - Copyright (c) 2022-2026 Sebastian Pipping - Licensed under the MIT license: - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to permit - persons to whom the Software is furnished to do so, subject to the - following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN - NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - USE OR OTHER DEALINGS IN THE SOFTWARE. - - SPDX-License-Identifier: MIT -*/ - -#if defined(XML_UNICODE) && defined(XML_UNICODE_WCHAR_T) -# include -#endif - -static size_t -xcslen(const XML_Char *s) { -#ifdef XML_UNICODE -# ifdef XML_UNICODE_WCHAR_T - return wcslen(s); -# else - // XML_Char is unsigned short - size_t len = 0; - while (s[len]) { - len++; - } - return len; -# endif -#else - return strlen(s); -#endif -} diff -Nru expat-2.8.3/expat/lib/xmlparse.c expat-2.9.0/expat/lib/xmlparse.c --- expat-2.8.3/expat/lib/xmlparse.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmlparse.c 2026-10-05 15:43:59.000000000 +0000 @@ -1,4 +1,4 @@ -/* ee5f82c3ffd57c5224394ba46f348dbce466d34d6c925a527ae46b1cfe6adf1d (2.8.3+) +/* e3ca845466942140c9af531ac57808c4650116df5d6dbeda3b6546d2548a586e (2.9.0+) __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| @@ -51,6 +51,13 @@ Copyright (c) 2026 Kartik Kenchi Copyright (c) 2026 Haris Hussain Copyright (c) 2026 Evgeny Kotkov + Copyright (c) 2026 Darren Carreras + Copyright (c) 2026 Alberto Maschietto + Copyright (c) 2026 Zeyou Liu + Copyright (c) 2026 Stan Ulbrych + Copyright (c) 2026 Braian Plaku + Copyright (c) 2026 Filippo Tedeschi + Copyright (c) 2026 Junki Lee Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -102,6 +109,7 @@ #include /* SIZE_MAX, UINT64_MAX, uint64_t, uintptr_t */ #include /* isnan */ #include +#include /* wcsncmp */ #ifdef _WIN32 # define getpid GetCurrentProcessId @@ -120,7 +128,7 @@ #include "ascii.h" #include "expat.h" #include "siphash.h" -#include "xcsinc.c" +#include "xcs.h" #if defined(HAVE_ARC4RANDOM) # include "random_arc4random.h" @@ -223,25 +231,10 @@ #define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) #include "internal.h" +#include "hash_table.h" #include "xmltok.h" #include "xmlrole.h" -typedef const XML_Char *KEY; - -typedef struct { - KEY name; -} NAMED; - -typedef struct { - NAMED **v; - unsigned char power; - size_t size; - size_t used; - XML_Parser parser; -} HASH_TABLE; - -static size_t keylen(KEY s); - static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key); /* For probing (after a collision) we need a step size relative prime @@ -257,11 +250,6 @@ #define PROBE_STEP(hash, mask, power) \ ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) -typedef struct { - NAMED **p; - NAMED **end; -} HASH_TABLE_ITER; - #define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ #define INIT_DATA_BUF_SIZE 1024 #define INIT_ATTS_SIZE 16 @@ -330,13 +318,13 @@ const XML_Char *base; const XML_Char *publicId; const XML_Char *notation; - XML_Bool open; - XML_Bool hasMore; /* true if entity has not been completely processed */ - /* An entity can be open while being already completely processed (hasMore == - XML_FALSE). The reason is the delayed closing of entities until their inner + bool open; + bool hasMore; /* true if entity has not been completely processed */ + /* An entity can be open while being already completely processed (!hasMore). + The reason is the delayed closing of entities until their inner entities are processed and closed */ - XML_Bool is_param; - XML_Bool is_internal; /* true if declared in internal subset outside PE */ + bool is_param; + bool is_internal; /* true if declared in internal subset outside PE */ } ENTITY; typedef struct { @@ -371,8 +359,8 @@ typedef struct attribute_id { XML_Char *name; PREFIX *prefix; - XML_Bool maybeTokenized; - XML_Bool xmlns; + bool maybeTokenized; + bool xmlns; } ATTRIBUTE_ID; typedef struct { @@ -381,6 +369,22 @@ const XML_Char *value; } DEFAULT_ATTRIBUTE; +// This structure allows mapping attribute names to instances of +// `DEFAULT_ATTRIBUTE`. +typedef struct { + // Member `name` goes first to make this structure compatible with structure + // `NAMED` (further up), which is needed to support use of structure + // `NAME_AND_DEFAULT_ATTRIBUTE` in a hash table as implemented by function + // `lookup` (further down). + const XML_Char *name; + // We would store a `DEFAULT_ATTRIBUTE *` here but the backing array + // can be reallocated which would invalidate the pointer. Using an index + // into the array instead, avoids that problem. + size_t attIndex; + // This is set to `false` by function `lookup`. + bool initialized; +} NAME_AND_DEFAULT_ATTRIBUTE; + typedef struct { unsigned long version; unsigned long hash; @@ -394,7 +398,7 @@ size_t nDefaultAtts; size_t allocDefaultAtts; DEFAULT_ATTRIBUTE *defaultAtts; - HASH_TABLE defaultAttsNames; + HASH_TABLE defaultAttForName; } ELEMENT_TYPE; typedef struct { @@ -412,12 +416,12 @@ XML_Bool standalone; #ifdef XML_DTD /* indicates if external PE has been read */ - XML_Bool paramEntityRead; + bool paramEntityRead; HASH_TABLE paramEntities; #endif /* XML_DTD */ PREFIX defaultPrefix; /* === scaffolding for building content model === */ - XML_Bool in_eldecl; + bool in_eldecl; CONTENT_SCAFFOLD *scaffold; unsigned contentStringLen; unsigned scaffSize; @@ -439,7 +443,7 @@ struct open_internal_entity *next; ENTITY *entity; int startTagLevel; - XML_Bool betweenDecl; /* WFC: PE Between Declarations */ + bool betweenDecl; /* WFC: PE Between Declarations */ enum EntityType type; } OPEN_INTERNAL_ENTITY; @@ -461,8 +465,8 @@ } ACCOUNTING; typedef struct MALLOC_TRACKER { - XmlBigCount bytesAllocated; - XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 + size_t bytesAllocated; + size_t peakBytesAllocated; // updated live only for debug level >=2 unsigned long debugLevel; float maximumAmplificationFactor; // >=1.0 XmlBigCount activationThresholdBytes; @@ -476,8 +480,8 @@ } ENTITY_STATS; #endif /* XML_GE == 1 */ -typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, - const char *end, const char **endPtr); +typedef enum XML_Error Processor(XML_Parser parser, const char *start, + const char *end, const char **endPtr); static Processor prologProcessor; static Processor prologInitProcessor; @@ -509,7 +513,7 @@ XML_Bool haveMore, XML_Bool allowClosingDoctype, enum XML_Account account); static enum XML_Error processEntity(XML_Parser parser, ENTITY *entity, - XML_Bool betweenDecl, enum EntityType type); + bool betweenDecl, enum EntityType type); static enum XML_Error doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, const char *start, const char *end, const char **endPtr, @@ -569,7 +573,7 @@ static const XML_Char *getContext(XML_Parser parser); static XML_Bool setContext(XML_Parser parser, const XML_Char *context); -static void FASTCALL normalizePublicId(XML_Char *s); +static void normalizePublicId(XML_Char *s); static DTD *dtdCreate(XML_Parser parser); /* do not call if m_parentParser != NULL */ @@ -579,34 +583,24 @@ XML_Parser parser); static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, STRING_POOL *newPool, const HASH_TABLE *oldTable); -static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, - size_t createSize); -static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser); -static void FASTCALL hashTableClear(HASH_TABLE *table); -static void FASTCALL hashTableDestroy(HASH_TABLE *table); -static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, - const HASH_TABLE *table); -static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); - -static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); -static void FASTCALL poolClear(STRING_POOL *pool); -static void FASTCALL poolDestroy(STRING_POOL *pool); + +static void poolInit(STRING_POOL *pool, XML_Parser parser); +static void poolClear(STRING_POOL *pool); +static void poolDestroy(STRING_POOL *pool); static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end); static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end); -static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); -static bool FASTCALL poolGrowUntil(STRING_POOL *pool, size_t needed); -static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool, - const XML_Char *s); -static const XML_Char *FASTCALL poolCopyStringNoFinish(STRING_POOL *pool, - const XML_Char *s); +static XML_Bool poolGrow(STRING_POOL *pool); +static bool poolGrowUntil(STRING_POOL *pool, size_t needed); +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char *poolCopyStringNoFinish(STRING_POOL *pool, + const XML_Char *s); static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); -static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool, - const XML_Char *s); +static const XML_Char *poolAppendString(STRING_POOL *pool, const XML_Char *s); -static int FASTCALL nextScaffoldPart(XML_Parser parser); +static int nextScaffoldPart(XML_Parser parser); static XML_Content *build_model(XML_Parser parser); static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, const char *end); @@ -655,6 +649,25 @@ static bool poolAppendChars(STRING_POOL *pool, const XML_Char *s, size_t len); +#if XML_GE == 1 +static enum XML_Prop_Error setBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor); + +static enum XML_Prop_Error setBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); + +static enum XML_Prop_Error +setAllocTrackerMaximumAmplification(XML_Parser parser, + float maximumAmplificationFactor); + +static enum XML_Prop_Error +setAllocTrackerActivationThreshold(XML_Parser parser, + unsigned long long activationThresholdBytes); +#endif /* XML_GE == 1 */ + +static enum XML_Prop_Error setReparseDeferralEnabled(XML_Parser parser, + XML_Bool enabled); + #define poolStart(pool) ((pool)->start) #define poolLength(pool) ((pool)->ptr - (pool)->start) #define poolChop(pool) ((void)--(pool->ptr)) @@ -755,6 +768,8 @@ void *m_unknownEncodingMem; void *m_unknownEncodingData; void *m_unknownEncodingHandlerData; + // Application callback invoked by callUnknownEncodingConvert. + int(XMLCALL *m_unknownEncodingConvert)(void *, const char *); void(XMLCALL *m_unknownEncodingRelease)(void *); PROLOG_STATE m_prologState; Processor *m_processor; @@ -792,9 +807,6 @@ NS_ATT *m_nsAtts; unsigned long m_nsAttsVersion; unsigned char m_nsAttsPower; -#ifdef XML_ATTR_INFO - XML_AttrInfo *m_attInfo; -#endif POSITION m_position; STRING_POOL m_tempPool; STRING_POOL m_temp2Pool; @@ -831,30 +843,30 @@ #if XML_GE == 1 static void -expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, - XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { +expat_heap_stat(XML_Parser rootParser, char operator, size_t absDiff, + size_t newTotal, size_t peakTotal, int sourceLine) { // NOTE: This can be +infinity or -nan const float amplification = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; fprintf( stderr, - "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( - "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", + "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_SIZE_T( + "10") " to " EXPAT_FMT_SIZE_T("10") " (" EXPAT_FMT_SIZE_T("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, absDiff, newTotal, peakTotal, (double)amplification, sourceLine); } static bool -expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, +expat_heap_increase_tolerable(XML_Parser rootParser, size_t increase, int sourceLine) { assert(rootParser != NULL); assert(increase > 0); - XmlBigCount newTotal = 0; + size_t newTotal = 0; bool tolerable = true; // Detect integer overflow - if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { + if (SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated < increase) { tolerable = false; } else { newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; @@ -894,8 +906,7 @@ const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size; - if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated - < bytesToAllocate) { + if (SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated < bytesToAllocate) { return NULL; // i.e. signal integer overflow as out-of-memory } @@ -1005,9 +1016,10 @@ } } - // NOTE: Integer overflow detection has already been done for us - // by expat_heap_increase_tolerable(..) above - assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size); + // Detect and prevent integer overflow + if (size > SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING) { + return NULL; + } // Actually allocate mallocedPtr = parser->m_mem.realloc_fcn( @@ -1019,8 +1031,7 @@ // Update accounting if (isIncrease) { - assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated - >= absDiff); + assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= absDiff); rootParser->m_alloc_tracker.bytesAllocated += absDiff; } else { // i.e. decrease assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); @@ -1177,6 +1188,25 @@ return parser->m_handlerCallDepth > 0; } +static void +callUnknownEncodingRelease(XML_Parser parser) { + beforeHandler(parser); + parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); + afterHandler(parser); + parser->m_unknownEncodingRelease = NULL; + parser->m_unknownEncodingData = NULL; +} + +static int XMLCALL +callUnknownEncodingConvert(void *data, const char *p) { + XML_Parser parser = data; + beforeHandler(parser); + const int result + = parser->m_unknownEncodingConvert(parser->m_unknownEncodingData, p); + afterHandler(parser); + return result; +} + static enum XML_Error callProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { @@ -1377,20 +1407,9 @@ FREE(parser, parser); return NULL; } -#ifdef XML_ATTR_INFO - parser->m_attInfo = MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo)); - if (parser->m_attInfo == NULL) { - FREE(parser, parser->m_atts); - FREE(parser, parser); - return NULL; - } -#endif parser->m_dataBuf = MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char)); if (parser->m_dataBuf == NULL) { FREE(parser, parser->m_atts); -#ifdef XML_ATTR_INFO - FREE(parser, parser->m_attInfo); -#endif FREE(parser, parser); return NULL; } @@ -1403,9 +1422,6 @@ if (parser->m_dtd == NULL) { FREE(parser, parser->m_dataBuf); FREE(parser, parser->m_atts); -#ifdef XML_ATTR_INFO - FREE(parser, parser->m_attInfo); -#endif FREE(parser, parser); return NULL; } @@ -1524,6 +1540,7 @@ parser->m_inheritedBindings = NULL; parser->m_nSpecifiedAtts = 0; parser->m_unknownEncodingMem = NULL; + parser->m_unknownEncodingConvert = NULL; parser->m_unknownEncodingRelease = NULL; parser->m_unknownEncodingData = NULL; parser->m_parsingStatus.parsing = XML_INITIALIZED; @@ -1553,7 +1570,7 @@ } /* moves list of bindings to m_freeBindingList */ -static void FASTCALL +static void moveToFreeBindingList(XML_Parser parser, BINDING *bindings) { while (bindings) { BINDING *b = bindings; @@ -1583,6 +1600,10 @@ if (parser->m_parentParser) return XML_FALSE; + // The application-defined release callback may access the parser, so it + // must run before any parser state is freed. + if (parser->m_unknownEncodingRelease) + callUnknownEncodingRelease(parser); /* move m_tagStack to m_freeTagList */ tStk = parser->m_tagStack; while (tStk) { @@ -1603,14 +1624,16 @@ moveEntityList(&parser->m_freeEntities, &parser->m_openValueEntities); moveToFreeBindingList(parser, parser->m_inheritedBindings); FREE(parser, parser->m_unknownEncodingMem); - if (parser->m_unknownEncodingRelease) - parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); poolClear(&parser->m_tempPool); poolClear(&parser->m_temp2Pool); FREE(parser, (void *)parser->m_protocolEncodingName); parser->m_protocolEncodingName = NULL; parserInit(parser, encodingName); dtdReset(parser->m_dtd, parser); + if (encodingName && ! parser->m_protocolEncodingName) { + parser->m_errorCode = XML_ERROR_NO_MEMORY; + return XML_FALSE; + } return XML_TRUE; } @@ -1826,7 +1849,7 @@ return parser; } -static void FASTCALL +static void destroyBindings(BINDING *bindings, XML_Parser parser) { for (;;) { BINDING *b = bindings; @@ -1843,6 +1866,10 @@ TAG *tagList; if ((parser == NULL) || isCalledFromInsideHandler(parser)) return; + // The application-defined release callback may access the parser, so it + // must run before any parser state is freed. + if (parser->m_unknownEncodingRelease) + callUnknownEncodingRelease(parser); /* free m_tagStack and m_freeTagList */ tagList = parser->m_tagStack; for (;;) { @@ -1903,9 +1930,6 @@ #endif /* XML_DTD */ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); FREE(parser, parser->m_atts); -#ifdef XML_ATTR_INFO - FREE(parser, parser->m_attInfo); -#endif FREE(parser, parser->m_groupConnector); // NOTE: We are avoiding FREE(..) here because parser->m_buffer // is not being allocated with MALLOC(..) but with plain @@ -1914,8 +1938,6 @@ FREE(parser, parser->m_dataBuf); FREE(parser, parser->m_nsAtts); FREE(parser, parser->m_unknownEncodingMem); - if (parser->m_unknownEncodingRelease) - parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); FREE(parser, parser); } @@ -1996,15 +2018,6 @@ return parser->m_idAttIndex; } -#ifdef XML_ATTR_INFO -const XML_AttrInfo *XMLCALL -XML_GetAttributeInfo(XML_Parser parser) { - if (parser == NULL) - return NULL; - return parser->m_attInfo; -} -#endif - void XMLCALL XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end) { @@ -2225,9 +2238,10 @@ #endif } -// DEPRECATED since Expat 2.8.0. -int XMLCALL -XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { +/* The body of XML_SetHashSalt, so that Expat's own tests can reach it + without tripping the deprecation of the public function. */ +XML_NONTESTING_STATIC int +xmlSetHashSalt(XML_Parser parser, unsigned long hash_salt) { if (parser == NULL) return 0; @@ -2253,6 +2267,12 @@ return 1; } +// DEPRECATED since Expat 2.8.0. +int XMLCALL +XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { + return xmlSetHashSalt(parser, hash_salt); +} + XML_Bool XMLCALL XML_SetHashSalt16Bytes(XML_Parser parser, const uint8_t entropy[16]) { if (parser == NULL) @@ -2426,6 +2446,11 @@ parser->m_parsingStatus.parsing = XML_PARSING; } + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { + parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT; + return XML_STATUS_ERROR; + } + // Detect and avoid integer overflow if ((uint64_t)len > UINT64_MAX - parser->m_parseEndByteIndex) { parser->m_errorCode = XML_ERROR_NO_MEMORY; @@ -2685,41 +2710,57 @@ return parser->m_errorCode; } -XML_Index XMLCALL -XML_GetCurrentByteIndex(XML_Parser parser) { +int64_t XMLCALL +XML_GetCurrentByteIndex64(XML_Parser parser) { if (parser == NULL) return -1; if (parser->m_eventPtr) { - // NOTE: XML_Index is known to wrap around for >2 GiB content - // on 32bit machines and 64bit Windows, unless (non-default and - // uncommon) XML_LARGE_SIZE is defined. - // That's a bug and it only lives on because we cannot break - // ABI compatibility of public API. - return (XML_Index)(parser->m_parseEndByteIndex - - (parser->m_parseEndPtr - parser->m_eventPtr)); + return (int64_t)(parser->m_parseEndByteIndex + - (parser->m_parseEndPtr - parser->m_eventPtr)); } return -1; } -int XMLCALL -XML_GetCurrentByteCount(XML_Parser parser) { +// DEPRECATED since Expat 2.9.0. +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) { + // NOTE: XML_Index is known to wrap around for >2 GiB content + // on 32bit machines and 64bit Windows, unless (non-default and + // uncommon) XML_LARGE_SIZE is defined. + // That's a bug and it only lives on because we cannot break + // ABI compatibility of public API. + return (XML_Index)XML_GetCurrentByteIndex64(parser); +} + +uint64_t XMLCALL +XML_GetCurrentByteCount64(XML_Parser parser) { if (parser == NULL) return 0; - if (parser->m_eventEndPtr && parser->m_eventPtr) - return (int)(parser->m_eventEndPtr - parser->m_eventPtr); + if (parser->m_eventEndPtr && parser->m_eventPtr) { + return parser->m_eventEndPtr - parser->m_eventPtr; + } return 0; } +// DEPRECATED since Expat 2.9.0. +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) { + // NOTE: int is known to wrap around for >2 GiB content. + // That's a bug and it only lives on because we cannot break + // ABI compatibility of public API. + return (int)XML_GetCurrentByteCount64(parser); +} + const char *XMLCALL -XML_GetInputContext(XML_Parser parser, int *offset, int *size) { +XML_GetInputContext64(XML_Parser parser, int64_t *offset, uint64_t *size) { #if XML_CONTEXT_BYTES > 0 if (parser == NULL) return NULL; if (parser->m_eventPtr && parser->m_buffer) { if (offset != NULL) - *offset = (int)(parser->m_eventPtr - parser->m_buffer); + *offset = parser->m_eventPtr - parser->m_buffer; if (size != NULL) - *size = (int)(parser->m_bufferEnd - parser->m_buffer); + *size = parser->m_bufferEnd - parser->m_buffer; return parser->m_buffer; } #else @@ -2730,8 +2771,41 @@ return NULL; } -XML_Size XMLCALL -XML_GetCurrentLineNumber(XML_Parser parser) { +// DEPRECATED since Expat 2.9.0. +const char *XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) { +#if XML_CONTEXT_BYTES > 0 + if (parser == NULL) + return NULL; + + int64_t offset64; + uint64_t size64; + + const char *const buffer = XML_GetInputContext64(parser, &offset64, &size64); + + if (buffer == NULL) + return NULL; + + // NOTE: int is known to wrap around for >2 GiB content. + // That's a bug and it only lives on because we cannot break + // ABI compatibility of public API. + if (offset != NULL) + *offset = (int)offset64; + + if (size != NULL) + *size = (int)size64; + + return buffer; +#else + (void)parser; + (void)offset; + (void)size; +#endif /* XML_CONTEXT_BYTES > 0 */ + return NULL; +} + +uint64_t XMLCALL +XML_GetCurrentLineNumber64(XML_Parser parser) { if (parser == NULL) return 0; if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) { @@ -2739,16 +2813,22 @@ parser->m_eventPtr, &parser->m_position); parser->m_positionPtr = parser->m_eventPtr; } - // NOTE: XML_Size is known to wrap around for >2 4iB content + return parser->m_position.lineNumber + 1; +} + +// DEPRECATED since Expat 2.9.0. +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) { + // NOTE: XML_Size is known to wrap around for >4 GiB content // on 32bit machines and 64bit Windows, unless (non-default and // uncommon) XML_LARGE_SIZE is defined. // That's a bug and it only lives on because we cannot break // ABI compatibility of public API. - return (XML_Size)(parser->m_position.lineNumber + 1); + return (XML_Size)XML_GetCurrentLineNumber64(parser); } -XML_Size XMLCALL -XML_GetCurrentColumnNumber(XML_Parser parser) { +uint64_t XMLCALL +XML_GetCurrentColumnNumber64(XML_Parser parser) { if (parser == NULL) return 0; if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) { @@ -2756,12 +2836,18 @@ parser->m_eventPtr, &parser->m_position); parser->m_positionPtr = parser->m_eventPtr; } - // NOTE: XML_Size is known to wrap around for >2 4iB content + return parser->m_position.columnNumber; +} + +// DEPRECATED since Expat 2.9.0. +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) { + // NOTE: XML_Size is known to wrap around for >4 GiB content // on 32bit machines and 64bit Windows, unless (non-default and // uncommon) XML_LARGE_SIZE is defined. // That's a bug and it only lives on because we cannot break // ABI compatibility of public API. - return (XML_Size)parser->m_position.columnNumber; + return (XML_Size)XML_GetCurrentColumnNumber64(parser); } void XMLCALL @@ -2807,7 +2893,7 @@ void XMLCALL XML_DefaultCurrent(XML_Parser parser) { - if (parser == NULL) + if (parser == NULL || ! isCalledFromInsideHandler(parser)) return; if (parser->m_defaultHandler) { if (parser->m_openInternalEntities) @@ -2978,18 +3064,12 @@ {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), XML_CONTEXT_BYTES}, #endif -#ifdef XML_MIN_SIZE - {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, -#endif #ifdef XML_NS {XML_FEATURE_NS, XML_L("XML_NS"), 0}, #endif #ifdef XML_LARGE_SIZE {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, #endif -#ifdef XML_ATTR_INFO - {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, -#endif #if XML_GE == 1 /* Added in Expat 2.4.0 for XML_DTD defined and * added in Expat 2.6.0 for XML_GE == 1. */ @@ -3016,59 +3096,294 @@ } #if XML_GE == 1 +static enum XML_Prop_Error +setBillionLaughsAttackProtectionMaximumAmplification( + XML_Parser parser, float maximumAmplificationFactor) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (parser->m_parentParser != NULL) + return XML_PROP_ERROR_PARSER_NOT_ROOT; + + if (isnan(maximumAmplificationFactor) || (maximumAmplificationFactor < 1.0f)) + return XML_PROP_ERROR_INVALID_VALUE; + + parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor; + + return XML_PROP_ERROR_NONE; +} + XML_Bool XMLCALL XML_SetBillionLaughsAttackProtectionMaximumAmplification( XML_Parser parser, float maximumAmplificationFactor) { - if ((parser == NULL) || (parser->m_parentParser != NULL) - || isnan(maximumAmplificationFactor) - || (maximumAmplificationFactor < 1.0f)) { - return XML_FALSE; - } - parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor; - return XML_TRUE; + return (setBillionLaughsAttackProtectionMaximumAmplification( + parser, maximumAmplificationFactor) + == XML_PROP_ERROR_NONE) + ? XML_TRUE + : XML_FALSE; +} + +static enum XML_Prop_Error +setBillionLaughsAttackProtectionActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (parser->m_parentParser != NULL) + return XML_PROP_ERROR_PARSER_NOT_ROOT; + + parser->m_accounting.activationThresholdBytes = activationThresholdBytes; + + return XML_PROP_ERROR_NONE; } XML_Bool XMLCALL XML_SetBillionLaughsAttackProtectionActivationThreshold( XML_Parser parser, unsigned long long activationThresholdBytes) { - if ((parser == NULL) || (parser->m_parentParser != NULL)) { - return XML_FALSE; - } - parser->m_accounting.activationThresholdBytes = activationThresholdBytes; - return XML_TRUE; + return (setBillionLaughsAttackProtectionActivationThreshold( + parser, activationThresholdBytes) + == XML_PROP_ERROR_NONE) + ? XML_TRUE + : XML_FALSE; +} + +static enum XML_Prop_Error +setAllocTrackerMaximumAmplification(XML_Parser parser, + float maximumAmplificationFactor) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (parser->m_parentParser != NULL) + return XML_PROP_ERROR_PARSER_NOT_ROOT; + + if (isnan(maximumAmplificationFactor) || (maximumAmplificationFactor < 1.0f)) + return XML_PROP_ERROR_INVALID_VALUE; + + parser->m_alloc_tracker.maximumAmplificationFactor + = maximumAmplificationFactor; + + return XML_PROP_ERROR_NONE; } XML_Bool XMLCALL XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, float maximumAmplificationFactor) { - if ((parser == NULL) || (parser->m_parentParser != NULL) - || isnan(maximumAmplificationFactor) - || (maximumAmplificationFactor < 1.0f)) { - return XML_FALSE; - } - parser->m_alloc_tracker.maximumAmplificationFactor - = maximumAmplificationFactor; - return XML_TRUE; + return (setAllocTrackerMaximumAmplification(parser, + maximumAmplificationFactor) + == XML_PROP_ERROR_NONE) + ? XML_TRUE + : XML_FALSE; +} + +static enum XML_Prop_Error +setAllocTrackerActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (parser->m_parentParser != NULL) + return XML_PROP_ERROR_PARSER_NOT_ROOT; + + parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; + + return XML_PROP_ERROR_NONE; } XML_Bool XMLCALL XML_SetAllocTrackerActivationThreshold( XML_Parser parser, unsigned long long activationThresholdBytes) { - if ((parser == NULL) || (parser->m_parentParser != NULL)) { - return XML_FALSE; - } - parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; - return XML_TRUE; + return (setAllocTrackerActivationThreshold(parser, activationThresholdBytes) + == XML_PROP_ERROR_NONE) + ? XML_TRUE + : XML_FALSE; } #endif /* XML_GE == 1 */ +static enum XML_Prop_Error +setReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (enabled != XML_TRUE && enabled != XML_FALSE) + return XML_PROP_ERROR_INVALID_VALUE; + + parser->m_reparseDeferralEnabled = enabled; + + return XML_PROP_ERROR_NONE; +} + XML_Bool XMLCALL XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { - if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) { - parser->m_reparseDeferralEnabled = enabled; - return XML_TRUE; + return (setReparseDeferralEnabled(parser, enabled) == XML_PROP_ERROR_NONE) + ? XML_TRUE + : XML_FALSE; +} + +enum XML_Prop_Error XMLCALL +XML_SetPropertyBool(XML_Parser parser, enum XML_Parser_Property property, + XML_Bool value) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + switch (property) { +#if XML_GE == 1 + case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD: + case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION: + case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD: + case XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION: + return XML_PROP_ERROR_INVALID_TYPE; +#endif /* XML_GE == 1 */ + case XML_PROP_REPARSE_DEFERRAL_ENABLED: + return setReparseDeferralEnabled(parser, value); + default: + return XML_PROP_ERROR_INVALID_KEY; + } + + assert(0 && "considered unreachable"); +} + +enum XML_Prop_Error XMLCALL +XML_SetPropertyDouble(XML_Parser parser, enum XML_Parser_Property property, + double value) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + +#if XML_GE == 0 + UNUSED_P(value); +#endif + + switch (property) { +#if XML_GE == 1 + case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION: + return setAllocTrackerMaximumAmplification(parser, (float)value); + case XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION: + return setBillionLaughsAttackProtectionMaximumAmplification(parser, + (float)value); + case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD: + case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD: +#endif /* XML_GE == 1 */ + case XML_PROP_REPARSE_DEFERRAL_ENABLED: + return XML_PROP_ERROR_INVALID_TYPE; + default: + return XML_PROP_ERROR_INVALID_KEY; + } + + assert(0 && "considered unreachable"); +} + +enum XML_Prop_Error XMLCALL +XML_SetPropertyUInt64(XML_Parser parser, enum XML_Parser_Property property, + uint64_t value) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + +#if XML_GE == 0 + UNUSED_P(value); +#endif + + switch (property) { +#if XML_GE == 1 + case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD: + return setAllocTrackerActivationThreshold(parser, + (unsigned long long)value); + case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD: + return setBillionLaughsAttackProtectionActivationThreshold( + parser, (unsigned long long)value); + case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION: + case XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION: +#endif /* XML_GE == 1 */ + case XML_PROP_REPARSE_DEFERRAL_ENABLED: + return XML_PROP_ERROR_INVALID_TYPE; + default: + return XML_PROP_ERROR_INVALID_KEY; + } + + assert(0 && "considered unreachable"); +} + +enum XML_Prop_Error XMLCALL +XML_GetPropertyBool(XML_Parser parser, enum XML_Parser_Property property, + XML_Bool *value) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (value == NULL) + return XML_PROP_ERROR_INVALID_VALUE; + + switch (property) { +#if XML_GE == 1 + case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD: + case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION: + case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD: + case XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION: + return XML_PROP_ERROR_INVALID_TYPE; +#endif /* XML_GE == 1 */ + case XML_PROP_REPARSE_DEFERRAL_ENABLED: + *value = parser->m_reparseDeferralEnabled; + break; + default: + return XML_PROP_ERROR_INVALID_KEY; + } + + return XML_PROP_ERROR_NONE; +} + +enum XML_Prop_Error XMLCALL +XML_GetPropertyDouble(XML_Parser parser, enum XML_Parser_Property property, + double *value) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (value == NULL) + return XML_PROP_ERROR_INVALID_VALUE; + + switch (property) { +#if XML_GE == 1 + case XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION: + *value = (double)parser->m_accounting.maximumAmplificationFactor; + break; + case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION: + *value = (double)parser->m_alloc_tracker.maximumAmplificationFactor; + break; + case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD: + case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD: +#endif /* XML_GE == 1 */ + case XML_PROP_REPARSE_DEFERRAL_ENABLED: + return XML_PROP_ERROR_INVALID_TYPE; + default: + return XML_PROP_ERROR_INVALID_KEY; + } + + return XML_PROP_ERROR_NONE; +} + +enum XML_Prop_Error XMLCALL +XML_GetPropertyUInt64(XML_Parser parser, enum XML_Parser_Property property, + uint64_t *value) { + if (parser == NULL) + return XML_PROP_ERROR_PARSER_NULL; + + if (value == NULL) + return XML_PROP_ERROR_INVALID_VALUE; + + switch (property) { +#if XML_GE == 1 + case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD: + *value = parser->m_accounting.activationThresholdBytes; + break; + case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD: + *value = parser->m_alloc_tracker.activationThresholdBytes; + break; + case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION: + case XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION: +#endif /* XML_GE == 1 */ + case XML_PROP_REPARSE_DEFERRAL_ENABLED: + return XML_PROP_ERROR_INVALID_TYPE; + default: + return XML_PROP_ERROR_INVALID_KEY; } - return XML_FALSE; + + return XML_PROP_ERROR_NONE; } /* Initially tag->rawName always points into the parse buffer; @@ -3125,7 +3440,7 @@ return XML_TRUE; } -static enum XML_Error PTRCALL +static enum XML_Error contentProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { enum XML_Error result = doContent( @@ -3139,7 +3454,7 @@ return result; } -static enum XML_Error PTRCALL +static enum XML_Error externalEntityInitProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { enum XML_Error result = initializeEncoding(parser); @@ -3149,7 +3464,7 @@ return externalEntityInitProcessor2(parser, start, end, endPtr); } -static enum XML_Error PTRCALL +static enum XML_Error externalEntityInitProcessor2(XML_Parser parser, const char *start, const char *end, const char **endPtr) { const char *next = start; /* XmlContentTok doesn't always set the last arg */ @@ -3194,7 +3509,7 @@ return externalEntityInitProcessor3(parser, start, end, endPtr); } -static enum XML_Error PTRCALL +static enum XML_Error externalEntityInitProcessor3(XML_Parser parser, const char *start, const char *end, const char **endPtr) { int tok; @@ -3246,7 +3561,7 @@ return externalEntityContentProcessor(parser, start, end, endPtr); } -static enum XML_Error PTRCALL +static enum XML_Error externalEntityContentProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { enum XML_Error result @@ -3410,9 +3725,9 @@ return result; } else if (parser->m_externalEntityRefHandler) { const XML_Char *context; - entity->open = XML_TRUE; + entity->open = true; context = getContext(parser); - entity->open = XML_FALSE; + entity->open = false; if (! context) return XML_ERROR_NO_MEMORY; beforeHandler(parser); @@ -3837,8 +4152,8 @@ sizeof(ELEMENT_TYPE)); if (! elementType) return XML_ERROR_NO_MEMORY; - if (! elementType->defaultAttsNames.parser) - hashTableInit(&(elementType->defaultAttsNames), parser); + if (! elementType->defaultAttForName.parser) + hashTableInit(&(elementType->defaultAttForName), parser); if (parser->m_ns && ! setElementTypePrefix(parser, elementType)) return XML_ERROR_NO_MEMORY; } @@ -3881,21 +4196,6 @@ return XML_ERROR_NO_MEMORY; } parser->m_atts = temp; -#ifdef XML_ATTR_INFO - /* Detect and prevent integer overflow. */ - if (parser->m_attsSize > SIZE_MAX / sizeof(XML_AttrInfo)) { - parser->m_attsSize = oldAttsSize; - return XML_ERROR_NO_MEMORY; - } - - XML_AttrInfo *const temp2 = REALLOC( - parser, parser->m_attInfo, parser->m_attsSize * sizeof(XML_AttrInfo)); - if (temp2 == NULL) { - parser->m_attsSize = oldAttsSize; - return XML_ERROR_NO_MEMORY; - } - parser->m_attInfo = temp2; -#endif if (n > oldAttsSize) { /* Detect and prevent integer overflow. */ if (n > (size_t)INT_MAX) @@ -3908,33 +4208,12 @@ const XML_Char **const appAtts = (const XML_Char **)parser->m_atts; for (size_t i = 0; i < n; i++) { ATTRIBUTE *currAtt = &parser->m_atts[i]; -#ifdef XML_ATTR_INFO - XML_AttrInfo *currAttInfo = &parser->m_attInfo[i]; -#endif /* add the name and value to the attribute list */ ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, currAtt->name + XmlNameLength(enc, currAtt->name)); if (! attId) return XML_ERROR_NO_MEMORY; -#ifdef XML_ATTR_INFO - // NOTE: XML_Index is known to wrap around for >2 GiB content - // on 32bit machines and 64bit Windows, unless (non-default and - // uncommon) XML_LARGE_SIZE is defined. - // That's a bug and it only lives on because we cannot break - // ABI compatibility of public API. - currAttInfo->nameStart - = (XML_Index)(parser->m_parseEndByteIndex - - (parser->m_parseEndPtr - currAtt->name)); - currAttInfo->nameEnd - = currAttInfo->nameStart + XmlNameLength(enc, currAtt->name); - currAttInfo->valueStart - = (XML_Index)(parser->m_parseEndByteIndex - - (parser->m_parseEndPtr - currAtt->valuePtr)); - currAttInfo->valueEnd - = (XML_Index)(parser->m_parseEndByteIndex - - (parser->m_parseEndPtr - currAtt->valueEnd)); -#endif /* Detect duplicate attributes by their QNames. This does not work when namespace processing is turned on and different prefixes for the same namespace are used. For this case we have a check further down. @@ -3951,11 +4230,14 @@ /* figure out whether declared as other than CDATA */ if (attId->maybeTokenized) { - for (size_t j = 0; j < nDefaultAtts; j++) { - if (attId == elementType->defaultAtts[j].id) { - isCdata = elementType->defaultAtts[j].isCdata; - break; - } + NAME_AND_DEFAULT_ATTRIBUTE *const nameAndDefaultAttribute + = (NAME_AND_DEFAULT_ATTRIBUTE *)lookup( + parser, &(elementType->defaultAttForName), attId->name, 0); + if (nameAndDefaultAttribute != NULL) { + assert(nameAndDefaultAttribute->attIndex < elementType->nDefaultAtts); + const DEFAULT_ATTRIBUTE *const att + = elementType->defaultAtts + nameAndDefaultAttribute->attIndex; + isCdata = att->isCdata; } } @@ -4046,8 +4328,8 @@ unsigned int nsAttsSize = 1u << parser->m_nsAttsPower; unsigned char oldNsAttsPower = parser->m_nsAttsPower; /* size of hash table must be at least 2 * (# of prefixed attributes) */ - if ((nPrefixes << 1) - >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */ + if (parser->m_nsAttsPower == 0 + || (nPrefixes >> (parser->m_nsAttsPower - 1))) { /* hash table size must also be a power of 2 and >= 8 */ while (nPrefixes >> parser->m_nsAttsPower++) ; @@ -4505,6 +4787,7 @@ /* Detect and prevent integer overflow */ if (len > SIZE_MAX - EXPAND_SPARE || len + EXPAND_SPARE > SIZE_MAX / sizeof(XML_Char)) { + FREE(parser, b); return XML_ERROR_NO_MEMORY; } @@ -4542,7 +4825,7 @@ /* The idea here is to avoid using stack for each CDATA section when the whole file is parsed with one call. */ -static enum XML_Error PTRCALL +static enum XML_Error cdataSectionProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { enum XML_Error result = doCdataSection( @@ -4708,7 +4991,7 @@ /* The idea here is to avoid using stack for each IGNORE section when the whole file is parsed with one call. */ -static enum XML_Error PTRCALL +static enum XML_Error ignoreSectionProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { enum XML_Error result @@ -4946,30 +5229,39 @@ const int status = parser->m_unknownEncodingHandler( parser->m_unknownEncodingHandlerData, encodingName, &info); afterHandler(parser); + + parser->m_unknownEncodingRelease = info.release; + parser->m_unknownEncodingData = info.data; + if (status) { ENCODING *enc; parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding()); if (! parser->m_unknownEncodingMem) { - if (info.release) - info.release(info.data); + if (parser->m_unknownEncodingRelease) + callUnknownEncodingRelease(parser); + else + parser->m_unknownEncodingData = NULL; return XML_ERROR_NO_MEMORY; } + parser->m_unknownEncodingConvert = info.convert; enc = (parser->m_ns ? XmlInitUnknownEncodingNS : XmlInitUnknownEncoding)( - parser->m_unknownEncodingMem, info.map, info.convert, info.data); + parser->m_unknownEncodingMem, info.map, + info.convert ? callUnknownEncodingConvert : NULL, parser); if (enc) { - parser->m_unknownEncodingData = info.data; - parser->m_unknownEncodingRelease = info.release; parser->m_encoding = enc; return XML_ERROR_NONE; } + parser->m_unknownEncodingConvert = NULL; } - if (info.release != NULL) - info.release(info.data); + if (parser->m_unknownEncodingRelease != NULL) + callUnknownEncodingRelease(parser); + else + parser->m_unknownEncodingData = NULL; } return XML_ERROR_UNKNOWN_ENCODING; } -static enum XML_Error PTRCALL +static enum XML_Error prologInitProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { enum XML_Error result = initializeEncoding(parser); @@ -4981,7 +5273,7 @@ #ifdef XML_DTD -static enum XML_Error PTRCALL +static enum XML_Error externalParEntInitProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { enum XML_Error result = initializeEncoding(parser); @@ -4990,7 +5282,7 @@ /* we know now that XML_Parse(Buffer) has been called, so we consider the external parameter entity read */ - parser->m_dtd->paramEntityRead = XML_TRUE; + parser->m_dtd->paramEntityRead = true; if (parser->m_prologState.inEntityValue) { parser->m_processor = entityValueInitProcessor; @@ -5001,7 +5293,7 @@ } } -static enum XML_Error PTRCALL +static enum XML_Error entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { int tok; @@ -5085,7 +5377,7 @@ } } -static enum XML_Error PTRCALL +static enum XML_Error externalParEntProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { const char *next = s; @@ -5131,7 +5423,7 @@ XML_ACCOUNT_DIRECT); } -static enum XML_Error PTRCALL +static enum XML_Error entityValueProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { const char *start = s; @@ -5178,7 +5470,7 @@ #endif /* XML_DTD */ -static enum XML_Error PTRCALL +static enum XML_Error prologProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { const char *next = s; @@ -5424,7 +5716,7 @@ } if (parser->m_useForeignDTD) entity->base = parser->m_curBase; - dtd->paramEntityRead = XML_FALSE; + dtd->paramEntityRead = false; beforeHandler(parser); const int status = parser->m_externalEntityRefHandler( parser->m_externalEntityRefHandlerArg, 0, entity->base, @@ -5474,7 +5766,7 @@ if (! entity) return XML_ERROR_NO_MEMORY; entity->base = parser->m_curBase; - dtd->paramEntityRead = XML_FALSE; + dtd->paramEntityRead = false; beforeHandler(parser); const int status = parser->m_externalEntityRefHandler( parser->m_externalEntityRefHandlerArg, 0, entity->base, @@ -5817,7 +6109,7 @@ } else { poolFinish(&dtd->pool); parser->m_declEntity->publicId = NULL; - parser->m_declEntity->is_param = XML_FALSE; + parser->m_declEntity->is_param = false; /* if we have a parent parser or are reading an internal parameter entity, then the entity declaration is not considered "internal" */ @@ -5847,7 +6139,7 @@ } else { poolFinish(&dtd->pool); parser->m_declEntity->publicId = NULL; - parser->m_declEntity->is_param = XML_TRUE; + parser->m_declEntity->is_param = true; /* if we have a parent parser or are reading an internal parameter entity, then the entity declaration is not considered "internal" */ @@ -6082,8 +6374,7 @@ return XML_ERROR_RECURSIVE_ENTITY_REF; if (entity->textPtr) { enum XML_Error result; - XML_Bool betweenDecl - = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + bool betweenDecl = (role == XML_ROLE_PARAM_ENTITY_REF); result = processEntity(parser, entity, betweenDecl, ENTITY_INTERNAL); if (result != XML_ERROR_NONE) return result; @@ -6091,8 +6382,8 @@ break; } if (parser->m_externalEntityRefHandler) { - dtd->paramEntityRead = XML_FALSE; - entity->open = XML_TRUE; + dtd->paramEntityRead = false; + entity->open = true; entityTrackingOnOpen(parser, entity, __LINE__); beforeHandler(parser); const int status = parser->m_externalEntityRefHandler( @@ -6101,11 +6392,11 @@ afterHandler(parser); if (! status) { entityTrackingOnClose(parser, entity, __LINE__); - entity->open = XML_FALSE; + entity->open = false; return XML_ERROR_EXTERNAL_ENTITY_HANDLING; } entityTrackingOnClose(parser, entity, __LINE__); - entity->open = XML_FALSE; + entity->open = false; handleDefault = XML_FALSE; if (! dtd->paramEntityRead) { dtd->keepProcessing = dtd->standalone; @@ -6135,7 +6426,7 @@ return XML_ERROR_NO_MEMORY; dtd->scaffLevel = 0; dtd->scaffCount = 0; - dtd->in_eldecl = XML_TRUE; + dtd->in_eldecl = true; handleDefault = XML_FALSE; } break; @@ -6164,7 +6455,7 @@ afterHandler(parser); handleDefault = XML_FALSE; } - dtd->in_eldecl = XML_FALSE; + dtd->in_eldecl = false; } break; @@ -6246,7 +6537,7 @@ parser->m_handlerArg, parser->m_declElementType->name, model); afterHandler(parser); } - dtd->in_eldecl = XML_FALSE; + dtd->in_eldecl = false; dtd->contentStringLen = 0; } } @@ -6315,7 +6606,7 @@ /* not reached */ } -static enum XML_Error PTRCALL +static enum XML_Error epilogProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { parser->m_processor = epilogProcessor; @@ -6394,7 +6685,7 @@ } static enum XML_Error -processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, +processEntity(XML_Parser parser, ENTITY *entity, bool betweenDecl, enum EntityType type) { OPEN_INTERNAL_ENTITY *openEntity, **openEntityList; OPEN_INTERNAL_ENTITY **const freeEntityList = &parser->m_freeEntities; @@ -6429,8 +6720,8 @@ if (! openEntity) return XML_ERROR_NO_MEMORY; } - entity->open = XML_TRUE; - entity->hasMore = XML_TRUE; + entity->open = true; + entity->hasMore = true; #if XML_GE == 1 entityTrackingOnOpen(parser, entity, __LINE__); #endif @@ -6452,7 +6743,7 @@ return XML_ERROR_NONE; } -static enum XML_Error PTRCALL +static enum XML_Error internalEntityProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { UNUSED_P(s); @@ -6502,7 +6793,7 @@ // Entity is complete. We cannot close it here since we need to first // process its possible inner entities (which are added to the // m_openInternalEntities during doProlog or doContent calls above) - entity->hasMore = XML_FALSE; + entity->hasMore = false; if (! entity->is_param && (openEntity->startTagLevel != parser->m_tagLevel)) { return XML_ERROR_ASYNC_ENTITY; @@ -6520,7 +6811,7 @@ // to false. This means we can directly remove the head of // m_openInternalEntities assert(parser->m_openInternalEntities == openEntity); - entity->open = XML_FALSE; + entity->open = false; parser->m_openInternalEntities = parser->m_openInternalEntities->next; /* put openEntity back in list of free instances */ @@ -6534,7 +6825,7 @@ return XML_ERROR_NONE; } -static enum XML_Error PTRCALL +static enum XML_Error errorProcessor(XML_Parser parser, const char *s, const char *end, const char **nextPtr) { UNUSED_P(s); @@ -6585,7 +6876,7 @@ // Entity is complete. We cannot close it here since we need to first // process its possible inner entities (which are added to the // m_openAttributeEntities during appendAttributeValue) - entity->hasMore = XML_FALSE; + entity->hasMore = false; continue; } // End of entity processing, "if" block skips the rest @@ -6598,7 +6889,7 @@ // with hasMore set to false. This means we can directly remove the head // of m_openAttributeEntities assert(parser->m_openAttributeEntities == openEntity); - entity->open = XML_FALSE; + entity->open = false; parser->m_openAttributeEntities = parser->m_openAttributeEntities->next; /* put openEntity back in list of free instances */ @@ -6893,8 +7184,8 @@ } if (entity->systemId) { if (parser->m_externalEntityRefHandler) { - dtd->paramEntityRead = XML_FALSE; - entity->open = XML_TRUE; + dtd->paramEntityRead = false; + entity->open = true; entityTrackingOnOpen(parser, entity, __LINE__); beforeHandler(parser); const int status = parser->m_externalEntityRefHandler( @@ -6903,12 +7194,12 @@ afterHandler(parser); if (! status) { entityTrackingOnClose(parser, entity, __LINE__); - entity->open = XML_FALSE; + entity->open = false; result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; goto endEntityValue; } entityTrackingOnClose(parser, entity, __LINE__); - entity->open = XML_FALSE; + entity->open = false; if (! dtd->paramEntityRead) dtd->keepProcessing = dtd->standalone; } else @@ -7045,7 +7336,7 @@ // Entity is complete. We cannot close it here since we need to first // process its possible inner entities (which are added to the // m_openValueEntities during storeEntityValue) - entity->hasMore = XML_FALSE; + entity->hasMore = false; continue; } // End of entity processing, "if" block skips the rest @@ -7058,7 +7349,7 @@ // with hasMore set to false. This means we can directly remove the head // of m_openValueEntities assert(parser->m_openValueEntities == openEntity); - entity->open = XML_FALSE; + entity->open = false; parser->m_openValueEntities = parser->m_openValueEntities->next; /* put openEntity back in list of free instances */ @@ -7107,7 +7398,7 @@ #endif /* XML_GE == 0 */ -static void FASTCALL +static void normalizeLines(XML_Char *s) { XML_Char *p; for (;; s++) { @@ -7239,7 +7530,7 @@ /* The handling of default attributes gets messed up if we have a default which duplicates a non-default. */ NAMED *const nameFound - = lookup(parser, &(type->defaultAttsNames), attId->name, 0); + = lookup(parser, &(type->defaultAttForName), attId->name, 0); if (nameFound) return 1; if (isId && ! type->idAtt && ! attId->xmlns) @@ -7273,13 +7564,26 @@ att->value = value; att->isCdata = isCdata; if (! isCdata) - attId->maybeTokenized = XML_TRUE; + attId->maybeTokenized = true; - NAMED *const nameAddedOrFound - = lookup(parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED)); - if (! nameAddedOrFound) + NAME_AND_DEFAULT_ATTRIBUTE *const nameAndDefaultAttribute + = (NAME_AND_DEFAULT_ATTRIBUTE *)lookup( + parser, &(type->defaultAttForName), attId->name, + sizeof(NAME_AND_DEFAULT_ATTRIBUTE)); + if (! nameAndDefaultAttribute) return 0; + assert(nameAndDefaultAttribute->name == attId->name); + + // NOTE: The XML 1.0r4 spec says: + // "When more than one definition is provided for the same attribute of a + // given element type, the first declaration is binding and later + // declarations are ignored." + if (! nameAndDefaultAttribute->initialized) { + nameAndDefaultAttribute->attIndex = type->nDefaultAtts; + nameAndDefaultAttribute->initialized = true; + } + type->nDefaultAtts += 1; return 1; } @@ -7345,7 +7649,7 @@ else id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); - id->xmlns = XML_TRUE; + id->xmlns = true; } else { int i; for (i = 0; name[i]; i++) { @@ -7480,7 +7784,7 @@ e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&parser->m_tempPool), 0); if (e) - e->open = XML_TRUE; + e->open = true; if (*s != XML_T('\0')) s++; context = s; @@ -7535,7 +7839,7 @@ return XML_TRUE; } -static void FASTCALL +static void normalizePublicId(XML_Char *publicId) { XML_Char *p = publicId; XML_Char *s; @@ -7568,13 +7872,13 @@ hashTableInit(&(p->attributeIds), parser); hashTableInit(&(p->prefixes), parser); #ifdef XML_DTD - p->paramEntityRead = XML_FALSE; + p->paramEntityRead = false; hashTableInit(&(p->paramEntities), parser); #endif /* XML_DTD */ p->defaultPrefix.name = NULL; p->defaultPrefix.binding = NULL; - p->in_eldecl = XML_FALSE; + p->in_eldecl = false; p->scaffIndex = NULL; p->scaffIndexSize = 0; p->scaffold = NULL; @@ -7597,12 +7901,12 @@ ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! e) break; - hashTableDestroy(&(e->defaultAttsNames)); + hashTableDestroy(&(e->defaultAttForName)); FREE(parser, e->defaultAtts); } hashTableClear(&(p->generalEntities)); #ifdef XML_DTD - p->paramEntityRead = XML_FALSE; + p->paramEntityRead = false; hashTableClear(&(p->paramEntities)); #endif /* XML_DTD */ hashTableClear(&(p->elementTypes)); @@ -7613,7 +7917,7 @@ p->defaultPrefix.name = NULL; p->defaultPrefix.binding = NULL; - p->in_eldecl = XML_FALSE; + p->in_eldecl = false; FREE(parser, p->scaffIndex); p->scaffIndex = NULL; @@ -7639,7 +7943,7 @@ ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! e) break; - hashTableDestroy(&(e->defaultAttsNames)); + hashTableDestroy(&(e->defaultAttForName)); FREE(parser, e->defaultAtts); } hashTableDestroy(&(p->generalEntities)); @@ -7732,8 +8036,8 @@ if (! newE) return 0; - if (! newE->defaultAttsNames.parser) - hashTableInit(&(newE->defaultAttsNames), parser); + if (! newE->defaultAttForName.parser) + hashTableInit(&(newE->defaultAttForName), parser); if (oldE->nDefaultAtts) { /* Detect and prevent integer overflow. */ @@ -7766,11 +8070,22 @@ } else newE->defaultAtts[i].value = NULL; - NAMED *const nameAddedOrFound = lookup(parser, &(newE->defaultAttsNames), - attributeName, sizeof(NAMED)); - if (! nameAddedOrFound) { + NAME_AND_DEFAULT_ATTRIBUTE *const nameAndDefaultAttribute + = (NAME_AND_DEFAULT_ATTRIBUTE *)lookup( + parser, &(newE->defaultAttForName), attributeName, + sizeof(NAME_AND_DEFAULT_ATTRIBUTE)); + if (! nameAndDefaultAttribute) { return 0; } + + // NOTE: The XML 1.0r4 spec says: + // "When more than one definition is provided for the same attribute of a + // given element type, the first declaration is binding and later + // declarations are ignored." + if (! nameAndDefaultAttribute->initialized) { + nameAndDefaultAttribute->attIndex = i; + nameAndDefaultAttribute->initialized = true; + } } } @@ -7867,23 +8182,27 @@ #define INIT_POWER 6 -static XML_Bool FASTCALL -keyeq(KEY s1, KEY s2) { +// Compares two strings `s1` and `s2` whereas: +// - `s2` is zero-terminated but +// - `s1` is made up of exactly (not just up to) `s1len` non-zero characters. +XML_NONTESTING_STATIC XML_Bool +keyeq(KEY s1, size_t s1len, KEY s2) { #ifdef XML_UNICODE # ifdef XML_UNICODE_WCHAR_T - return (wcscmp(s1, s2) == 0) ? XML_TRUE : XML_FALSE; + return (wcsncmp(s1, s2, s1len) == 0 && s2[s1len] == L'\0') ? XML_TRUE + : XML_FALSE; # else - for (; *s1 == *s2; s1++, s2++) - if (*s1 == 0) - return XML_TRUE; - return XML_FALSE; + for (; s1len > 0 && *s1 == *s2; s1len--, s1++, s2++) + ; /* no loop body! */ + return ((s1len == 0) && (*s2 == 0)) ? XML_TRUE : XML_FALSE; # endif #else - return (strcmp(s1, s2) == 0) ? XML_TRUE : XML_FALSE; + return (strncmp(s1, s2, s1len) == 0 && s2[s1len] == '\0') ? XML_TRUE + : XML_FALSE; #endif } -static size_t +XML_NONTESTING_STATIC size_t keylen(KEY s) { return xcslen(s); } @@ -7896,19 +8215,39 @@ *key = rootParser->m_hash_secret_salt_128; } -static unsigned long FASTCALL -hash(XML_Parser parser, KEY s) { +static unsigned long +hash(XML_Parser parser, KEY s, size_t keyLen) { struct siphash state; struct sipkey key; (void)sip24_valid; copy_salt_to_sipkey(parser, &key); sip24_init(&state, &key); - sip24_update(&state, s, keylen(s) * sizeof(XML_Char)); + sip24_update(&state, s, keyLen * sizeof(XML_Char)); return (unsigned long)sip24_final(&state); } -static NAMED * -lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { +// Function `lookupWithLength` can be used to either… +// +// a) check whether an element with key `name` exists in the given hash table +// (read-only mode where `createSize == 0`) or +// +// b) check whether an element with key `name` exists in the given hash table +// *and* insert it if missing (i.e. read-write mode where `createSize != 0`. +// +// When inserting, a block of `createSize` number of bytes will be allocated +// and set to zero, and the resulting block of memory will be considered +// to start with a `NAMED` structure, and `->name = name;` is performed. +// The fact that all other bytes in the structure are initially zero can +// be used to tell cases "existed and found" and "newly inserted" apart +// with the structure returned. +// +// NOTE: Read-only lookup does not need zero-terminated keys but +// read-write mode does, because keys can be re-hashed later and the +// hash table does not store key length information. +// +XML_NONTESTING_STATIC NAMED * +lookupWithLength(XML_Parser parser, HASH_TABLE *table, KEY name, size_t nameLen, + size_t createSize) { size_t i; if (table->size == 0) { size_t tsize; @@ -7924,14 +8263,14 @@ return NULL; } memset(table->v, 0, tsize); - i = hash(parser, name) & ((unsigned long)table->size - 1); + i = hash(parser, name, nameLen) & ((unsigned long)table->size - 1); } else { - unsigned long h = hash(parser, name); + unsigned long h = hash(parser, name, nameLen); unsigned long mask = (unsigned long)table->size - 1; unsigned char step = 0; i = h & mask; while (table->v[i]) { - if (keyeq(name, table->v[i]->name)) + if (keyeq(name, nameLen, table->v[i]->name)) return table->v[i]; if (! step) step = PROBE_STEP(h, mask, table->power); @@ -7964,7 +8303,8 @@ memset(newV, 0, tsize); for (i = 0; i < table->size; i++) if (table->v[i]) { - unsigned long newHash = hash(parser, table->v[i]->name); + KEY const key = table->v[i]->name; + unsigned long newHash = hash(parser, key, keylen(key)); size_t j = newHash & newMask; step = 0; while (newV[j]) { @@ -7987,16 +8327,37 @@ } } } + assert(createSize >= sizeof(NAMED)); table->v[i] = MALLOC(table->parser, createSize); if (! table->v[i]) return NULL; memset(table->v[i], 0, createSize); - table->v[i]->name = name; + table->v[i]->name = name; // NOTE: This requires and assumes zero termination! (table->used)++; return table->v[i]; } -static void FASTCALL +// Function `lookup` can be used to either… +// +// a) check whether an element with key `name` exists in the given hash table +// (read-only mode where `createSize == 0`) or +// +// b) check whether an element with key `name` exists in the given hash table +// *and* insert it if missing (i.e. read-write mode where `createSize != 0`. +// +// When inserting, a block of `createSize` number of bytes will be allocated +// and set to zero, and the resulting block of memory will be considered +// to start with a `NAMED` structure, and `->name = name;` is performed. +// The fact that all other bytes in the structure are initially zero can +// be used to tell cases "existed and found" and "newly inserted" apart +// with the structure returned. +// +XML_NONTESTING_STATIC NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { + return lookupWithLength(parser, table, name, keylen(name), createSize); +} + +XML_NONTESTING_STATIC void hashTableClear(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) { @@ -8006,7 +8367,7 @@ table->used = 0; } -static void FASTCALL +XML_NONTESTING_STATIC void hashTableDestroy(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) @@ -8014,7 +8375,7 @@ FREE(table->parser, table->v); } -static void FASTCALL +XML_NONTESTING_STATIC void hashTableInit(HASH_TABLE *p, XML_Parser parser) { p->power = 0; p->size = 0; @@ -8023,13 +8384,13 @@ p->parser = parser; } -static void FASTCALL +XML_NONTESTING_STATIC void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) { iter->p = table->v; iter->end = iter->p ? iter->p + table->size : NULL; } -static NAMED *FASTCALL +XML_NONTESTING_STATIC NAMED * hashTableIterNext(HASH_TABLE_ITER *iter) { while (iter->p != iter->end) { NAMED *tem = *(iter->p)++; @@ -8039,7 +8400,7 @@ return NULL; } -static void FASTCALL +static void poolInit(STRING_POOL *pool, XML_Parser parser) { pool->blocks = NULL; pool->freeBlocks = NULL; @@ -8049,7 +8410,7 @@ pool->parser = parser; } -static void FASTCALL +static void poolClear(STRING_POOL *pool) { if (! pool->freeBlocks) pool->freeBlocks = pool->blocks; @@ -8068,7 +8429,7 @@ pool->end = NULL; } -static void FASTCALL +static void poolDestroy(STRING_POOL *pool) { BLOCK *p = pool->blocks; while (p) { @@ -8101,7 +8462,7 @@ return pool->start; } -static const XML_Char *FASTCALL +static const XML_Char * poolCopyString(STRING_POOL *pool, const XML_Char *s) { if (! poolAppendChars(pool, s, xcslen(s) + /*null terminator*/ 1)) return NULL; @@ -8112,7 +8473,7 @@ // A version of `poolCopyString` that does not call `poolFinish` // and reverts any partial advancement upon failure. -static const XML_Char *FASTCALL +static const XML_Char * poolCopyStringNoFinish(STRING_POOL *pool, const XML_Char *s) { const XML_Char *const original = s; do { @@ -8150,7 +8511,7 @@ return s; } -static const XML_Char *FASTCALL +static const XML_Char * poolAppendString(STRING_POOL *pool, const XML_Char *s) { if (! poolAppendChars(pool, s, xcslen(s))) return NULL; @@ -8195,7 +8556,7 @@ } } -static XML_Bool FASTCALL +static XML_Bool poolGrow(STRING_POOL *pool) { if (pool->freeBlocks) { if (pool->start == NULL) { @@ -8300,7 +8661,7 @@ return XML_TRUE; } -static bool FASTCALL +static bool poolGrowUntil(STRING_POOL *pool, size_t needed) { for (;;) { const size_t available = pool->end - pool->ptr; @@ -8313,7 +8674,7 @@ } } -static int FASTCALL +static int nextScaffoldPart(XML_Parser parser) { DTD *const dtd = parser->m_dtd; /* save one level of indirection */ CONTENT_SCAFFOLD *me; @@ -8535,8 +8896,8 @@ sizeof(ELEMENT_TYPE)); if (! ret) return NULL; - if (! ret->defaultAttsNames.parser) - hashTableInit(&(ret->defaultAttsNames), getRootParserOf(parser, NULL)); + if (! ret->defaultAttForName.parser) + hashTableInit(&(ret->defaultAttForName), getRootParserOf(parser, NULL)); if (ret->name != name) poolDiscard(&dtd->pool); else { diff -Nru expat-2.8.3/expat/lib/xmlrole.c expat-2.9.0/expat/lib/xmlrole.c --- expat-2.8.3/expat/lib/xmlrole.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmlrole.c 2026-10-05 15:43:59.000000000 +0000 @@ -12,7 +12,7 @@ Copyright (c) 2002-2006 Karl Waclawek Copyright (c) 2002-2003 Fred L. Drake, Jr. Copyright (c) 2005-2009 Steven Solie - Copyright (c) 2016-2023 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 David Loffredo Copyright (c) 2021 Donghee Na @@ -121,9 +121,8 @@ # define setTopLevel(state) ((state)->handler = internalSubset) #endif /* not XML_DTD */ -typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, int tok, - const char *ptr, const char *end, - const ENCODING *enc); +typedef int PROLOG_HANDLER(PROLOG_STATE *state, int tok, const char *ptr, + const char *end, const ENCODING *enc); static PROLOG_HANDLER prolog0, prolog1, prolog2, doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, internalSubset, entity0, entity1, entity2, @@ -137,9 +136,9 @@ #endif /* XML_DTD */ declClose, error; -static int FASTCALL common(PROLOG_STATE *state, int tok); +static int common(PROLOG_STATE *state, int tok); -static int PTRCALL +static int prolog0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -170,7 +169,7 @@ return common(state, tok); } -static int PTRCALL +static int prolog1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -202,7 +201,7 @@ return common(state, tok); } -static int PTRCALL +static int prolog2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -222,7 +221,7 @@ return common(state, tok); } -static int PTRCALL +static int doctype0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -239,7 +238,7 @@ return common(state, tok); } -static int PTRCALL +static int doctype1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -265,7 +264,7 @@ return common(state, tok); } -static int PTRCALL +static int doctype2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -281,7 +280,7 @@ return common(state, tok); } -static int PTRCALL +static int doctype3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -297,7 +296,7 @@ return common(state, tok); } -static int PTRCALL +static int doctype4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -316,7 +315,7 @@ return common(state, tok); } -static int PTRCALL +static int doctype5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -332,7 +331,7 @@ return common(state, tok); } -static int PTRCALL +static int internalSubset(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -377,7 +376,7 @@ #ifdef XML_DTD -static int PTRCALL +static int externalSubset0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { state->handler = externalSubset1; @@ -386,7 +385,7 @@ return externalSubset1(state, tok, ptr, end, enc); } -static int PTRCALL +static int externalSubset1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -414,7 +413,7 @@ #endif /* XML_DTD */ -static int PTRCALL +static int entity0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -433,7 +432,7 @@ return common(state, tok); } -static int PTRCALL +static int entity1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -449,7 +448,7 @@ return common(state, tok); } -static int PTRCALL +static int entity2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -473,7 +472,7 @@ return common(state, tok); } -static int PTRCALL +static int entity3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -489,7 +488,7 @@ return common(state, tok); } -static int PTRCALL +static int entity4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -505,7 +504,7 @@ return common(state, tok); } -static int PTRCALL +static int entity5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -524,7 +523,7 @@ return common(state, tok); } -static int PTRCALL +static int entity6(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -541,7 +540,7 @@ return common(state, tok); } -static int PTRCALL +static int entity7(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -565,7 +564,7 @@ return common(state, tok); } -static int PTRCALL +static int entity8(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -581,7 +580,7 @@ return common(state, tok); } -static int PTRCALL +static int entity9(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -597,7 +596,7 @@ return common(state, tok); } -static int PTRCALL +static int entity10(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -613,7 +612,7 @@ return common(state, tok); } -static int PTRCALL +static int notation0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -629,7 +628,7 @@ return common(state, tok); } -static int PTRCALL +static int notation1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -649,7 +648,7 @@ return common(state, tok); } -static int PTRCALL +static int notation2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -665,7 +664,7 @@ return common(state, tok); } -static int PTRCALL +static int notation3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -682,7 +681,7 @@ return common(state, tok); } -static int PTRCALL +static int notation4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -702,7 +701,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -719,7 +718,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -739,7 +738,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -769,7 +768,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -787,7 +786,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -806,7 +805,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -822,7 +821,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist6(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -838,7 +837,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist7(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -858,7 +857,7 @@ } /* default value */ -static int PTRCALL +static int attlist8(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -888,7 +887,7 @@ return common(state, tok); } -static int PTRCALL +static int attlist9(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -904,7 +903,7 @@ return common(state, tok); } -static int PTRCALL +static int element0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -921,7 +920,7 @@ return common(state, tok); } -static int PTRCALL +static int element1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -947,7 +946,7 @@ return common(state, tok); } -static int PTRCALL +static int element2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -981,7 +980,7 @@ return common(state, tok); } -static int PTRCALL +static int element3(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1005,7 +1004,7 @@ return common(state, tok); } -static int PTRCALL +static int element4(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1022,7 +1021,7 @@ return common(state, tok); } -static int PTRCALL +static int element5(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1042,7 +1041,7 @@ return common(state, tok); } -static int PTRCALL +static int element6(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1071,7 +1070,7 @@ return common(state, tok); } -static int PTRCALL +static int element7(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1120,7 +1119,7 @@ #ifdef XML_DTD -static int PTRCALL +static int condSect0(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { switch (tok) { @@ -1140,7 +1139,7 @@ return common(state, tok); } -static int PTRCALL +static int condSect1(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1157,7 +1156,7 @@ return common(state, tok); } -static int PTRCALL +static int condSect2(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1175,7 +1174,7 @@ #endif /* XML_DTD */ -static int PTRCALL +static int declClose(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(ptr); @@ -1211,7 +1210,7 @@ * * LCOV_EXCL_START */ -static int PTRCALL +static int error(PROLOG_STATE *state, int tok, const char *ptr, const char *end, const ENCODING *enc) { UNUSED_P(state); @@ -1223,7 +1222,7 @@ } /* LCOV_EXCL_STOP */ -static int FASTCALL +static int common(PROLOG_STATE *state, int tok) { #ifdef XML_DTD if (! state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) diff -Nru expat-2.8.3/expat/lib/xmlrole.h expat-2.9.0/expat/lib/xmlrole.h --- expat-2.8.3/expat/lib/xmlrole.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmlrole.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Karl Waclawek Copyright (c) 2002 Fred L. Drake, Jr. - Copyright (c) 2017-2025 Sebastian Pipping + Copyright (c) 2017-2026 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -40,10 +40,6 @@ # include "xmltok.h" -# ifdef __cplusplus -extern "C" { -# endif - enum { XML_ROLE_ERROR = -1, XML_ROLE_NONE = 0, @@ -112,8 +108,8 @@ }; typedef struct prolog_state { - int(PTRCALL *handler)(struct prolog_state *state, int tok, const char *ptr, - const char *end, const ENCODING *enc); + int (*handler)(struct prolog_state *state, int tok, const char *ptr, + const char *end, const ENCODING *enc); unsigned level; int role_none; # ifdef XML_DTD @@ -131,8 +127,4 @@ # define XmlTokenRole(state, tok, ptr, end, enc) \ (((state)->handler)(state, tok, ptr, end, enc)) -# ifdef __cplusplus -} -# endif - #endif /* not XmlRole_INCLUDED */ diff -Nru expat-2.8.3/expat/lib/xmltok.c expat-2.9.0/expat/lib/xmltok.c --- expat-2.8.3/expat/lib/xmltok.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmltok.c 2026-10-05 15:43:59.000000000 +0000 @@ -27,6 +27,7 @@ Copyright (c) 2025 Alfonso Gregory Copyright (c) 2026 Nick Begg Copyright (c) 2026 Kartik Kenchi + Copyright (c) 2026 Afonso Januário Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -136,20 +137,20 @@ : ((p)[1] & 0x80) == 0 \ || ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) -static int PTRFASTCALL +static int isNever(const ENCODING *enc, const char *p) { UNUSED_P(enc); UNUSED_P(p); return 0; } -static int PTRFASTCALL +static int utf8_isName2(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); } -static int PTRFASTCALL +static int utf8_isName3(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); @@ -157,13 +158,13 @@ #define utf8_isName4 isNever -static int PTRFASTCALL +static int utf8_isNmstrt2(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); } -static int PTRFASTCALL +static int utf8_isNmstrt3(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); @@ -171,19 +172,19 @@ #define utf8_isNmstrt4 isNever -static int PTRFASTCALL +static int utf8_isInvalid2(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_INVALID2((const unsigned char *)p); } -static int PTRFASTCALL +static int utf8_isInvalid3(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_INVALID3((const unsigned char *)p); } -static int PTRFASTCALL +static int utf8_isInvalid4(const ENCODING *enc, const char *p) { UNUSED_P(enc); return UTF8_INVALID4((const unsigned char *)p); @@ -192,36 +193,20 @@ struct normal_encoding { ENCODING enc; unsigned char type[256]; -#ifdef XML_MIN_SIZE - int(PTRFASTCALL *byteType)(const ENCODING *, const char *); - int(PTRFASTCALL *isNameMin)(const ENCODING *, const char *); - int(PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); - int(PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); - int(PTRCALL *charMatches)(const ENCODING *, const char *, int); -#endif /* XML_MIN_SIZE */ - int(PTRFASTCALL *isName2)(const ENCODING *, const char *); - int(PTRFASTCALL *isName3)(const ENCODING *, const char *); - int(PTRFASTCALL *isName4)(const ENCODING *, const char *); - int(PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); - int(PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); - int(PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); - int(PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); - int(PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); - int(PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); + int (*isName2)(const ENCODING *, const char *); + int (*isName3)(const ENCODING *, const char *); + int (*isName4)(const ENCODING *, const char *); + int (*isNmstrt2)(const ENCODING *, const char *); + int (*isNmstrt3)(const ENCODING *, const char *); + int (*isNmstrt4)(const ENCODING *, const char *); + int (*isInvalid2)(const ENCODING *, const char *); + int (*isInvalid3)(const ENCODING *, const char *); + int (*isInvalid4)(const ENCODING *, const char *); }; #define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *)(enc)) -#ifdef XML_MIN_SIZE - -# define STANDARD_VTABLE(E) \ - E##byteType, E##isNameMin, E##isNmstrtMin, E##byteToAscii, E##charMatches, - -#else - -# define STANDARD_VTABLE(E) /* as nothing */ - -#endif +#define STANDARD_VTABLE(E) /* as nothing */ #define NORMAL_VTABLE(E) \ E##isName2, E##isName3, E##isName4, E##isNmstrt2, E##isNmstrt3, \ @@ -232,80 +217,33 @@ /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL, \ /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, /* isInvalid4 */ NULL -static int FASTCALL checkCharRefNumber(int result); +#define UTF16_NULL_VTABLE(E) NULL_VTABLE + +static int checkCharRefNumber(int result); #include "xmltok_impl.h" #include "ascii.h" -#ifdef XML_MIN_SIZE -# define sb_isNameMin isNever -# define sb_isNmstrtMin isNever -#endif - -#ifdef XML_MIN_SIZE -# define MINBPC(enc) ((enc)->minBytesPerChar) -#else /* minimum bytes per character */ -# define MINBPC(enc) 1 -#endif +#define MINBPC(enc) 1 #define SB_BYTE_TYPE(enc, p) \ (((const struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) -#ifdef XML_MIN_SIZE -static int PTRFASTCALL -sb_byteType(const ENCODING *enc, const char *p) { - return SB_BYTE_TYPE(enc, p); -} -# define BYTE_TYPE(enc, p) (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) -#else -# define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) -#endif +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) -#ifdef XML_MIN_SIZE -# define BYTE_TO_ASCII(enc, p) (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) -static int PTRFASTCALL -sb_byteToAscii(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return *p; -} -#else -# define BYTE_TO_ASCII(enc, p) (*(p)) -#endif +#define BYTE_TO_ASCII(enc, p) (*(p)) #define IS_NAME_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isName##n(enc, p)) #define IS_NMSTRT_CHAR(enc, p, n) (AS_NORMAL_ENCODING(enc)->isNmstrt##n(enc, p)) -#ifdef XML_MIN_SIZE -# define IS_INVALID_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isInvalid##n \ - && AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p)) -#else -# define IS_INVALID_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p)) -#endif +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid##n(enc, p)) -#ifdef XML_MIN_SIZE -# define IS_NAME_CHAR_MINBPC(enc, p) \ - (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) -# define IS_NMSTRT_CHAR_MINBPC(enc, p) \ - (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) -#else -# define IS_NAME_CHAR_MINBPC(enc, p) (0) -# define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) -#endif +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) -#ifdef XML_MIN_SIZE -# define CHAR_MATCHES(enc, p, c) \ - (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) -static int PTRCALL -sb_charMatches(const ENCODING *enc, const char *p, int c) { - UNUSED_P(enc); - return *p == c; -} -#else /* c is an ASCII character */ -# define CHAR_MATCHES(enc, p, c) (*(p) == (c)) -#endif +#define CHAR_MATCHES(enc, p, c) (*(p) == (c)) #define PREFIX(ident) normal_##ident #define XML_TOK_IMPL_C @@ -368,7 +306,7 @@ *fromLimRef = fromLim; } -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result utf8_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, char **toP, const char *toLim) { bool input_incomplete = false; @@ -407,7 +345,7 @@ return XML_CONVERT_COMPLETED; } -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result utf8_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, unsigned short **toP, const unsigned short *toLim) { enum XML_Convert_Result res = XML_CONVERT_COMPLETED; @@ -505,7 +443,7 @@ }, STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)}; -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result latin1_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, char **toP, const char *toLim) { UNUSED_P(enc); @@ -528,7 +466,7 @@ } } -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result latin1_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, unsigned short **toP, const unsigned short *toLim) { UNUSED_P(enc); @@ -563,7 +501,7 @@ }, STANDARD_VTABLE(sb_) NULL_VTABLE}; -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result ascii_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, char **toP, const char *toLim) { UNUSED_P(enc); @@ -598,7 +536,7 @@ }, STANDARD_VTABLE(sb_) NULL_VTABLE}; -static int PTRFASTCALL +static int unicode_byte_type(char hi, char lo) { switch ((unsigned char)hi) { /* 0xD800-0xDBFF first 16-bit code unit or high surrogate (W1) */ @@ -625,7 +563,7 @@ } #define DEFINE_UTF16_TO_UTF8(E) \ - static enum XML_Convert_Result PTRCALL E##toUtf8( \ + static enum XML_Convert_Result E##toUtf8( \ const ENCODING *enc, const char **fromP, const char *fromLim, \ char **toP, const char *toLim) { \ const char *from = *fromP; \ @@ -702,7 +640,7 @@ } #define DEFINE_UTF16_TO_UTF16(E) \ - static enum XML_Convert_Result PTRCALL E##toUtf16( \ + static enum XML_Convert_Result E##toUtf16( \ const ENCODING *enc, const char **fromP, const char *fromLim, \ unsigned short **toP, const unsigned short *toLim) { \ enum XML_Convert_Result res = XML_CONVERT_COMPLETED; \ @@ -749,70 +687,38 @@ UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) #define LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) \ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) +/* A 4-byte UTF-16 character is a surrogate pair; byteType only reports BT_LEAD4 + for a high surrogate, so the pair is invalid unless the second unit is a low + surrogate (U+DC00..U+DFFF, i.e. high byte 0xDC..0xDF). */ +#define LITTLE2_IS_INVALID_CHAR(p, n) \ + ((n) == 4 && ((unsigned char)(p)[3] & 0xFC) != 0xDC) -#ifdef XML_MIN_SIZE - -static int PTRFASTCALL -little2_byteType(const ENCODING *enc, const char *p) { - return LITTLE2_BYTE_TYPE(enc, p); -} - -static int PTRFASTCALL -little2_byteToAscii(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return LITTLE2_BYTE_TO_ASCII(p); -} - -static int PTRCALL -little2_charMatches(const ENCODING *enc, const char *p, int c) { - UNUSED_P(enc); - return LITTLE2_CHAR_MATCHES(p, c); -} - -static int PTRFASTCALL -little2_isNameMin(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return LITTLE2_IS_NAME_CHAR_MINBPC(p); -} - -static int PTRFASTCALL -little2_isNmstrtMin(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return LITTLE2_IS_NMSTRT_CHAR_MINBPC(p); -} - -# undef VTABLE -# define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 - -#else /* not XML_MIN_SIZE */ - -# undef PREFIX -# define PREFIX(ident) little2_##ident -# define MINBPC(enc) 2 +#undef PREFIX +#define PREFIX(ident) little2_##ident +#define MINBPC(enc) 2 /* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ -# define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) -# define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(p) -# define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(p, c) -# define IS_NAME_CHAR(enc, p, n) 0 -# define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(p) -# define IS_NMSTRT_CHAR(enc, p, n) (0) -# define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) - -# define XML_TOK_IMPL_C -# include "xmltok_impl.c" -# undef XML_TOK_IMPL_C - -# undef MINBPC -# undef BYTE_TYPE -# undef BYTE_TO_ASCII -# undef CHAR_MATCHES -# undef IS_NAME_CHAR -# undef IS_NAME_CHAR_MINBPC -# undef IS_NMSTRT_CHAR -# undef IS_NMSTRT_CHAR_MINBPC -# undef IS_INVALID_CHAR +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) +#define IS_INVALID_CHAR(enc, p, n) LITTLE2_IS_INVALID_CHAR(p, n) -#endif /* not XML_MIN_SIZE */ +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR #ifdef XML_NS @@ -828,7 +734,7 @@ # include "asciitab.h" # include "latin1tab.h" }, - STANDARD_VTABLE(little2_) NULL_VTABLE}; + STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; #endif @@ -846,7 +752,7 @@ #undef BT_COLON #include "latin1tab.h" }, - STANDARD_VTABLE(little2_) NULL_VTABLE}; + STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; #if BYTEORDER != 4321 @@ -858,7 +764,7 @@ # include "iasciitab.h" # include "latin1tab.h" }, - STANDARD_VTABLE(little2_) NULL_VTABLE}; + STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; # endif @@ -870,7 +776,7 @@ # undef BT_COLON # include "latin1tab.h" }, - STANDARD_VTABLE(little2_) NULL_VTABLE}; + STANDARD_VTABLE(little2_) UTF16_NULL_VTABLE(little2_)}; #endif @@ -882,70 +788,38 @@ UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) #define BIG2_IS_NMSTRT_CHAR_MINBPC(p) \ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) +/* A 4-byte UTF-16 character is a surrogate pair; byteType only reports BT_LEAD4 + for a high surrogate, so the pair is invalid unless the second unit is a low + surrogate (U+DC00..U+DFFF, i.e. high byte 0xDC..0xDF). */ +#define BIG2_IS_INVALID_CHAR(p, n) \ + ((n) == 4 && ((unsigned char)(p)[2] & 0xFC) != 0xDC) -#ifdef XML_MIN_SIZE - -static int PTRFASTCALL -big2_byteType(const ENCODING *enc, const char *p) { - return BIG2_BYTE_TYPE(enc, p); -} - -static int PTRFASTCALL -big2_byteToAscii(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return BIG2_BYTE_TO_ASCII(p); -} - -static int PTRCALL -big2_charMatches(const ENCODING *enc, const char *p, int c) { - UNUSED_P(enc); - return BIG2_CHAR_MATCHES(p, c); -} - -static int PTRFASTCALL -big2_isNameMin(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return BIG2_IS_NAME_CHAR_MINBPC(p); -} - -static int PTRFASTCALL -big2_isNmstrtMin(const ENCODING *enc, const char *p) { - UNUSED_P(enc); - return BIG2_IS_NMSTRT_CHAR_MINBPC(p); -} - -# undef VTABLE -# define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 - -#else /* not XML_MIN_SIZE */ - -# undef PREFIX -# define PREFIX(ident) big2_##ident -# define MINBPC(enc) 2 +#undef PREFIX +#define PREFIX(ident) big2_##ident +#define MINBPC(enc) 2 /* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ -# define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) -# define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(p) -# define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(p, c) -# define IS_NAME_CHAR(enc, p, n) 0 -# define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(p) -# define IS_NMSTRT_CHAR(enc, p, n) (0) -# define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(p) - -# define XML_TOK_IMPL_C -# include "xmltok_impl.c" -# undef XML_TOK_IMPL_C - -# undef MINBPC -# undef BYTE_TYPE -# undef BYTE_TO_ASCII -# undef CHAR_MATCHES -# undef IS_NAME_CHAR -# undef IS_NAME_CHAR_MINBPC -# undef IS_NMSTRT_CHAR -# undef IS_NMSTRT_CHAR_MINBPC -# undef IS_INVALID_CHAR +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(p) +#define IS_INVALID_CHAR(enc, p, n) BIG2_IS_INVALID_CHAR(p, n) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C -#endif /* not XML_MIN_SIZE */ +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR #ifdef XML_NS @@ -961,7 +835,7 @@ # include "asciitab.h" # include "latin1tab.h" }, - STANDARD_VTABLE(big2_) NULL_VTABLE}; + STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; #endif @@ -979,7 +853,7 @@ #undef BT_COLON #include "latin1tab.h" }, - STANDARD_VTABLE(big2_) NULL_VTABLE}; + STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; #if BYTEORDER != 1234 @@ -991,7 +865,7 @@ # include "iasciitab.h" # include "latin1tab.h" }, - STANDARD_VTABLE(big2_) NULL_VTABLE}; + STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; # endif @@ -1003,13 +877,13 @@ # undef BT_COLON # include "latin1tab.h" }, - STANDARD_VTABLE(big2_) NULL_VTABLE}; + STANDARD_VTABLE(big2_) UTF16_NULL_VTABLE(big2_)}; #endif #undef PREFIX -static int FASTCALL +static int streqci(const char *s1, const char *s2) { for (;;) { char c1 = *s1++; @@ -1030,7 +904,7 @@ return 1; } -static void PTRCALL +static void initUpdatePosition(const ENCODING *enc, const char *ptr, const char *end, POSITION *pos) { UNUSED_P(enc); @@ -1048,7 +922,7 @@ return buf[0]; } -static int FASTCALL +static int isSpace(int c) { switch (c) { case 0x20: @@ -1154,6 +1028,34 @@ static const char KW_no[] = {ASCII_n, ASCII_o, '\0'}; +static const char KW_1_dot[] = {ASCII_1, ASCII_PERIOD, '\0'}; + +/* Checks a version pseudo-attribute value against the VersionNum production. + XML 1.0 Fourth Edition only allows the literal "1.0", but the Fifth + Edition relaxed this to "1." followed by one or more digits, since Expat + only implements 1.0 itself but plans to track the Fifth Edition's laxer + grammar here so that "1.1" and similar aren't rejected only to have that + rejection reverted later. Returns true for a value matching "1.[0-9]+". + val/valEnd bound the value itself; valEnd is the upper bound used when + decoding the individual characters between them. */ +static bool +checkXmlDeclVersionNum(const ENCODING *enc, const char *val, + const char *valEnd) { + if (valEnd - val < 2 * enc->minBytesPerChar + || ! XmlNameMatchesAscii(enc, val, val + 2 * enc->minBytesPerChar, + KW_1_dot)) + return false; + val += 2 * enc->minBytesPerChar; + if (val == valEnd) + return false; + for (; val != valEnd; val += enc->minBytesPerChar) { + int c = toAscii(enc, val, valEnd); + if (c < ASCII_0 || c > ASCII_9) + return false; + } + return true; +} + static int doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, const char *, const char *), @@ -1188,6 +1090,15 @@ *badPtr = val; return 0; } + /* Expat implements XML 1.0 only, so any version outside the "1.0"/"1.x" + family is rejected. Following the Fifth Edition's VersionNum + production (rather than the Fourth Edition's exact "1.0") avoids + rejecting "1.1" now only to have to revert that once Expat tracks + the newer edition. */ + if (! checkXmlDeclVersionNum(enc, val, ptr - enc->minBytesPerChar)) { + *badPtr = val; + return 0; + } if (! parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { *badPtr = ptr; return 0; @@ -1242,7 +1153,7 @@ return 1; } -static int FASTCALL +static int checkCharRefNumber(int result) { switch (result >> 8) { case 0xD8: @@ -1266,7 +1177,7 @@ return result; } -int FASTCALL +int XmlUtf8Encode(int c, char *buf) { enum { /* minN is minimum legal resulting value for N byte sequence */ @@ -1302,7 +1213,7 @@ return 0; /* LCOV_EXCL_LINE: this case too is eliminated before calling */ } -int FASTCALL +int XmlUtf16Encode(int charNum, unsigned short *buf) { if (charNum < 0) return 0; @@ -1334,7 +1245,7 @@ return sizeof(struct unknown_encoding); } -static int PTRFASTCALL +static int unknown_isName(const ENCODING *enc, const char *p) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); int c = uenc->convert(uenc->userData, p); @@ -1343,7 +1254,7 @@ return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); } -static int PTRFASTCALL +static int unknown_isNmstrt(const ENCODING *enc, const char *p) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); int c = uenc->convert(uenc->userData, p); @@ -1352,14 +1263,14 @@ return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); } -static int PTRFASTCALL +static int unknown_isInvalid(const ENCODING *enc, const char *p) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); int c = uenc->convert(uenc->userData, p); return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; } -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result unknown_toUtf8(const ENCODING *enc, const char **fromP, const char *fromLim, char **toP, const char *toLim) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); @@ -1389,7 +1300,7 @@ } } -static enum XML_Convert_Result PTRCALL +static enum XML_Convert_Result unknown_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, unsigned short **toP, const unsigned short *toLim) { const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); @@ -1513,7 +1424,7 @@ = {ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, '\0'}; -static int FASTCALL +static int getEncodingIndex(const char *name) { static const char *const encodingNames[] = { KW_ISO_8859_1, KW_US_ASCII, KW_UTF_8, KW_UTF_16, KW_UTF_16BE, KW_UTF_16LE, diff -Nru expat-2.8.3/expat/lib/xmltok.h expat-2.9.0/expat/lib/xmltok.h --- expat-2.8.3/expat/lib/xmltok.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmltok.h 2026-10-05 15:43:59.000000000 +0000 @@ -41,10 +41,6 @@ # include // uint64_t -# ifdef __cplusplus -extern "C" { -# endif - /* The following token may be returned by XmlContentTok */ # define XML_TOK_TRAILING_RSQB \ -5 /* ] or ]] at the end of the scan; might be \ @@ -163,41 +159,40 @@ struct encoding; typedef struct encoding ENCODING; -typedef int(PTRCALL *SCANNER)(const ENCODING *, const char *, const char *, - const char **); +typedef int (*SCANNER)(const ENCODING *, const char *, const char *, + const char **); enum XML_Convert_Result { XML_CONVERT_COMPLETED = 0, XML_CONVERT_INPUT_INCOMPLETE = 1, - XML_CONVERT_OUTPUT_EXHAUSTED - = 2 /* and therefore potentially input remaining as well */ + XML_CONVERT_OUTPUT_EXHAUSTED = 2 /* and therefore potentially input remaining + as well */ }; struct encoding { SCANNER scanners[XML_N_STATES]; SCANNER literalScanners[XML_N_LITERAL_TYPES]; - int(PTRCALL *nameMatchesAscii)(const ENCODING *, const char *, const char *, - const char *); - int(PTRFASTCALL *nameLength)(const ENCODING *, const char *); - const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); - int(PTRCALL *getAtts)(const ENCODING *enc, const char *ptr, int attsMax, - ATTRIBUTE *atts); - int(PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); - int(PTRCALL *predefinedEntityName)(const ENCODING *, const char *, - const char *); - void(PTRCALL *updatePosition)(const ENCODING *, const char *ptr, - const char *end, POSITION *); - int(PTRCALL *isPublicId)(const ENCODING *enc, const char *ptr, - const char *end, const char **badPtr); - enum XML_Convert_Result(PTRCALL *utf8Convert)(const ENCODING *enc, - const char **fromP, - const char *fromLim, char **toP, - const char *toLim); - enum XML_Convert_Result(PTRCALL *utf16Convert)(const ENCODING *enc, - const char **fromP, - const char *fromLim, - unsigned short **toP, - const unsigned short *toLim); + int (*nameMatchesAscii)(const ENCODING *, const char *, const char *, + const char *); + int (*nameLength)(const ENCODING *, const char *); + const char *(*skipS)(const ENCODING *, const char *); + int (*getAtts)(const ENCODING *enc, const char *ptr, int attsMax, + ATTRIBUTE *atts); + int (*charRefNumber)(const ENCODING *enc, const char *ptr); + int (*predefinedEntityName)(const ENCODING *, const char *, const char *); + void (*updatePosition)(const ENCODING *, const char *ptr, const char *end, + POSITION *); + int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr); + enum XML_Convert_Result (*utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, char **toP, + const char *toLim); + enum XML_Convert_Result (*utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); int minBytesPerChar; char isUtf8; char isUtf16; @@ -297,8 +292,8 @@ const char *name); const ENCODING *XmlGetUtf8InternalEncoding(void); const ENCODING *XmlGetUtf16InternalEncoding(void); -int FASTCALL XmlUtf8Encode(int charNumber, char *buf); -int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlUtf8Encode(int charNumber, char *buf); +int XmlUtf16Encode(int charNumber, unsigned short *buf); int XmlSizeOfUnknownEncoding(void); typedef int(XMLCALL *CONVERTER)(void *userData, const char *p); @@ -318,8 +313,5 @@ const ENCODING *XmlGetUtf16InternalEncodingNS(void); ENCODING *XmlInitUnknownEncodingNS(void *mem, const int *table, CONVERTER convert, void *userData); -# ifdef __cplusplus -} -# endif #endif /* not XmlTok_INCLUDED */ diff -Nru expat-2.8.3/expat/lib/xmltok_impl.c expat-2.9.0/expat/lib/xmltok_impl.c --- expat-2.8.3/expat/lib/xmltok_impl.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmltok_impl.c 2026-10-05 15:43:59.000000000 +0000 @@ -1,4 +1,4 @@ -/* This file is included (from xmltok.c, 1-3 times depending on XML_MIN_SIZE)! +/* This file is included (from xmltok.c, 3 times)! __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Fred L. Drake, Jr. Copyright (c) 2002-2016 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2018 Benjamin Peterson Copyright (c) 2018 Anton Maklakov @@ -18,6 +18,7 @@ Copyright (c) 2020 Boris Kolpackov Copyright (c) 2022 Martin Ettl Copyright (c) 2026 Nick Begg + Copyright (c) 2026 Kartik Kenchi Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -44,10 +45,6 @@ #ifdef XML_TOK_IMPL_C -# ifndef IS_INVALID_CHAR // i.e. for UTF-16 and XML_MIN_SIZE not defined -# define IS_INVALID_CHAR(enc, ptr, n) (0) -# endif - # define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ case BT_LEAD##n: \ if (end - ptr < n) \ @@ -144,7 +141,7 @@ /* ptr points to character following "= end) @@ -430,7 +427,7 @@ /* ptr points to character following "= end) @@ -922,7 +919,7 @@ /* ptr points to character following "%" */ -static int PTRCALL +static int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { REQUIRE_CHAR(enc, ptr, end); @@ -952,7 +949,7 @@ return XML_TOK_PARTIAL; } -static int PTRCALL +static int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { REQUIRE_CHAR(enc, ptr, end); @@ -982,7 +979,7 @@ return -XML_TOK_POUND_NAME; } -static int PTRCALL +static int PREFIX(scanLit)(int open, const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { while (HAS_CHAR(enc, ptr, end)) { @@ -1016,7 +1013,7 @@ return XML_TOK_PARTIAL; } -static int PTRCALL +static int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { int tok; @@ -1260,7 +1257,7 @@ return -tok; } -static int PTRCALL +static int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { const char *start; @@ -1329,7 +1326,7 @@ return XML_TOK_DATA_CHARS; } -static int PTRCALL +static int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { const char *start; @@ -1396,7 +1393,7 @@ # ifdef XML_DTD -static int PTRCALL +static int PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { int level = 0; @@ -1448,7 +1445,7 @@ # endif /* XML_DTD */ -static int PTRCALL +static int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, const char **badPtr) { ptr += MINBPC(enc); @@ -1508,7 +1505,7 @@ first attsMax attributes are stored in atts. */ -static int PTRCALL +static int PREFIX(getAtts)(const ENCODING *enc, const char *ptr, int attsMax, ATTRIBUTE *atts) { enum { other, inName, inValue } state = inName; @@ -1601,7 +1598,7 @@ /* not reached */ } -static int PTRFASTCALL +static int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) { int result = 0; /* skip &# */ @@ -1659,7 +1656,7 @@ return checkCharRefNumber(result); } -static int PTRCALL +static int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end) { UNUSED_P(enc); @@ -1713,7 +1710,7 @@ return 0; } -static int PTRCALL +static int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, const char *end1, const char *ptr2) { UNUSED_P(enc); @@ -1732,7 +1729,7 @@ return ptr1 == end1; } -static int PTRFASTCALL +static int PREFIX(nameLength)(const ENCODING *enc, const char *ptr) { const char *start = ptr; for (;;) { @@ -1762,7 +1759,7 @@ } } -static const char *PTRFASTCALL +static const char * PREFIX(skipS)(const ENCODING *enc, const char *ptr) { for (;;) { switch (BYTE_TYPE(enc, ptr)) { @@ -1777,7 +1774,7 @@ } } -static void PTRCALL +static void PREFIX(updatePosition)(const ENCODING *enc, const char *ptr, const char *end, POSITION *pos) { while (HAS_CHAR(enc, ptr, end)) { diff -Nru expat-2.8.3/expat/lib/xmltok_ns.c expat-2.9.0/expat/lib/xmltok_ns.c --- expat-2.8.3/expat/lib/xmltok_ns.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/lib/xmltok_ns.c 2026-10-05 15:43:59.000000000 +0000 @@ -11,7 +11,7 @@ Copyright (c) 2002 Greg Stein Copyright (c) 2002 Fred L. Drake, Jr. Copyright (c) 2002-2006 Karl Waclawek - Copyright (c) 2017-2021 Sebastian Pipping + Copyright (c) 2017-2026 Sebastian Pipping Copyright (c) 2025 Alfonso Gregory Licensed under the MIT license: @@ -64,14 +64,14 @@ &ns(utf8_encoding).enc /* NO_ENC */ }; -static int PTRCALL +static int NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr); } -static int PTRCALL +static int NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, const char **nextTokPtr) { return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, diff -Nru expat-2.8.3/expat/tests/Makefile.am expat-2.9.0/expat/tests/Makefile.am --- expat-2.8.3/expat/tests/Makefile.am 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/Makefile.am 2026-10-05 15:43:59.000000000 +0000 @@ -49,13 +49,16 @@ common.c \ dummy.c \ handlers.c \ + hash_tests.c \ memcheck.c \ minicheck.c \ misc_tests.c \ ns_tests.c \ nsalloc_tests.c \ + props_tests.c \ runtests.c \ - structdata.c + structdata.c \ + xcs_tests.c runtests_LDADD = ../lib/libtestpat.la @@ -69,13 +72,16 @@ common.h \ dummy.h \ handlers.h \ + hash_tests.h \ + memcheck.h \ + minicheck.h \ misc_tests.h \ ns_tests.h \ nsalloc_tests.h \ - structdata.h \ - minicheck.h \ - memcheck.h \ + props_tests.h \ README.md \ + structdata.h \ udiffer.py \ + xcs_tests.h \ xmltest.log.expected \ xmltest.sh diff -Nru expat-2.8.3/expat/tests/acc_tests.c expat-2.9.0/expat/tests/acc_tests.c --- expat-2.8.3/expat/tests/acc_tests.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/acc_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -359,6 +359,41 @@ } END_TEST +START_TEST(test_billions_laughs_attack_protection_api_float_round_trip) { + const float values[] = {1.0f, 1.1f, INFINITY}; + + for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { + set_subtest("float %f", (double)i); + const float wanted = values[i]; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + // Self-Test: Original value not already at target value + double actual = 123.456; + assert_true((float)actual != wanted); +# if XML_GE == 1 + assert_true( + XML_GetPropertyDouble( + parser, XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, &actual) + == XML_PROP_ERROR_NONE); + assert_true((float)actual != wanted); // Self-Test + + // Test: Target value applied successfully + assert_true( + XML_SetBillionLaughsAttackProtectionMaximumAmplification(parser, wanted) + == XML_TRUE); + assert_true( + XML_GetPropertyDouble( + parser, XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, &actual) + == XML_PROP_ERROR_NONE); + assert_true((float)actual == wanted); +# endif // XML_GE == 1 + XML_ParserFree(parser); + } +} +END_TEST + START_TEST(test_helper_unsigned_char_to_printable) { // Smoke test unsigned char uc = 0; @@ -451,6 +486,8 @@ tcase_add_test(tc_accounting, test_accounting_precision); tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api); + tcase_add_test(tc_accounting, + test_billions_laughs_attack_protection_api_float_round_trip); tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable); tcase_add_test__ifdef_xml_dtd(tc_accounting, test_amplification_isolated_external_parser); diff -Nru expat-2.8.3/expat/tests/acc_tests.h expat-2.9.0/expat/tests/acc_tests.h --- expat-2.8.3/expat/tests/acc_tests.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/acc_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,17 +42,9 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_ACC_TESTS_H -# define XML_ACC_TESTS_H +#define XML_ACC_TESTS_H extern void make_accounting_test_case(Suite *s); #endif /* XML_ACC_TESTS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/alloc_tests.c expat-2.9.0/expat/tests/alloc_tests.c --- expat-2.8.3/expat/tests/alloc_tests.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/alloc_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -21,6 +21,7 @@ Copyright (c) 2023 Sony Corporation / Snild Dolkow Copyright (c) 2025 Berkay Eren Ürün Copyright (c) 2026 Matthew Fernandez + Copyright (c) 2026 Filippo Tedeschi Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -563,6 +564,24 @@ } END_TEST +/* Test robustness of XML_ParserReset() with a failing allocator when setting + * encoding */ +START_TEST(test_alloc_reset_encoding) { + int i; + const int max_alloc_count = 5; + + for (i = 0; i < max_alloc_count; i++) { + g_allocation_count = i; + if (XML_ParserReset(g_parser, XCS("us-ascii")) == XML_TRUE) + break; + } + if (i == 0) + fail("Reset with encoding succeeded despite failing allocator"); + else if (i == max_alloc_count) + fail("Reset with encoding failed at max allocation count"); +} +END_TEST + /* Test robustness of XML_SetBase against a failing allocator */ START_TEST(test_alloc_set_base) { const XML_Char *new_base = XCS("/local/file/name.xml"); @@ -2118,6 +2137,10 @@ assert_true(ptr != NULL); assert_true(sizeRecordedFor(ptr) == 10); + assert_true(expat_realloc(parser, ptr, SIZE_MAX, -1) == NULL); + + assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged + assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL); assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged @@ -2273,6 +2296,40 @@ } END_TEST +START_TEST(test_alloc_tracker_api_float_round_trip) { + const float values[] = {1.0f, 1.1f, INFINITY}; + + for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { + set_subtest("float %f", (double)i); + const float wanted = values[i]; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + // Self-Test: Original value not already at target value + double actual = 123.456; + assert_true((float)actual != wanted); +#if XML_GE == 1 + assert_true( + XML_GetPropertyDouble( + parser, XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, &actual) + == XML_PROP_ERROR_NONE); + assert_true((float)actual != wanted); // Self-Test + + // Test: Target value applied successfully + assert_true(XML_SetAllocTrackerMaximumAmplification(parser, wanted) + == XML_TRUE); + assert_true( + XML_GetPropertyDouble( + parser, XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, &actual) + == XML_PROP_ERROR_NONE); + assert_true((float)actual == wanted); +#endif // XML_GE == 1 + XML_ParserFree(parser); + } +} +END_TEST + START_TEST(test_mem_api_cycle) { XML_Parser parser = XML_ParserCreate(NULL); @@ -2336,6 +2393,7 @@ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_parameter_entity); tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_dtd_default_handling); tcase_add_test(tc_alloc, test_alloc_explicit_encoding); + tcase_add_test(tc_alloc, test_alloc_reset_encoding); tcase_add_test(tc_alloc, test_alloc_set_base); tcase_add_test(tc_alloc, test_alloc_realloc_buffer); tcase_add_test__if_xml_ge(tc_alloc, test_alloc_ext_entity_realloc_buffer); @@ -2386,6 +2444,7 @@ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_threshold); tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_getbuffer_unlimited); tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_api); + tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_api_float_round_trip); tcase_add_test(tc_alloc, test_mem_api_cycle); tcase_add_test__if_xml_ge(tc_alloc, test_mem_api_unlimited); diff -Nru expat-2.8.3/expat/tests/alloc_tests.h expat-2.9.0/expat/tests/alloc_tests.h --- expat-2.8.3/expat/tests/alloc_tests.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/alloc_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,17 +42,9 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_ALLOC_TESTS_H -# define XML_ALLOC_TESTS_H +#define XML_ALLOC_TESTS_H extern void make_alloc_test_case(Suite *s); #endif /* XML_ALLOC_TESTS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/basic_tests.c expat-2.9.0/expat/tests/basic_tests.c --- expat-2.8.3/expat/tests/basic_tests.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/basic_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -23,6 +23,10 @@ Copyright (c) 2026 Francesco Bertolaccini Copyright (c) 2026 Matthew Fernandez Copyright (c) 2026 Kartik Kenchi + Copyright (c) 2026 Zeyou Liu + Copyright (c) 2026 Afonso Januário + Copyright (c) 2026 Braian Plaku + Copyright (c) 2026 Filippo Tedeschi Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -54,7 +58,8 @@ #include "expat_config.h" #include -#include // ULONG_MAX +#include // PRIu64 +#include // ULONG_MAX #include #include #include @@ -69,6 +74,7 @@ #include "handlers.h" #include "siphash.h" #include "basic_tests.h" +#include "../lib/xcs.h" #define EXPAT_TESTS_ASAN 1 @@ -213,7 +219,7 @@ "This triggers the table growth and collides with b2\n" "\n"; - XML_SetHashSalt(g_parser, COLLIDING_HASH_SALT); + xmlSetHashSalt(g_parser, COLLIDING_HASH_SALT); if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); @@ -643,16 +649,15 @@ const char *text = "\n" "\n" "\n"; - XML_Size lineno; + uint64_t lineno; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); - lineno = XML_GetCurrentLineNumber(g_parser); + lineno = XML_GetCurrentLineNumber64(g_parser); if (lineno != 4) { char buffer[100]; - snprintf(buffer, sizeof(buffer), - "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno); + snprintf(buffer, sizeof(buffer), "expected 4 lines, saw %" PRIu64, lineno); fail(buffer); } } @@ -661,16 +666,16 @@ /* Regression test #2 for SF bug #653180. */ START_TEST(test_column_number_after_parse) { const char *text = ""; - XML_Size colno; + uint64_t colno; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) == XML_STATUS_ERROR) xml_failure(g_parser); - colno = XML_GetCurrentColumnNumber(g_parser); + colno = XML_GetCurrentColumnNumber64(g_parser); if (colno != 11) { char buffer[100]; - snprintf(buffer, sizeof(buffer), - "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno); + snprintf(buffer, sizeof(buffer), "expected 11 columns, saw %" PRIu64, + colno); fail(buffer); } } @@ -713,16 +718,15 @@ const char *text = "\n" " \n" " "; /* missing */ - XML_Size lineno; + uint64_t lineno; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Expected a parse error"); - lineno = XML_GetCurrentLineNumber(g_parser); + lineno = XML_GetCurrentLineNumber64(g_parser); if (lineno != 3) { char buffer[100]; - snprintf(buffer, sizeof(buffer), - "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno); + snprintf(buffer, sizeof(buffer), "expected 3 lines, saw %" PRIu64, lineno); fail(buffer); } } @@ -733,16 +737,15 @@ const char *text = "\n" " \n" " "; /* missing */ - XML_Size colno; + uint64_t colno; if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) != XML_STATUS_ERROR) fail("Expected a parse error"); - colno = XML_GetCurrentColumnNumber(g_parser); + colno = XML_GetCurrentColumnNumber64(g_parser); if (colno != 4) { char buffer[100]; - snprintf(buffer, sizeof(buffer), - "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno); + snprintf(buffer, sizeof(buffer), "expected 4 columns, saw %" PRIu64, colno); fail(buffer); } } @@ -918,9 +921,8 @@ for (i = 0; atts[i] != NULL; i += 2) { const XML_Char *attrname = atts[i]; const XML_Char *value = atts[i + 1]; - if (xcstrcmp(XCS("attr"), attrname) == 0 - || xcstrcmp(XCS("ents"), attrname) == 0 - || xcstrcmp(XCS("refs"), attrname) == 0) { + if (xcscmp(XCS("attr"), attrname) == 0 || xcscmp(XCS("ents"), attrname) == 0 + || xcscmp(XCS("refs"), attrname) == 0) { if (! is_whitespace_normalized(value, 0)) { char buffer[256]; snprintf(buffer, sizeof(buffer), @@ -997,6 +999,50 @@ } END_TEST +/* Regression test for GH #967: Expat only implements XML 1.0 Fourth + Edition, so a declared version outside the "1.x" family must be + rejected rather than silently accepted. A version matching the Fifth + Edition's VersionNum production ("1." followed by one or more digits) + is accepted even though Expat itself only implements 1.0 Fourth + Edition, per GH #967's review thread: rejecting "1.1" now would just + have to be reverted once Expat tracks the Fifth Edition, so it's let + through rather than blocked twice. */ +START_TEST(test_xmldecl_wrong_version_number) { + const char *const badVersions[] = {"2.3", "0.9", "10", "1.", "1", " 1.0 "}; + + for (size_t i = 0; i < sizeof(badVersions) / sizeof(badVersions[0]); i++) { + char doc[64]; + snprintf(doc, sizeof(doc), "\n", badVersions[i]); + set_subtest("version='%s'", badVersions[i]); + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE) + == XML_STATUS_ERROR); + assert_true(XML_GetErrorCode(parser) == XML_ERROR_XML_DECL); + XML_ParserFree(parser); + } +} +END_TEST + +/* GH #967's review pointed out that XML 1.0 Fifth Edition relaxed + VersionNum to "1." followed by one or more digits, so versions like + "1.1" and "1.123" should parse rather than being rejected. */ +START_TEST(test_xmldecl_accepts_1_x_version) { + const char *const goodVersions[] = {"1.1", "1.123"}; + + for (size_t i = 0; i < sizeof(goodVersions) / sizeof(goodVersions[0]); i++) { + char doc[64]; + snprintf(doc, sizeof(doc), "\n", goodVersions[i]); + set_subtest("version='%s'", goodVersions[i]); + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc), XML_TRUE) + == XML_STATUS_OK); + XML_ParserFree(parser); + } +} +END_TEST + /* Regression test for SF bug #584832. */ START_TEST(test_unknown_encoding_internal_entity) { const char *text = "\n" @@ -1845,6 +1891,301 @@ } END_TEST +// Helper that creates a UTF-16LE copy of UTF-16BE literal input and vice versa +static char * +utf16_dup_flipped(const char *text, size_t lenBytes) { + assert_true(lenBytes < SIZE_MAX); + assert_true(lenBytes % 2 == 0); + char *const buffer = malloc(lenBytes + 1); + assert_true(buffer != NULL); + + for (size_t i = 0; i < lenBytes; i++) { + // This maps 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, 4 -> 5, .. + size_t j = i + ((i % 2 == 0) ? +1 : -1); + assert_true(j < lenBytes); + buffer[j] = text[i]; + } + + buffer[lenBytes] = '\0'; + + return buffer; +} + +/* Tests that invalid combinations of surrogates are detected when decoding + UTF-16, both little-endian and big-endian. + Previously, a high surrogate not followed by a low surrogate slipped + through. Without validation the high would consume the next + code unit as a fake low, hiding e.g. a following '<' from the + tokenizer. */ +START_TEST(test_utf16_surrogate_pairs) { + struct TestCase { + const char *idea; + const char *content; + bool expectedSuccess; + }; + + struct TestCase testCases[] = { + // Group {smallest high - 1}{*} + {"{smallest high - 1}{smallest high - 1}", + "\0<\0a\0>" + "\xD7\xFF" + "\xD7\xFF" + "\0<\0/\0a\0>", + true}, + {"{smallest high - 1}{smallest high}", + "\0<\0a\0>" + "\xD7\xFF" + "\xD8\x00" + "\0<\0/\0a\0>", + false}, + {"{smallest high - 1}{largest high}", + "\0<\0a\0>" + "\xD7\xFF" + "\xDB\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest high - 1}{smallest low}", + "\0<\0a\0>" + "\xD7\xFF" + "\xDC\x00" + "\0<\0/\0a\0>", + false}, + {"{smallest high - 1}{largest low}", + "\0<\0a\0>" + "\xD7\xFF" + "\xDF\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest high - 1}{largest low + 1}", + "\0<\0a\0>" + "\xD7\xFF" + "\xE0\x00" + "\0<\0/\0a\0>", + true}, + // Group {smallest high}{*} + {"{smallest high}{smallest high - 1}", + "\0<\0a\0>" + "\xD8\x00" + "\xD7\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest high}{smallest high}", + "\0<\0a\0>" + "\xD8\x00" + "\xD8\x00" + "\0<\0/\0a\0>", + false}, + {"{smallest high}{largest high}", + "\0<\0a\0>" + "\xD8\x00" + "\xDB\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest high}{smallest low}", + "\0<\0a\0>" + "\xD8\x00" + "\xDC\x00" + "\0<\0/\0a\0>", + true}, + {"{smallest high}{largest low}", + "\0<\0a\0>" + "\xD8\x00" + "\xDF\xFF" + "\0<\0/\0a\0>", + true}, + {"{smallest high}{largest low + 1}", + "\0<\0a\0>" + "\xD8\x00" + "\xE0\x00" + "\0<\0/\0a\0>", + false}, + // Group {largest high}{*} + {"{largest high}{smallest high - 1}", + "\0<\0a\0>" + "\xDB\xFF" + "\xD7\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest high}{smallest high}", + "\0<\0a\0>" + "\xDB\xFF" + "\xD8\x00" + "\0<\0/\0a\0>", + false}, + {"{largest high}{largest high}", + "\0<\0a\0>" + "\xDB\xFF" + "\xDB\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest high}{smallest low}", + "\0<\0a\0>" + "\xDB\xFF" + "\xDC\x00" + "\0<\0/\0a\0>", + true}, + {"{largest high}{largest low}", + "\0<\0a\0>" + "\xDB\xFF" + "\xDF\xFF" + "\0<\0/\0a\0>", + true}, + {"{largest high}{largest low + 1}", + "\0<\0a\0>" + "\xDB\xFF" + "\xE0\x00" + "\0<\0/\0a\0>", + false}, + // Group {smallest low}{*} + {"{smallest low}{smallest high - 1}", + "\0<\0a\0>" + "\xDC\x00" + "\xD7\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest low}{smallest high}", + "\0<\0a\0>" + "\xDC\x00" + "\xD8\x00" + "\0<\0/\0a\0>", + false}, + {"{smallest low}{largest high}", + "\0<\0a\0>" + "\xDC\x00" + "\xDB\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest low}{smallest low}", + "\0<\0a\0>" + "\xDC\x00" + "\xDC\x00" + "\0<\0/\0a\0>", + false}, + {"{smallest low}{largest low}", + "\0<\0a\0>" + "\xDC\x00" + "\xDF\xFF" + "\0<\0/\0a\0>", + false}, + {"{smallest low}{largest low + 1}", + "\0<\0a\0>" + "\xDC\x00" + "\xE0\x00" + "\0<\0/\0a\0>", + false}, + // Group {largest low}{*} + {"{largest low}{smallest high - 1}", + "\0<\0a\0>" + "\xDF\xFF" + "\xD7\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest low}{smallest high}", + "\0<\0a\0>" + "\xDF\xFF" + "\xD8\x00" + "\0<\0/\0a\0>", + false}, + {"{largest low}{largest high}", + "\0<\0a\0>" + "\xDF\xFF" + "\xDB\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest low}{smallest low}", + "\0<\0a\0>" + "\xDF\xFF" + "\xDC\x00" + "\0<\0/\0a\0>", + false}, + {"{largest low}{largest low}", + "\0<\0a\0>" + "\xDF\xFF" + "\xDF\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest low}{largest low + 1}", + "\0<\0a\0>" + "\xDF\xFF" + "\xE0\x00" + "\0<\0/\0a\0>", + false}, + // Group {largest low + 1}{*} + {"{largest low + 1}{smallest high - 1}", + "\0<\0a\0>" + "\xE0\x00" + "\xD7\xFF" + "\0<\0/\0a\0>", + true}, + {"{largest low + 1}{smallest high}", + "\0<\0a\0>" + "\xE0\x00" + "\xD8\x00" + "\0<\0/\0a\0>", + false}, + {"{largest low + 1}{largest high}", + "\0<\0a\0>" + "\xE0\x00" + "\xDB\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest low + 1}{smallest low}", + "\0<\0a\0>" + "\xE0\x00" + "\xDC\x00" + "\0<\0/\0a\0>", + false}, + {"{largest low + 1}{largest low}", + "\0<\0a\0>" + "\xE0\x00" + "\xDF\xFF" + "\0<\0/\0a\0>", + false}, + {"{largest low + 1}{largest low + 1}", + "\0<\0a\0>" + "\xE0\x00" + "\xE0\x00" + "\0<\0/\0a\0>", + true}, + }; + + for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) { + set_subtest("%s", testCases[i].idea); + + const int lenBytes = /**/ 6 + /*first*/ 2 + /*second*/ 2 + /**/ 8; + const bool expectedSuccess = testCases[i].expectedSuccess; + const enum XML_Status expectedStatus + = (expectedSuccess ? XML_STATUS_OK : XML_STATUS_ERROR); + + const char *const bigEndian = testCases[i].content; + char *const littleEndian = utf16_dup_flipped(bigEndian, lenBytes); + assert_true(littleEndian != NULL); + const char *endianCases[] = {bigEndian, littleEndian}; + + for (size_t j = 0; j < sizeof(endianCases) / sizeof(endianCases[0]); j++) { + const char *text = endianCases[j]; + + assert_true(text[lenBytes] == '\0'); // self-test + assert_true((text[0] == '\0') + != (text[lenBytes - 1] == '\0')); // self-test + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + assert_true(_XML_Parse_SINGLE_BYTES(parser, text, lenBytes, XML_TRUE) + == expectedStatus); + if (! expectedSuccess) { + assert_true(XML_GetErrorCode(parser) == XML_ERROR_INVALID_TOKEN); + } + + XML_ParserFree(parser); + } + + free(littleEndian); + } +} +END_TEST + START_TEST(test_bad_cdata) { struct CaseData { const char *text; @@ -2256,6 +2597,57 @@ } END_TEST +/* Test XML_DefaultCurrent does not dereference NULL when outside an event */ +START_TEST(test_default_current_outside_event) { + struct handler_record_list handlerCalls; + handlerCalls.count = 0; + + set_subtest("NULL parser"); + XML_DefaultCurrent(NULL); + + set_subtest("before parse"); + { + XML_Parser parser = XML_ParserCreate(NULL); + XML_SetDefaultHandler(parser, record_default_handler); + XML_SetUserData(parser, &handlerCalls); + + XML_DefaultCurrent(parser); + assert_true(handlerCalls.count == 0); + + XML_ParserFree(parser); + } + + set_subtest("after parse"); + { + const char *text = "hello"; + XML_Parser parser = XML_ParserCreate(NULL); + if (XML_Parse(parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(parser); + + XML_SetDefaultHandler(parser, record_default_handler); + XML_SetUserData(parser, &handlerCalls); + + XML_DefaultCurrent(parser); + assert_true(handlerCalls.count == 0); + + XML_ParserFree(parser); + } + + set_subtest("explicit encoding before parse"); + { + XML_Parser parser = XML_ParserCreate(XCS("ISO-8859-1")); + XML_SetDefaultHandler(parser, record_default_handler); + XML_SetUserData(parser, &handlerCalls); + + XML_DefaultCurrent(parser); + assert_true(handlerCalls.count == 0); + + XML_ParserFree(parser); + } +} +END_TEST + /* Test DTD element parsing code paths */ START_TEST(test_dtd_elements) { const char *text = "", NULL, NULL}; /* Check hash salt is passed through too */ - XML_SetHashSalt(g_parser, 0x12345678); + xmlSetHashSalt(g_parser, 0x12345678); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(g_parser, &test_data); XML_SetExternalEntityRefHandler(g_parser, external_entity_loader); @@ -2376,7 +2767,7 @@ != XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING) fail("Failed to reject late foreign DTD setting"); /* Ditto for the hash salt */ - if (XML_SetHashSalt(g_parser, 0x23456789)) + if (xmlSetHashSalt(g_parser, 0x23456789)) fail("Failed to reject late hash salt change"); /* Now finish the parse */ @@ -2427,7 +2818,7 @@ ExtTest test_data = {"", NULL, NULL}; /* Check hash salt is passed through too */ - XML_SetHashSalt(g_parser, 0x12345678); + xmlSetHashSalt(g_parser, 0x12345678); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetUserData(g_parser, &test_data); XML_SetExternalEntityRefHandler(g_parser, external_entity_loader); @@ -2446,7 +2837,7 @@ != XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING) fail("Failed to reject late foreign DTD setting"); /* Ditto for the hash salt */ - if (XML_SetHashSalt(g_parser, 0x23456789)) + if (xmlSetHashSalt(g_parser, 0x23456789)) fail("Failed to reject late hash salt change"); /* Now finish the parse */ @@ -2491,7 +2882,7 @@ old_base = XML_GetBase(g_parser); if (XML_SetBase(g_parser, new_base) != XML_STATUS_OK) fail("Unable to set base"); - if (xcstrcmp(XML_GetBase(g_parser), new_base) != 0) + if (xcscmp(XML_GetBase(g_parser), new_base) != 0) fail("Base setting not correct"); if (XML_SetBase(g_parser, NULL) != XML_STATUS_OK) fail("Unable to NULL base"); @@ -2809,6 +3200,79 @@ } END_TEST +static void XMLCALL +check_second_attr_normalization(void *userData, const XML_Char *name, + const XML_Char **atts) { + int *const seen_second = userData; + UNUSED_P(name); + + for (size_t i = 0; atts[i] != NULL; i += 2) { + const XML_Char *const key = atts[i]; + const XML_Char *const value = atts[i + 1]; + if (xcscmp(key, XCS("second")) != 0) + continue; + *seen_second = 1; + /* Attribute "second" is not of type CDATA, so leading, trailing and + * repeated whitespace is to be normalized away. */ + if (xcscmp(value, XCS("a b")) != 0) + fail("Attribute of non-CDATA type was not whitespace-normalized"); + } +} + +static int XMLCALL +external_entity_attr_checker(XML_Parser parser, const XML_Char *context, + const XML_Char *base, const XML_Char *systemId, + const XML_Char *publicId) { + const char *const text = ""; + UNUSED_P(base); + UNUSED_P(systemId); + UNUSED_P(publicId); + + XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); + if (ext_parser == NULL) + fail("Could not create external entity parser"); + + if (_XML_Parse_SINGLE_BYTES(ext_parser, text, (int)strlen(text), XML_TRUE) + != XML_STATUS_OK) + xml_failure(ext_parser); + + XML_ParserFree(ext_parser); + return XML_STATUS_OK; +} + +START_TEST(test_default_attr_index_after_dtd_copy) { + /* Function storeAtts resolves member .attIndex of structure + * NAME_AND_DEFAULT_ATTRIBUTE to tell whether an attribute value needs + * whitespace normalization, so function dtdCopy needs to carry that index + * over to the copy. Attribute "first" is declared before attribute + * "second" so that a mixed-up index resolves to the wrong declaration. + */ + const char *text = "\n" + " \n" + " \n" + " \n" + " \n" + "]>\n" + "&e;\n"; + int seen_second = 0; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + XML_SetUserData(parser, &seen_second); + XML_SetExternalEntityRefHandler(parser, external_entity_attr_checker); + XML_SetStartElementHandler(parser, check_second_attr_normalization); + + if (_XML_Parse_SINGLE_BYTES(parser, text, (int)strlen(text), XML_TRUE) + != XML_STATUS_OK) + xml_failure(parser); + if (! seen_second) + fail("Attribute \"second\" has not been reported"); + + XML_ParserFree(parser); +} +END_TEST + /* Test reset works correctly in the middle of processing an internal * entity. Exercises some obscure code in XML_ParserReset(). */ @@ -3310,6 +3774,46 @@ } END_TEST +/* Test XML_ParseBuffer rejects calls where len exceeds available buffer */ +START_TEST(test_parse_buffer_exceeds_buffer) { + for (int isFinal = 0; isFinal < 2; isFinal++) { + set_subtest("isFinal=%d", isFinal); + + XML_Parser parser = XML_ParserCreate(NULL); + + /* Calling XML_ParseBuffer without any prior XML_GetBuffer call */ + if (XML_ParseBuffer(parser, 10, isFinal) != XML_STATUS_ERROR) + fail("XML_ParseBuffer without XML_GetBuffer was expected to fail."); + if (XML_GetErrorCode(parser) != XML_ERROR_NO_BUFFER) + fail("Expected XML_ERROR_NO_BUFFER."); + + /* Acquire a small buffer */ + void *const buffer = XML_GetBuffer(parser, 10); + if (buffer == NULL) + fail("XML_GetBuffer failed."); + + /* Calling XML_ParseBuffer with len exceeding available buffer capacity */ + if (XML_ParseBuffer(parser, 10000, isFinal) != XML_STATUS_ERROR) + fail("XML_ParseBuffer with len > capacity was expected to fail."); + if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT) + fail("Expected XML_ERROR_INVALID_ARGUMENT."); + + /* Valid parse */ + memcpy(buffer, "", 7); + if (XML_ParseBuffer(parser, 7, XML_FALSE) != XML_STATUS_OK) + xml_failure(parser); + + /* Subsequent XML_ParseBuffer where len exceeds remaining capacity */ + if (XML_ParseBuffer(parser, 10000, isFinal) != XML_STATUS_ERROR) + fail("XML_ParseBuffer on subsequent chunk was expected to fail."); + if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_ARGUMENT) + fail("Expected XML_ERROR_INVALID_ARGUMENT."); + + XML_ParserFree(parser); + } +} +END_TEST + /* Test odd corners of the XML_GetBuffer interface */ static enum XML_Status get_feature(enum XML_FeatureEnum feature_id, long *presult) { @@ -3440,6 +3944,8 @@ maxbuf = maxbuf / 2; fprintf(stderr, "Reducing maxbuf to %d...\n", maxbuf); } +#else + UNUSED_P(maxbuf); #endif for (int i = 0; i < num_prefixes; ++i) { @@ -3463,6 +3969,8 @@ // The limit should be consistent; no prefix should allow us to // reach above the max buffer size. assert_true(XML_GetBuffer(parser, maxbuf + 1) == NULL); +#else + UNUSED_P(maxbuf); #endif XML_ParserFree(parser); @@ -3488,16 +3996,16 @@ START_TEST(test_byte_info_at_end) { const char *text = ""; - if (XML_GetCurrentByteIndex(g_parser) != -1 - || XML_GetCurrentByteCount(g_parser) != 0) + if (XML_GetCurrentByteIndex64(g_parser) != -1 + || XML_GetCurrentByteCount64(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) + if (XML_GetCurrentByteCount64(g_parser) != 0) fail("Terminal byte count incorrect"); - if (XML_GetCurrentByteIndex(g_parser) != (XML_Index)strlen(text)) + if (XML_GetCurrentByteIndex64(g_parser) != (int64_t)strlen(text)) fail("Terminal byte index incorrect"); } END_TEST @@ -3511,9 +4019,9 @@ 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) + if (XML_GetCurrentByteCount64(g_parser) != 0) fail("Error byte count incorrect"); - if (XML_GetCurrentByteIndex(g_parser) != strlen(PRE_ERROR_STR)) + if (XML_GetCurrentByteIndex64(g_parser) != strlen(PRE_ERROR_STR)) fail("Error byte index incorrect"); } END_TEST @@ -3526,11 +4034,12 @@ #define END_ELEMENT "" START_TEST(test_byte_info_at_cdata) { const char *text = START_ELEMENT CDATA_TEXT END_ELEMENT; - int offset, size; + int64_t offset; + uint64_t size; ByteTestData data; /* Check initial context is empty */ - if (XML_GetInputContext(g_parser, &offset, &size) != NULL) + if (XML_GetInputContext64(g_parser, &offset, &size) != NULL) fail("Unexpected context at start of parse"); data.start_element_len = (int)strlen(START_ELEMENT); @@ -3731,7 +4240,7 @@ if (ext_parser == NULL) fail("Could not create external entity parser"); - if (! xcstrcmp(systemId, XCS("004-2.ent"))) { + if (! xcscmp(systemId, XCS("004-2.ent"))) { struct bom_testdata *const testdata = XML_GetUserData(parser); const char *const external = testdata->external; const int split = testdata->split; @@ -3742,7 +4251,7 @@ xml_failure(ext_parser); } text = external + split; // the parse below will continue where we left off. - } else if (! xcstrcmp(systemId, XCS("004-1.ent"))) { + } else if (! xcscmp(systemId, XCS("004-1.ent"))) { text = "\n" "\n" "\n"; @@ -6678,6 +7187,8 @@ tcase_add_test(tc_basic, test_xmldecl_missing_attr); tcase_add_test(tc_basic, test_xmldecl_missing_value); tcase_add_test(tc_basic, test_xmldecl_empty_version); + tcase_add_test(tc_basic, test_xmldecl_wrong_version_number); + tcase_add_test(tc_basic, test_xmldecl_accepts_1_x_version); tcase_add_test__if_xml_ge(tc_basic, test_unknown_encoding_internal_entity); tcase_add_test(tc_basic, test_unrecognised_encoding_internal_entity); tcase_add_test__ifdef_xml_dtd(tc_basic, test_ext_entity_set_encoding); @@ -6711,12 +7222,14 @@ tcase_add_test(tc_basic, test_long_cdata_utf16); tcase_add_test(tc_basic, test_multichar_cdata_utf16); tcase_add_test(tc_basic, test_utf16_bad_surrogate_pair); + tcase_add_test(tc_basic, test_utf16_surrogate_pairs); tcase_add_test(tc_basic, test_bad_cdata); tcase_add_test(tc_basic, test_bad_cdata_utf16); tcase_add_test(tc_basic, test_stop_parser_between_cdata_calls); tcase_add_test(tc_basic, test_suspend_parser_between_cdata_calls); tcase_add_test(tc_basic, test_memory_allocation); tcase_add_test__if_xml_ge(tc_basic, test_default_current); + tcase_add_test(tc_basic, test_default_current_outside_event); tcase_add_test(tc_basic, test_dtd_elements); tcase_add_test(tc_basic, test_dtd_elements_nesting); tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd); @@ -6737,6 +7250,7 @@ tcase_add_test(tc_basic, test_duplicate_cdata_attribute_multiple_attlistdecl_3); tcase_add_test(tc_basic, test_duplicate_id_attribute_multiple_attlistdecl); + tcase_add_test__if_xml_ge(tc_basic, test_default_attr_index_after_dtd_copy); tcase_add_test__if_xml_ge(tc_basic, test_reset_in_entity); tcase_add_test(tc_basic, test_resume_invalid_parse); tcase_add_test(tc_basic, test_resume_resuspended); @@ -6758,6 +7272,7 @@ tcase_add_test(tc_basic, test_empty_parse); tcase_add_test(tc_basic, test_negative_len_parse); tcase_add_test(tc_basic, test_negative_len_parse_buffer); + tcase_add_test(tc_basic, test_parse_buffer_exceeds_buffer); tcase_add_test(tc_basic, test_get_buffer_1); tcase_add_test(tc_basic, test_get_buffer_2); #if XML_CONTEXT_BYTES > 0 diff -Nru expat-2.8.3/expat/tests/basic_tests.h expat-2.9.0/expat/tests/basic_tests.h --- expat-2.8.3/expat/tests/basic_tests.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/basic_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,17 +42,9 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_BASIC_TESTS_H -# define XML_BASIC_TESTS_H +#define XML_BASIC_TESTS_H extern void make_basic_test_case(Suite *s); #endif /* XML_BASIC_TESTS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/benchmark/benchmark.c expat-2.9.0/expat/tests/benchmark/benchmark.c --- expat-2.8.3/expat/tests/benchmark/benchmark.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/benchmark/benchmark.c 2026-10-05 15:43:59.000000000 +0000 @@ -8,7 +8,7 @@ Copyright (c) 2003-2006 Karl Waclawek Copyright (c) 2005-2007 Steven Solie - Copyright (c) 2017-2025 Sebastian Pipping + Copyright (c) 2017-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Licensed under the MIT license: @@ -40,7 +40,8 @@ # include // close #endif -#include // open +#include // PRIu64 +#include // open #include #include #include // ptrdiff_t @@ -48,12 +49,6 @@ #include #include "expat.h" -#ifdef XML_LARGE_SIZE -# define XML_FMT_INT_MOD "ll" -#else -# define XML_FMT_INT_MOD "l" -#endif - #ifdef XML_UNICODE_WCHAR_T # define XML_FMT_STR "ls" #else @@ -148,11 +143,11 @@ assert(parseBufferSize <= (ptrdiff_t)bufferSize); if (! XML_Parse(parser, XMLBufPtr, (int)parseBufferSize, isFinal)) { fprintf(stderr, - "error '%" XML_FMT_STR "' at line %" XML_FMT_INT_MOD - "u character %" XML_FMT_INT_MOD "u\n", + "error '%" XML_FMT_STR "' at line %" PRIu64 + " character %" PRIu64 "\n", XML_ErrorString(XML_GetErrorCode(parser)), - XML_GetCurrentLineNumber(parser), - XML_GetCurrentColumnNumber(parser)); + XML_GetCurrentLineNumber64(parser), + XML_GetCurrentColumnNumber64(parser)); free(XMLBuf); XML_ParserFree(parser); return 4; diff -Nru expat-2.8.3/expat/tests/chardata.c expat-2.9.0/expat/tests/chardata.c --- expat-2.8.3/expat/tests/chardata.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/chardata.c 2026-10-05 15:43:59.000000000 +0000 @@ -9,7 +9,7 @@ Copyright (c) 2002-2004 Fred L. Drake, Jr. Copyright (c) 2003 Greg Stein Copyright (c) 2016 Gilles Espinasse - Copyright (c) 2016-2023 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Joe Orton Copyright (c) 2017 Rhodri James Copyright (c) 2022 Sean McBride @@ -49,7 +49,7 @@ #include #include "chardata.h" -#include "../lib/xcsinc.c" +#include "../lib/xcs.h" void CharData_Init(CharData *storage) { diff -Nru expat-2.8.3/expat/tests/chardata.h expat-2.9.0/expat/tests/chardata.h --- expat-2.8.3/expat/tests/chardata.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/chardata.h 2026-10-05 15:43:59.000000000 +0000 @@ -9,7 +9,7 @@ Copyright (c) 2002-2004 Fred L. Drake, Jr. Copyright (c) 2017 Rhodri James - Copyright (c) 2017 Sebastian Pipping + Copyright (c) 2017-2026 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -34,10 +34,6 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_CHARDATA_H # define XML_CHARDATA_H 1 @@ -57,7 +53,3 @@ int CharData_CheckXMLChars(CharData *storage, const XML_Char *s); #endif /* XML_CHARDATA_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/common.c expat-2.9.0/expat/tests/common.c --- expat-2.8.3/expat/tests/common.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/common.c 2026-10-05 15:43:59.000000000 +0000 @@ -48,7 +48,8 @@ #include #include -#include // for SIZE_MAX +#include // for PRIu64 +#include // for SIZE_MAX #include #include @@ -188,10 +189,10 @@ char buffer[1024]; enum XML_Error err = XML_GetErrorCode(parser); snprintf(buffer, sizeof(buffer), - " %d: %" XML_FMT_STR " (line %" XML_FMT_INT_MOD - "u, offset %" XML_FMT_INT_MOD "u)\n reported from %s, line %d\n", - err, XML_ErrorString(err), XML_GetCurrentLineNumber(parser), - XML_GetCurrentColumnNumber(parser), file, line); + " %d: %" XML_FMT_STR " (line %" PRIu64 ", offset %" PRIu64 + ")\n reported from %s, line %d\n", + err, XML_ErrorString(err), XML_GetCurrentLineNumber64(parser), + XML_GetCurrentColumnNumber64(parser), file, line); _fail(file, line, buffer); } diff -Nru expat-2.8.3/expat/tests/common.h expat-2.9.0/expat/tests/common.h --- expat-2.8.3/expat/tests/common.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/common.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -43,42 +43,32 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_COMMON_H -# define XML_COMMON_H +#define XML_COMMON_H -# include "expat_config.h" -# include "minicheck.h" -# include "chardata.h" - -# ifdef XML_LARGE_SIZE -# define XML_FMT_INT_MOD "ll" -# else -# define XML_FMT_INT_MOD "l" -# endif +#include "expat_config.h" +#include "minicheck.h" +#include "chardata.h" + +#ifdef XML_LARGE_SIZE +# define XML_FMT_INT_MOD "ll" +#else +# define XML_FMT_INT_MOD "l" +#endif -# ifdef XML_UNICODE_WCHAR_T -# define XML_FMT_STR "ls" -# include -# define xcstrlen(s) wcslen(s) -# define xcstrcmp(s, t) wcscmp((s), (t)) -# define xcstrncmp(s, t, n) wcsncmp((s), (t), (n)) -# define XCS(s) _XCS(s) -# define _XCS(s) L##s +#ifdef XML_UNICODE_WCHAR_T +# define XML_FMT_STR "ls" +# include +# define XCS(s) _XCS(s) +# define _XCS(s) L##s +#else +# ifdef XML_UNICODE +# error "No support for UTF-16 character without wchar_t in tests" # else -# ifdef XML_UNICODE -# error "No support for UTF-16 character without wchar_t in tests" -# else -# define XML_FMT_STR "s" -# define xcstrlen(s) strlen(s) -# define xcstrcmp(s, t) strcmp((s), (t)) -# define xcstrncmp(s, t, n) strncmp((s), (t), (n)) -# define XCS(s) s -# endif /* XML_UNICODE */ -# endif /* XML_UNICODE_WCHAR_T */ +# define XML_FMT_STR "s" +# define XCS(s) s +# endif /* XML_UNICODE */ +#endif /* XML_UNICODE_WCHAR_T */ extern XML_Parser g_parser; @@ -98,7 +88,7 @@ extern void _xml_failure(XML_Parser parser, const char *file, int line); -# define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__) +#define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__) extern enum XML_Status _XML_Parse_SINGLE_BYTES(XML_Parser parser, const char *s, int len, int isFinal); @@ -107,8 +97,8 @@ const char *errorMessage, const char *file, int lineno); -# define expect_failure(text, errorCode, errorMessage) \ - _expect_failure((text), (errorCode), (errorMessage), __FILE__, __LINE__) +#define expect_failure(text, errorCode, errorMessage) \ + _expect_failure((text), (errorCode), (errorMessage), __FILE__, __LINE__) /* Support functions for handlers to collect up character and attribute data. */ @@ -116,14 +106,14 @@ extern void _run_character_check(const char *text, const XML_Char *expected, const char *file, int line); -# define run_character_check(text, expected) \ - _run_character_check(text, expected, __FILE__, __LINE__) +#define run_character_check(text, expected) \ + _run_character_check(text, expected, __FILE__, __LINE__) extern void _run_attribute_check(const char *text, const XML_Char *expected, const char *file, int line); -# define run_attribute_check(text, expected) \ - _run_attribute_check(text, expected, __FILE__, __LINE__) +#define run_attribute_check(text, expected) \ + _run_attribute_check(text, expected, __FILE__, __LINE__) typedef struct ExtTest { const char *parse_text; @@ -135,11 +125,11 @@ const XML_Char *expected, const char *file, int line); -# define run_ext_character_check(text, test_data, expected) \ - _run_ext_character_check(text, test_data, expected, __FILE__, __LINE__) +#define run_ext_character_check(text, test_data, expected) \ + _run_ext_character_check(text, test_data, expected, __FILE__, __LINE__) -# define ALLOC_ALWAYS_SUCCEED (-1) -# define REALLOC_ALWAYS_SUCCEED (-1) +#define ALLOC_ALWAYS_SUCCEED (-1) +#define REALLOC_ALWAYS_SUCCEED (-1) extern int g_allocation_count; extern int g_reallocation_count; @@ -151,7 +141,3 @@ extern char *portable_strndup(const char *s, size_t n); #endif /* XML_COMMON_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/dummy.h expat-2.9.0/expat/tests/dummy.h --- expat-2.8.3/expat/tests/dummy.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/dummy.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,31 +42,27 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_DUMMY_H -# define XML_DUMMY_H +#define XML_DUMMY_H -# define DUMMY_START_DOCTYPE_HANDLER_FLAG (1UL << 0) -# define DUMMY_END_DOCTYPE_HANDLER_FLAG (1UL << 1) -# define DUMMY_ENTITY_DECL_HANDLER_FLAG (1UL << 2) -# define DUMMY_NOTATION_DECL_HANDLER_FLAG (1UL << 3) -# define DUMMY_ELEMENT_DECL_HANDLER_FLAG (1UL << 4) -# define DUMMY_ATTLIST_DECL_HANDLER_FLAG (1UL << 5) -# define DUMMY_COMMENT_HANDLER_FLAG (1UL << 6) -# define DUMMY_PI_HANDLER_FLAG (1UL << 7) -# define DUMMY_START_ELEMENT_HANDLER_FLAG (1UL << 8) -# define DUMMY_START_CDATA_HANDLER_FLAG (1UL << 9) -# define DUMMY_END_CDATA_HANDLER_FLAG (1UL << 10) -# define DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG (1UL << 11) -# define DUMMY_START_NS_DECL_HANDLER_FLAG (1UL << 12) -# define DUMMY_END_NS_DECL_HANDLER_FLAG (1UL << 13) -# define DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG (1UL << 14) -# define DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG (1UL << 15) -# define DUMMY_SKIP_HANDLER_FLAG (1UL << 16) -# define DUMMY_DEFAULT_HANDLER_FLAG (1UL << 17) +#define DUMMY_START_DOCTYPE_HANDLER_FLAG (1UL << 0) +#define DUMMY_END_DOCTYPE_HANDLER_FLAG (1UL << 1) +#define DUMMY_ENTITY_DECL_HANDLER_FLAG (1UL << 2) +#define DUMMY_NOTATION_DECL_HANDLER_FLAG (1UL << 3) +#define DUMMY_ELEMENT_DECL_HANDLER_FLAG (1UL << 4) +#define DUMMY_ATTLIST_DECL_HANDLER_FLAG (1UL << 5) +#define DUMMY_COMMENT_HANDLER_FLAG (1UL << 6) +#define DUMMY_PI_HANDLER_FLAG (1UL << 7) +#define DUMMY_START_ELEMENT_HANDLER_FLAG (1UL << 8) +#define DUMMY_START_CDATA_HANDLER_FLAG (1UL << 9) +#define DUMMY_END_CDATA_HANDLER_FLAG (1UL << 10) +#define DUMMY_UNPARSED_ENTITY_DECL_HANDLER_FLAG (1UL << 11) +#define DUMMY_START_NS_DECL_HANDLER_FLAG (1UL << 12) +#define DUMMY_END_NS_DECL_HANDLER_FLAG (1UL << 13) +#define DUMMY_START_DOCTYPE_DECL_HANDLER_FLAG (1UL << 14) +#define DUMMY_END_DOCTYPE_DECL_HANDLER_FLAG (1UL << 15) +#define DUMMY_SKIP_HANDLER_FLAG (1UL << 16) +#define DUMMY_DEFAULT_HANDLER_FLAG (1UL << 17) extern void init_dummy_handlers(void); extern unsigned long get_dummy_handler_flags(void); @@ -146,7 +142,3 @@ int is_parameter_entity); #endif /* XML_DUMMY_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/handlers.c expat-2.9.0/expat/tests/handlers.c --- expat-2.8.3/expat/tests/handlers.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/handlers.c 2026-10-05 15:43:59.000000000 +0000 @@ -63,6 +63,7 @@ #include "structdata.h" #include "common.h" #include "handlers.h" +#include "../lib/xcs.h" /* Global variables for user parameter settings tests */ /* Variable holding the expected handler userData */ @@ -95,15 +96,16 @@ const XML_Char **attr) { StructData *storage = (StructData *)userData; UNUSED_P(attr); - StructData_AddItem(storage, name, (int)XML_GetCurrentColumnNumber(g_parser), - (int)XML_GetCurrentLineNumber(g_parser), STRUCT_START_TAG); + StructData_AddItem(storage, name, (int)XML_GetCurrentColumnNumber64(g_parser), + (int)XML_GetCurrentLineNumber64(g_parser), + STRUCT_START_TAG); } void XMLCALL end_element_event_handler2(void *userData, const XML_Char *name) { StructData *storage = (StructData *)userData; - StructData_AddItem(storage, name, (int)XML_GetCurrentColumnNumber(g_parser), - (int)XML_GetCurrentLineNumber(g_parser), STRUCT_END_TAG); + StructData_AddItem(storage, name, (int)XML_GetCurrentColumnNumber64(g_parser), + (int)XML_GetCurrentLineNumber64(g_parser), STRUCT_END_TAG); } void XMLCALL @@ -116,7 +118,7 @@ int count, id, i; while (info->name != NULL) { - if (! xcstrcmp(name, info->name)) + if (! xcscmp(name, info->name)) break; info++; } @@ -138,14 +140,14 @@ fail("ID not present"); return; } - if (id != -1 && xcstrcmp(atts[id], info->id_name) != 0) { + if (id != -1 && xcscmp(atts[id], info->id_name) != 0) { fail("ID does not have the correct name"); return; } for (i = 0; i < info->attr_count + info->default_attr_count; i++) { attr = info->attributes; while (attr->name != NULL) { - if (! xcstrcmp(atts[0], attr->name)) + if (! xcscmp(atts[0], attr->name)) break; attr++; } @@ -153,7 +155,7 @@ fail("Attribute not recognised"); return; } - if (xcstrcmp(atts[1], attr->value) != 0) { + if (xcscmp(atts[1], attr->value) != 0) { fail("Attribute has wrong value"); return; } @@ -176,9 +178,9 @@ const XML_Char **atts) { UNUSED_P(userData); UNUSED_P(atts); - if (! xcstrcmp(name, XCS("suspend"))) + if (! xcscmp(name, XCS("suspend"))) XML_StopParser(g_parser, XML_TRUE); - if (! xcstrcmp(name, XCS("abort"))) + if (! xcscmp(name, XCS("abort"))) XML_StopParser(g_parser, XML_FALSE); } @@ -195,12 +197,12 @@ const XML_Char **atts) { XML_Char **elemstr = (XML_Char **)userData; char buffer[1024]; - if (xcstrcmp(elemstr[0], name) != 0) { + if (xcscmp(elemstr[0], name) != 0) { snprintf(buffer, sizeof(buffer), "unexpected start string: '%" XML_FMT_STR "'", name); fail(buffer); } - if (xcstrcmp(elemstr[1], atts[0]) != 0) { + if (xcscmp(elemstr[1], atts[0]) != 0) { snprintf(buffer, sizeof(buffer), "unexpected attribute string: '%" XML_FMT_STR "'", atts[0]); fail(buffer); @@ -215,7 +217,7 @@ void XMLCALL triplet_end_checker(void *userData, const XML_Char *name) { XML_Char **elemstr = (XML_Char **)userData; - if (xcstrcmp(elemstr[0], name) != 0) { + if (xcscmp(elemstr[0], name) != 0) { char buffer[1024]; snprintf(buffer, sizeof(buffer), "unexpected end string: '%" XML_FMT_STR "'", name); @@ -291,7 +293,7 @@ UnknownEncodingHandler(void *data, const XML_Char *encoding, XML_Encoding *info) { UNUSED_P(data); - if (xcstrcmp(encoding, XCS("unsupported-encoding")) == 0) { + if (xcscmp(encoding, XCS("unsupported-encoding")) == 0) { int i; for (i = 0; i < 256; ++i) info->map[i] = i; @@ -323,7 +325,7 @@ unknown_released_encoding_handler(void *data, const XML_Char *encoding, XML_Encoding *info) { UNUSED_P(data); - if (! xcstrcmp(encoding, XCS("unsupported-encoding"))) { + if (! xcscmp(encoding, XCS("unsupported-encoding"))) { int i; for (i = 0; i < 256; i++) @@ -359,12 +361,12 @@ int i; int high_map = -2; /* Assume a 2-byte sequence */ - if (! xcstrcmp(encoding, XCS("invalid-9")) - || ! xcstrcmp(encoding, XCS("ascii-like")) - || ! xcstrcmp(encoding, XCS("invalid-len")) - || ! xcstrcmp(encoding, XCS("invalid-a")) - || ! xcstrcmp(encoding, XCS("invalid-surrogate")) - || ! xcstrcmp(encoding, XCS("invalid-high"))) + if (! xcscmp(encoding, XCS("invalid-9")) + || ! xcscmp(encoding, XCS("ascii-like")) + || ! xcscmp(encoding, XCS("invalid-len")) + || ! xcscmp(encoding, XCS("invalid-a")) + || ! xcscmp(encoding, XCS("invalid-surrogate")) + || ! xcscmp(encoding, XCS("invalid-high"))) high_map = -1; for (i = 0; i < 128; ++i) @@ -373,28 +375,28 @@ info->map[i] = high_map; /* If required, put an invalid value in the ASCII entries */ - if (! xcstrcmp(encoding, XCS("invalid-9"))) + if (! xcscmp(encoding, XCS("invalid-9"))) info->map[9] = 5; /* If required, have a top-bit set character starts a 5-byte sequence */ - if (! xcstrcmp(encoding, XCS("invalid-len"))) + if (! xcscmp(encoding, XCS("invalid-len"))) info->map[0x81] = -5; /* If required, make a top-bit set character a valid ASCII character */ - if (! xcstrcmp(encoding, XCS("invalid-a"))) + if (! xcscmp(encoding, XCS("invalid-a"))) info->map[0x82] = 'a'; /* If required, give a top-bit set character a forbidden value, * what would otherwise be the first of a surrogate pair. */ - if (! xcstrcmp(encoding, XCS("invalid-surrogate"))) + if (! xcscmp(encoding, XCS("invalid-surrogate"))) info->map[0x83] = 0xd801; /* If required, give a top-bit set character too high a value */ - if (! xcstrcmp(encoding, XCS("invalid-high"))) + if (! xcscmp(encoding, XCS("invalid-high"))) info->map[0x84] = 0x010101; info->data = data; info->release = NULL; - if (! xcstrcmp(encoding, XCS("failing-conv"))) + if (! xcscmp(encoding, XCS("failing-conv"))) info->convert = failing_converter; - else if (! xcstrcmp(encoding, XCS("prefix-conv"))) + else if (! xcscmp(encoding, XCS("prefix-conv"))) info->convert = prefix_converter; else info->convert = NULL; @@ -437,7 +439,7 @@ UNUSED_P(base); UNUSED_P(publicId); while (options->parse_text != NULL) { - if (! xcstrcmp(systemId, options->system_id)) { + if (! xcscmp(systemId, options->system_id)) { enum XML_Status rc; ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) @@ -874,13 +876,13 @@ if (ext_parser == NULL) fail("Could not create external entity parser"); - if (! xcstrcmp(systemId, XCS("004-1.ent"))) { + if (! xcscmp(systemId, XCS("004-1.ent"))) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE) != XML_STATUS_ERROR) fail("Inner DTD with invalid tag not rejected"); if (XML_GetErrorCode(ext_parser) != XML_ERROR_EXTERNAL_ENTITY_HANDLING) xml_failure(ext_parser); - } else if (! xcstrcmp(systemId, XCS("004-2.ent"))) { + } else if (! xcscmp(systemId, XCS("004-2.ent"))) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE) != XML_STATUS_ERROR) fail("Invalid tag in external param not rejected"); @@ -984,11 +986,11 @@ ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); - if (! xcstrcmp(systemId, XCS("004-1.ent"))) { + if (! xcscmp(systemId, XCS("004-1.ent"))) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); - } else if (! xcstrcmp(systemId, XCS("004-2.ent"))) { + } else if (! xcscmp(systemId, XCS("004-2.ent"))) { ExtFaults *fault = XML_GetUserData(parser); enum XML_Status status; enum XML_Error error; @@ -1030,7 +1032,7 @@ ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); - if (! xcstrcmp(systemId, XCS("foo"))) { + if (! xcscmp(systemId, XCS("foo"))) { XML_SetNotStandaloneHandler(ext_parser, reject_not_standalone_handler); if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE) != XML_STATUS_ERROR) @@ -1040,7 +1042,7 @@ XML_SetNotStandaloneHandler(ext_parser, NULL); XML_ParserFree(ext_parser); return XML_STATUS_ERROR; - } else if (! xcstrcmp(systemId, XCS("bar"))) { + } else if (! xcscmp(systemId, XCS("bar"))) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); @@ -1068,12 +1070,12 @@ ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) fail("Could not create external entity parser"); - if (! xcstrcmp(systemId, XCS("004-1.ent"))) { + if (! xcscmp(systemId, XCS("004-1.ent"))) { if (_XML_Parse_SINGLE_BYTES(ext_parser, text1, (int)strlen(text1), XML_TRUE) == XML_STATUS_ERROR) xml_failure(ext_parser); } - if (! xcstrcmp(systemId, XCS("004-2.ent"))) { + if (! xcscmp(systemId, XCS("004-2.ent"))) { XML_SetXmlDeclHandler(ext_parser, entity_suspending_xdecl_handler); XML_SetUserData(ext_parser, ext_parser); if (_XML_Parse_SINGLE_BYTES(ext_parser, text2, (int)strlen(text2), XML_TRUE) @@ -1101,9 +1103,9 @@ ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) return XML_STATUS_ERROR; - if (systemId != NULL && ! xcstrcmp(systemId, XCS("http://example.org/"))) { + if (systemId != NULL && ! xcscmp(systemId, XCS("http://example.org/"))) { text = text1; - } else if (publicId != NULL && ! xcstrcmp(publicId, XCS("foo"))) { + } else if (publicId != NULL && ! xcscmp(publicId, XCS("foo"))) { text = text2; } else fail("Unexpected parameters to external entity parser"); @@ -1126,9 +1128,9 @@ UNUSED_P(base); UNUSED_P(publicId); - if (systemId == NULL || ! xcstrcmp(systemId, XCS("bar"))) + if (systemId == NULL || ! xcscmp(systemId, XCS("bar"))) return XML_STATUS_OK; - if (xcstrcmp(systemId, XCS("foo")) != 0) + if (xcscmp(systemId, XCS("foo")) != 0) fail("Unexpected system ID"); ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL); if (ext_parser == NULL) @@ -1527,9 +1529,9 @@ const struct AccountingTestCase *const testCase = XML_GetUserData(parser); const char *externalText = NULL; - if (xcstrcmp(systemId, XCS("first.ent")) == 0) { + if (xcscmp(systemId, XCS("first.ent")) == 0) { externalText = testCase->firstExternalText; - } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) { + } else if (xcscmp(systemId, XCS("second.ent")) == 0) { externalText = testCase->secondExternalText; } else { assert(! "systemId is neither \"first.ent\" nor \"second.ent\""); @@ -1569,16 +1571,16 @@ const XML_Char *default_value, int is_required) { AttTest *at = (AttTest *)userData; - if (xcstrcmp(element_name, at->element_name) != 0) + if (xcscmp(element_name, at->element_name) != 0) fail("Unexpected element name in attribute declaration"); - if (xcstrcmp(attr_name, at->attr_name) != 0) + if (xcscmp(attr_name, at->attr_name) != 0) fail("Unexpected attribute name in attribute declaration"); - if (xcstrcmp(attr_type, at->attr_type) != 0) + if (xcscmp(attr_type, at->attr_type) != 0) fail("Unexpected attribute type in attribute declaration"); if ((default_value == NULL && at->default_value != NULL) || (default_value != NULL && at->default_value == NULL) || (default_value != NULL - && xcstrcmp(default_value, at->default_value) != 0)) + && xcscmp(default_value, at->default_value) != 0)) fail("Unexpected default value in attribute declaration"); if (is_required != at->is_required) fail("Requirement mismatch in attribute declaration"); @@ -1649,23 +1651,24 @@ void XMLCALL byte_character_handler(void *userData, const XML_Char *s, int len) { #if XML_CONTEXT_BYTES > 0 - int offset, size; + int64_t offset; + uint64_t size; const char *buffer; ByteTestData *data = (ByteTestData *)userData; UNUSED_P(s); - buffer = XML_GetInputContext(g_parser, &offset, &size); + buffer = XML_GetInputContext64(g_parser, &offset, &size); if (buffer == NULL) fail("Failed to get context buffer"); if (offset != data->start_element_len) fail("Context offset in unexpected position"); if (len != data->cdata_len) fail("CDATA length reported incorrectly"); - if (size != data->total_string_len) + if (size != (uint64_t)data->total_string_len) fail("Context size is not full buffer"); - if (XML_GetCurrentByteIndex(g_parser) != offset) + if (XML_GetCurrentByteIndex64(g_parser) != offset) fail("Character byte index incorrect"); - if (XML_GetCurrentByteCount(g_parser) != len) + if (XML_GetCurrentByteCount64(g_parser) != (uint64_t)len) fail("Character byte count incorrect"); #else UNUSED_P(s); @@ -1723,7 +1726,7 @@ record_element_start_handler(void *userData, const XML_Char *name, const XML_Char **atts) { UNUSED_P(atts); - CharData_AppendXMLChars((CharData *)userData, name, (int)xcstrlen(name)); + CharData_AppendXMLChars((CharData *)userData, name, (int)xcslen(name)); } void XMLCALL @@ -1763,13 +1766,13 @@ || entity_value_to_match == NULL) { return; } - if (! xcstrcmp(entityName, entity_name_to_match)) { + if (! xcscmp(entityName, entity_name_to_match)) { /* The cast here is safe because we control the horizontal and * the vertical, and we therefore know our strings are never * going to overflow an int. */ - if (value_length != (int)xcstrlen(entity_value_to_match) - || xcstrncmp(value, entity_value_to_match, value_length) != 0) { + if (value_length != (int)xcslen(entity_value_to_match) + || xcsncmp(value, entity_value_to_match, value_length) != 0) { entity_match_flag = ENTITY_MATCH_FAIL; } else { entity_match_flag = ENTITY_MATCH_SUCCESS; diff -Nru expat-2.8.3/expat/tests/handlers.h expat-2.9.0/expat/tests/handlers.h --- expat-2.8.3/expat/tests/handlers.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/handlers.h 2026-10-05 15:43:59.000000000 +0000 @@ -45,16 +45,12 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_HANDLERS_H -# define XML_HANDLERS_H +#define XML_HANDLERS_H -# include "expat_config.h" +#include "expat_config.h" -# include "expat.h" +#include "expat.h" /* Variable holding the expected handler userData */ extern const void *g_handler_data; @@ -74,8 +70,8 @@ extern void XMLCALL end_element_event_handler(void *userData, const XML_Char *name); -# define STRUCT_START_TAG 0 -# define STRUCT_END_TAG 1 +#define STRUCT_START_TAG 0 +#define STRUCT_END_TAG 1 extern void XMLCALL start_element_event_handler2(void *userData, const XML_Char *name, @@ -516,22 +512,21 @@ _handler_record_get(const struct handler_record_list *storage, int index, const char *file, int line); -# define handler_record_get(storage, index) \ - _handler_record_get((storage), (index), __FILE__, __LINE__) +#define handler_record_get(storage, index) \ + _handler_record_get((storage), (index), __FILE__, __LINE__) -# define assert_record_handler_called(storage, index, expected_name, \ - expected_arg) \ - do { \ - const struct handler_record_entry *e \ - = handler_record_get(storage, index); \ - assert_true(strcmp(e->name, expected_name) == 0); \ - assert_true(e->arg == (expected_arg)); \ - } while (0) +#define assert_record_handler_called(storage, index, expected_name, \ + expected_arg) \ + do { \ + const struct handler_record_entry *e = handler_record_get(storage, index); \ + assert_true(strcmp(e->name, expected_name) == 0); \ + assert_true(e->arg == (expected_arg)); \ + } while (0) /* Entity Declaration Handlers */ -# define ENTITY_MATCH_FAIL (-1) -# define ENTITY_MATCH_NOT_FOUND (0) -# define ENTITY_MATCH_SUCCESS (1) +#define ENTITY_MATCH_FAIL (-1) +#define ENTITY_MATCH_NOT_FOUND (0) +#define ENTITY_MATCH_SUCCESS (1) extern void XMLCALL param_entity_match_handler( void *userData, const XML_Char *entityName, int is_parameter_entity, @@ -630,7 +625,3 @@ int len); #endif /* XML_HANDLERS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/hash_tests.c expat-2.9.0/expat/tests/hash_tests.c --- expat-2.8.3/expat/tests/hash_tests.c 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/tests/hash_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,157 @@ +/* Tests related to the hash tables used inside Expat +__ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#include "hash_tests.h" + +#include "common.h" // for XCS +#include "expat.h" +#include "hash_table.h" +#include "minicheck.h" + +#include +#include // for memcmp + +START_TEST(test_hash_table) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + typedef struct { + const XML_Char *name; + bool initialized; + } NAME_AND_FLAG; + + HASH_TABLE table; + XML_Parser parser = XML_ParserCreate(NULL); + hashTableInit(&table, parser); + + const XML_Char *const key1 = XCS("key1"); + const XML_Char *const key2 = XCS("key2"); + const XML_Char *const key3 = XCS("key1overlap"); + + // Self-test: `key3` starts with `key1` but is different from it + assert_true(memcmp(key1, key3, keylen(key1)) == 0); + assert_true(keyeq(key1, keylen(key1), key3) == XML_FALSE); + + // Test: Look up false for all keys because the table is empty + assert_true(lookup(parser, &table, key1, 0) == NULL); + assert_true(lookup(parser, &table, key2, 0) == NULL); + assert_true(lookup(parser, &table, key3, 0) == NULL); + + // Test: Iteration yields 0 items initially + { + HASH_TABLE_ITER iter1; + hashTableIterInit(&iter1, &table); + assert_true(hashTableIterNext(&iter1) == NULL); + } + + // Test: Insertion works (including initialization to zero) + NAME_AND_FLAG *const inserted1 + = (NAME_AND_FLAG *)lookup(parser, &table, key1, sizeof(NAME_AND_FLAG)); + assert_true(inserted1 != NULL); + assert_true(inserted1->name == key1); + assert_true(! inserted1->initialized); + + // Make it possible to tell the struct apart from a freshly inserted version + inserted1->initialized = true; + + // Test: Only present keys can be looked up + assert_true(lookup(parser, &table, key1, 0) != NULL); + assert_true(lookup(parser, &table, key2, 0) == NULL); + assert_true(lookup(parser, &table, key3, 0) == NULL); + + // Test: Key length works without false positives + assert_true(lookupWithLength(parser, &table, key3, /*nameLen=*/3, 0) == NULL); + assert_true(lookupWithLength(parser, &table, key3, /*nameLen=*/4, 0) != NULL); + assert_true(lookupWithLength(parser, &table, key3, /*nameLen=*/5, 0) == NULL); + + // TEST: Lookup does not reset existing entries to zeros + NAME_AND_FLAG *const found + = (NAME_AND_FLAG *)lookup(parser, &table, key1, sizeof(NAME_AND_FLAG)); + assert_true(found != NULL); + assert_true(found->name == key1); + assert_true(found->initialized); // this is key + + // Test: Insertion of a second item works + assert_true(lookup(parser, &table, key2, 0) == NULL); + assert_true(lookup(parser, &table, key2, sizeof(NAME_AND_FLAG)) != NULL); + assert_true(lookup(parser, &table, key2, 0) != NULL); + + // Test: Iteration yields nothing but the two expected items + { + HASH_TABLE_ITER iter2; + hashTableIterInit(&iter2, &table); + size_t itemCount = 0; + while (true) { + const NAME_AND_FLAG *const item + = (const NAME_AND_FLAG *)hashTableIterNext(&iter2); + if (item == NULL) + break; + + itemCount++; + + if (keyeq(key1, keylen(key1), item->name) == XML_TRUE) + assert_true(item->initialized); + else if (keyeq(key2, keylen(key2), item->name) == XML_TRUE) + assert_true(! item->initialized); + else + fail("unexpected item .name"); + } + assert_true(itemCount == 2); + } + + // Test: After clearing all lookups fail + hashTableClear(&table); + assert_true(lookup(parser, &table, key1, 0) == NULL); + assert_true(lookup(parser, &table, key2, 0) == NULL); + assert_true(lookup(parser, &table, key3, 0) == NULL); + + // Test: After clearing iteration yields 0 items again + { + HASH_TABLE_ITER iter3; + hashTableIterInit(&iter3, &table); + assert_true(hashTableIterNext(&iter3) == NULL); + } + + hashTableDestroy(&table); + XML_ParserFree(parser); +} +END_TEST + +void +make_hash_test_case(Suite *s) { + TCase *const tc_hash = tcase_create("hash tests"); + suite_add_tcase(s, tc_hash); + tcase_add_test(tc_hash, test_hash_table); +} diff -Nru expat-2.8.3/expat/tests/hash_tests.h expat-2.9.0/expat/tests/hash_tests.h --- expat-2.8.3/expat/tests/hash_tests.h 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/tests/hash_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,41 @@ +/* Tests related to the hash tables used inside Expat + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#ifndef XML_HASH_TESTS_H +#define XML_HASH_TESTS_H + +#include "minicheck.h" + +extern void make_hash_test_case(Suite *s); + +#endif /* XML_HASH_TESTS_H */ diff -Nru expat-2.8.3/expat/tests/memcheck.c expat-2.9.0/expat/tests/memcheck.c --- expat-2.8.3/expat/tests/memcheck.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/memcheck.c 2026-10-05 15:43:59.000000000 +0000 @@ -10,6 +10,7 @@ Copyright (c) 2017-2023 Sebastian Pipping Copyright (c) 2022 Sean McBride Copyright (c) 2026 Matthew Fernandez + Copyright (c) 2026 Leo Camus Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -117,7 +118,7 @@ if (entry->next != NULL) entry->next->prev = entry->prev; else - alloc_tail = entry->next; + alloc_tail = entry->prev; free(entry); } else { printf("Attempting to free unallocated memory at %p\n", ptr); diff -Nru expat-2.8.3/expat/tests/memcheck.h expat-2.9.0/expat/tests/memcheck.h --- expat-2.8.3/expat/tests/memcheck.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/memcheck.h 2026-10-05 15:43:59.000000000 +0000 @@ -7,8 +7,8 @@ \___/_/\_\ .__/ \__,_|\__| |_| XML parser - Copyright (c) 2017 Rhodri James - Copyright (c) 2017 Sebastian Pipping + Copyright (c) 2017 Rhodri James + Copyright (c) 2017-2026 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -33,10 +33,6 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_MEMCHECK_H # define XML_MEMCHECK_H 1 @@ -53,7 +49,3 @@ int tracking_report(void); #endif /* XML_MEMCHECK_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/minicheck.h expat-2.9.0/expat/tests/minicheck.h --- expat-2.8.3/expat/tests/minicheck.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/minicheck.h 2026-10-05 15:43:59.000000000 +0000 @@ -14,7 +14,7 @@ Copyright (c) 2004-2006 Fred L. Drake, Jr. Copyright (c) 2006-2012 Karl Waclawek - Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2022 Rhodri James Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow Licensed under the MIT license: @@ -41,27 +41,15 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_MINICHECK_H -# define XML_MINICHECK_H +#define XML_MINICHECK_H -# define CK_NOFORK 0 -# define CK_FORK 1 +#define CK_NOFORK 0 +#define CK_FORK 1 -# define CK_SILENT 0 -# define CK_NORMAL 1 -# define CK_VERBOSE 2 - -/* Workaround for Microsoft's compiler and Tru64 Unix systems where the - C compiler has a working __func__, but the C++ compiler only has a - working __FUNCTION__. This could be fixed in configure.in, but it's - not worth it right now. */ -# if defined(_MSC_VER) || (defined(__osf__) && defined(__cplusplus)) -# define __func__ __FUNCTION__ -# endif +#define CK_SILENT 0 +#define CK_NORMAL 1 +#define CK_VERBOSE 2 /* PRINTF_LIKE has two effects: 1. Make clang's -Wformat-nonliteral stop warning about non-literal format @@ -69,30 +57,30 @@ 2. Make both clang and gcc's -Wformat-nonliteral warn about *callers* of the annotated function that use a non-literal format string. */ -# if defined(__GNUC__) -# define PRINTF_LIKE(fmtpos, argspos) \ - __attribute__((format(printf, fmtpos, argspos))) -# else -# define PRINTF_LIKE(fmtpos, argspos) -# endif +#if defined(__GNUC__) +# define PRINTF_LIKE(fmtpos, argspos) \ + __attribute__((format(printf, fmtpos, argspos))) +#else +# define PRINTF_LIKE(fmtpos, argspos) +#endif -# define START_TEST(testname) \ - static void testname(void) { \ - _check_set_test_info(__func__, __FILE__, __LINE__); \ - { -# define END_TEST \ - } \ - } +#define START_TEST(testname) \ + static void testname(void) { \ + _check_set_test_info(__func__, __FILE__, __LINE__); \ + { +#define END_TEST \ + } \ + } void PRINTF_LIKE(1, 2) set_subtest(char const *fmt, ...); -# define fail(msg) _fail(__FILE__, __LINE__, msg) -# define assert_true(cond) \ - do { \ - if (! (cond)) { \ - _fail(__FILE__, __LINE__, "check failed: " #cond); \ - } \ - } while (0) +#define fail(msg) _fail(__FILE__, __LINE__, msg) +#define assert_true(cond) \ + do { \ + if (! (cond)) { \ + _fail(__FILE__, __LINE__, "check failed: " #cond); \ + } \ + } while (0) typedef void (*tcase_setup_function)(void); typedef void (*tcase_teardown_function)(void); @@ -131,11 +119,11 @@ * Prototypes for the actual implementation. */ -# if defined(__has_attribute) -# if __has_attribute(noreturn) +#if defined(__has_attribute) +# if __has_attribute(noreturn) __attribute__((noreturn)) -# endif # endif +#endif void _fail(const char *file, int line, const char *msg); Suite *suite_create(const char *name); TCase *tcase_create(const char *name); @@ -150,7 +138,3 @@ void srunner_free(SRunner *runner); #endif /* XML_MINICHECK_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/misc_tests.c expat-2.9.0/expat/tests/misc_tests.c --- expat-2.8.3/expat/tests/misc_tests.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/misc_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -23,6 +23,8 @@ Copyright (c) 2026 Matthew Fernandez Copyright (c) 2026 Kartik Kenchi Copyright (c) 2026 Evgeny Kotkov + Copyright (c) 2026 Darren Carreras + Copyright (c) 2026 Junki Lee Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -54,6 +56,8 @@ #include "expat_config.h" #include +#include +#include #include #include "expat.h" @@ -64,6 +68,7 @@ #include "ascii.h" /* for ASCII_xxx */ #include "handlers.h" #include "misc_tests.h" +#include "../lib/xcs.h" void XMLCALL accumulate_characters_ext_handler(void *userData, const XML_Char *s, int len); @@ -217,7 +222,7 @@ if (! versions_equal(&read_version, &parsed_version)) fail("Version mismatch"); - if (xcstrcmp(version_text, XCS("expat_2.8.3")) + if (xcscmp(version_text, XCS("expat_2.9.0")) != 0) /* needs bump on releases */ fail("XML_*_VERSION in expat.h out of sync?\n"); } @@ -393,8 +398,8 @@ XML_Parser parser; enum XML_Status parseResult; int setParamEntityResult; - XML_Size lineNumber; - XML_Size columnNumber; + uint64_t lineNumber; + uint64_t columnNumber; parser = XML_ParserCreate(NULL); setParamEntityResult @@ -443,13 +448,13 @@ if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) fail("Error code does not match XML_ERROR_INVALID_TOKEN"); - lineNumber = XML_GetCurrentLineNumber(parser); + lineNumber = XML_GetCurrentLineNumber64(parser); if (lineNumber != 6) - fail("XML_GetCurrentLineNumber does not work as expected."); + fail("XML_GetCurrentLineNumber64 does not work as expected."); - columnNumber = XML_GetCurrentColumnNumber(parser); + columnNumber = XML_GetCurrentColumnNumber64(parser); if (columnNumber != 0) - fail("XML_GetCurrentColumnNumber does not work as expected."); + fail("XML_GetCurrentColumnNumber64 does not work as expected."); XML_ParserFree(parser); } @@ -628,22 +633,23 @@ // Inspired by function XML_OriginalString of Perl's XML::Parser static char * dup_original_string(XML_Parser parser) { - const int byte_count = XML_GetCurrentByteCount(parser); + const uint64_t byte_count = XML_GetCurrentByteCount64(parser); - assert_true(byte_count >= 0); + int64_t offset64 = -1; + uint64_t size64 = UINT64_MAX; - int offset = -1; - int size = -1; - - const char *const context = XML_GetInputContext(parser, &offset, &size); + const char *const context64 + = XML_GetInputContext64(parser, &offset64, &size64); #if XML_CONTEXT_BYTES > 0 - assert_true(context != NULL); - assert_true(offset >= 0); - assert_true(size >= 0); - return portable_strndup(context + offset, byte_count); + assert_true(context64 != NULL); + assert_true(offset64 != -1); + assert_true(size64 != UINT64_MAX); + + return portable_strndup(context64 + offset64, byte_count); #else - assert_true(context == NULL); + UNUSED_P(byte_count); + assert_true(context64 == NULL); return NULL; #endif } @@ -708,8 +714,8 @@ const char *doc; enum XML_Status expectedStatusNoGE; enum XML_Error expectedErrorNoGE; - XML_Size expectedErrorLine; - XML_Size expectedErrorColumn; + uint64_t expectedErrorLine; + uint16_t expectedErrorColumn; }; const struct test_case cases[] = { // Opened by one entity, closed by another @@ -768,8 +774,9 @@ == expectedStatus); assert_true(XML_GetErrorCode(parser) == expectedError); #if XML_GE == 1 - assert_true(XML_GetCurrentLineNumber(parser) == testCase.expectedErrorLine); - assert_true(XML_GetCurrentColumnNumber(parser) + assert_true(XML_GetCurrentLineNumber64(parser) + == testCase.expectedErrorLine); + assert_true(XML_GetCurrentColumnNumber64(parser) == testCase.expectedErrorColumn); #endif XML_ParserFree(parser); @@ -839,6 +846,120 @@ } END_TEST +typedef struct { + XML_Parser parser; + int converterCallCount; + int releaseCallCount; +} EncodingCallbackData; + +static int XMLCALL +reentrant_encoding_converter(void *userData, const char *s) { + EncodingCallbackData *const data = userData; + UNUSED_P(s); + data->converterCallCount++; + forbidden_calls_character_handler(data->parser, NULL, 0); + return 'A'; +} + +static void XMLCALL +reentrant_encoding_release(void *userData) { + EncodingCallbackData *const data = userData; + data->releaseCallCount++; + forbidden_calls_character_handler(data->parser, NULL, 0); +} + +static int XMLCALL +reentrant_encoding_handler(void *userData, const XML_Char *name, + XML_Encoding *info) { + EncodingCallbackData *const data = userData; + UNUSED_P(name); + + for (int i = 0; i < 256; i++) + info->map[i] = i; + info->map[0x80] = -2; // Route byte 0x80 through the custom converter. + info->data = data; + info->convert = reentrant_encoding_converter; + info->release = reentrant_encoding_release; + return XML_STATUS_OK; +} + +START_TEST(test_misc_unknown_encoding_callbacks_protected) { + const char *const doc + = "\x80\x80"; + XML_Parser parser = XML_ParserCreate(NULL); + EncodingCallbackData data = {parser, 0, 0}; + XML_SetUnknownEncodingHandler(parser, reentrant_encoding_handler, &data); + + assert_true(XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_TRUE) + == XML_STATUS_OK); + assert_true(data.converterCallCount > 0); + assert_true(data.releaseCallCount == 0); // Released by XML_ParserFree below. + + XML_ParserFree(parser); + assert_true(data.releaseCallCount == 1); +} +END_TEST + +typedef struct { + XML_Parser parser; + int releaseCallCount; + uint64_t lineAtRelease; +} EncodingReleaseData; + +static void XMLCALL +position_reading_encoding_release(void *userData) { + EncodingReleaseData *const data = userData; + data->releaseCallCount++; + data->lineAtRelease = XML_GetCurrentLineNumber64(data->parser); +} + +static int XMLCALL +position_reading_encoding_handler(void *userData, const XML_Char *name, + XML_Encoding *info) { + UNUSED_P(name); + + for (int i = 0; i < 256; i++) + info->map[i] = i; + info->data = userData; + info->convert = NULL; + info->release = position_reading_encoding_release; + return XML_STATUS_OK; +} + +static void +run_unknown_encoding_release_getter_test(bool resetParser) { + const char *const doc = "\n" + "\n" + " \n" + "\n"; + XML_Parser parser = XML_ParserCreate(NULL); + EncodingReleaseData data = {parser, 0, 0}; + + assert_true(parser != NULL); + XML_SetUnknownEncodingHandler(parser, position_reading_encoding_handler, + &data); + assert_true(XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_TRUE) + == XML_STATUS_ERROR); + assert_true(XML_GetErrorCode(parser) == XML_ERROR_TAG_MISMATCH); + + if (resetParser) + assert_true(XML_ParserReset(parser, NULL) == XML_TRUE); + XML_ParserFree(parser); + + assert_true(data.releaseCallCount == 1); + assert_true(data.lineAtRelease == 4); +} + +START_TEST(test_misc_unknown_encoding_release_getter_parser_free) { + run_unknown_encoding_release_getter_test(false); +} +END_TEST + +START_TEST(test_misc_unknown_encoding_release_getter_parser_reset) { + run_unknown_encoding_release_getter_test(true); +} +END_TEST + // General attack payload idea by Jason Kratzer of Mozilla START_TEST(test_misc_low_surrogate_mozilla_bug_2053153) { const char doc_before[] = "<\0!\0D\0O\0C\0T\0Y\0P\0E\0 \0d\0 \0[\0\n\0" @@ -936,6 +1057,11 @@ tcase_add_test(tc_misc, test_misc_no_infinite_loop_issue_1161); tcase_add_test(tc_misc, test_misc_calls_forbidden_from_handlers); tcase_add_test(tc_misc, test_misc_resume_parser_forbidden_from_handler); + tcase_add_test(tc_misc, test_misc_unknown_encoding_callbacks_protected); + tcase_add_test(tc_misc, + test_misc_unknown_encoding_release_getter_parser_free); + tcase_add_test(tc_misc, + test_misc_unknown_encoding_release_getter_parser_reset); tcase_add_test(tc_misc, test_misc_input_2gb); tcase_add_test(tc_misc, test_misc_low_surrogate_mozilla_bug_2053153); } diff -Nru expat-2.8.3/expat/tests/misc_tests.h expat-2.9.0/expat/tests/misc_tests.h --- expat-2.8.3/expat/tests/misc_tests.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/misc_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,17 +42,9 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_MISC_TESTS_H -# define XML_MISC_TESTS_H +#define XML_MISC_TESTS_H extern void make_miscellaneous_test_case(Suite *s); #endif /* XML_MISC_TESTS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/ns_tests.h expat-2.9.0/expat/tests/ns_tests.h --- expat-2.8.3/expat/tests/ns_tests.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/ns_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,17 +42,9 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_NS_TESTS_H -# define XML_NS_TESTS_H +#define XML_NS_TESTS_H extern void make_namespace_test_case(Suite *s); #endif /* XML_NS_TESTS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/nsalloc_tests.c expat-2.9.0/expat/tests/nsalloc_tests.c --- expat-2.8.3/expat/tests/nsalloc_tests.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/nsalloc_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -43,6 +43,8 @@ SPDX-License-Identifier: MIT */ +#include "expat_config.h" + #if defined(NDEBUG) # undef NDEBUG /* because test suite relies on assert(...) at the moment */ #endif diff -Nru expat-2.8.3/expat/tests/nsalloc_tests.h expat-2.9.0/expat/tests/nsalloc_tests.h --- expat-2.8.3/expat/tests/nsalloc_tests.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/nsalloc_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -42,17 +42,9 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_NSALLOC_TESTS_H -# define XML_NSALLOC_TESTS_H +#define XML_NSALLOC_TESTS_H extern void make_nsalloc_test_case(Suite *s); #endif /* XML_NSALLOC_TESTS_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/props_tests.c expat-2.9.0/expat/tests/props_tests.c --- expat-2.8.3/expat/tests/props_tests.c 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/tests/props_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,790 @@ +/* Tests related to the public properties API of Expat +__ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#include "expat_config.h" + +#include "props_tests.h" + +#include "common.h" // for g_chunkSize +#include "expat.h" +#include "internal.h" // for e.g. EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT + +#include +#include + +enum ExpectedType { + TYPE_BOOL, + TYPE_DOUBLE, + TYPE_UINT64, +}; + +START_TEST(test_props_getter_defaults) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + +#if XML_GE == 1 + // Case XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD + { + uint64_t actionThresholdBytes1 = 123; + assert_true(XML_GetPropertyUInt64( + parser, XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, + &actionThresholdBytes1) + == XML_PROP_ERROR_NONE); + assert_true(actionThresholdBytes1 + == EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT); + } + + // Case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION + { + double maximumAmplification1 = 123.456; + assert_true(XML_GetPropertyDouble( + parser, XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, + &maximumAmplification1) + == XML_PROP_ERROR_NONE); + assert_true(maximumAmplification1 + == (double)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT); + } + + // Case XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD + { + uint64_t actionThresholdBytes2 = 123; + assert_true(XML_GetPropertyUInt64( + parser, XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, + &actionThresholdBytes2) + == XML_PROP_ERROR_NONE); + assert_true( + actionThresholdBytes2 + == EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT); + } + + // Case XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION + { + double maximumAmplification2 = 123.456; + assert_true(XML_GetPropertyDouble( + parser, XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, + &maximumAmplification2) + == XML_PROP_ERROR_NONE); + assert_true( + maximumAmplification2 + == (double) + EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT); + } +#endif + + // Case XML_PROP_REPARSE_DEFERRAL_ENABLED + XML_Bool reparseDeferralEnabled = XML_FALSE; + assert_true(XML_GetPropertyBool(parser, XML_PROP_REPARSE_DEFERRAL_ENABLED, + &reparseDeferralEnabled) + == XML_PROP_ERROR_NONE); + assert_true(reparseDeferralEnabled == g_reparseDeferralEnabledDefault); + + // Case XML_PROP_INVALID + XML_Bool dummyBool = XML_FALSE; + double dummyDouble = 123.456; + uint64_t dummyUInt64 = 123; + assert_true(XML_GetPropertyBool(parser, XML_PROP_INVALID, &dummyBool) + == XML_PROP_ERROR_INVALID_KEY); + assert_true(XML_GetPropertyDouble(parser, XML_PROP_INVALID, &dummyDouble) + == XML_PROP_ERROR_INVALID_KEY); + assert_true(XML_GetPropertyUInt64(parser, XML_PROP_INVALID, &dummyUInt64) + == XML_PROP_ERROR_INVALID_KEY); + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_getter_error_parser_null) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + XML_Parser parserNonNull = XML_ParserCreate(NULL); + XML_Parser parsers[] = {parserNonNull, NULL}; + assert_true(parserNonNull != NULL); + + for (size_t i = 0; i < sizeof(parsers) / sizeof(parsers[0]); i++) { + XML_Parser parser = parsers[i]; + const enum XML_Prop_Error validKeyExpectedError + = (parser == NULL) ? XML_PROP_ERROR_PARSER_NULL : XML_PROP_ERROR_NONE; + // NOTE: Currently the parser is checked for being `NULL` before the key is + // being checked for being valid. + // That precedence among errors is not considered part of the API + // contract: either error would be fine to return. + const enum XML_Prop_Error invalidKeyExpectedError + = (parser == NULL) ? XML_PROP_ERROR_PARSER_NULL + : XML_PROP_ERROR_INVALID_KEY; + + XML_Bool dummyBool = XML_FALSE; + double dummyDouble = 123.456; + uint64_t dummyUInt64 = 123; + +#if XML_GE == 1 + assert_true( + XML_GetPropertyUInt64( + parser, XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, &dummyUInt64) + == validKeyExpectedError); + assert_true( + XML_GetPropertyDouble( + parser, XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, &dummyDouble) + == validKeyExpectedError); + assert_true( + XML_GetPropertyUInt64( + parser, XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, &dummyUInt64) + == validKeyExpectedError); + assert_true( + XML_GetPropertyDouble( + parser, XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, &dummyDouble) + == validKeyExpectedError); +#endif + assert_true(XML_GetPropertyBool(parser, XML_PROP_REPARSE_DEFERRAL_ENABLED, + &dummyBool) + == validKeyExpectedError); + + assert_true(XML_GetPropertyBool(parser, XML_PROP_INVALID, &dummyBool) + == invalidKeyExpectedError); + assert_true(XML_GetPropertyDouble(parser, XML_PROP_INVALID, &dummyDouble) + == invalidKeyExpectedError); + assert_true(XML_GetPropertyUInt64(parser, XML_PROP_INVALID, &dummyUInt64) + == invalidKeyExpectedError); + } + + XML_ParserFree(parserNonNull); +} +END_TEST + +START_TEST(test_props_getter_error_invalid_key) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + XML_Bool dummyBool = XML_FALSE; + double dummyDouble = 123.456; + uint64_t dummyUInt64 = 123; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + assert_true(XML_GetPropertyBool(parser, XML_PROP_INVALID, &dummyBool) + == XML_PROP_ERROR_INVALID_KEY); + assert_true(XML_GetPropertyDouble(parser, XML_PROP_INVALID, &dummyDouble) + == XML_PROP_ERROR_INVALID_KEY); + assert_true(XML_GetPropertyUInt64(parser, XML_PROP_INVALID, &dummyUInt64) + == XML_PROP_ERROR_INVALID_KEY); + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_getter_error_invalid_type) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + struct TestCase { + enum XML_Parser_Property key; + enum ExpectedType expectedType; + }; + + struct TestCase cases[] = { +#if XML_GE == 1 + {XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, + {XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, +#endif + {XML_PROP_REPARSE_DEFERRAL_ENABLED, TYPE_BOOL}, + }; + + XML_Bool dummyBool = XML_FALSE; + double dummyDouble = 123.456; + uint64_t dummyUInt64 = 123; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + set_subtest("property %d", (int)testCase->key); + + const enum XML_Prop_Error expectedBoolError + = (testCase->expectedType == TYPE_BOOL) ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_TYPE; + const enum XML_Prop_Error expectedDoubleError + = (testCase->expectedType == TYPE_DOUBLE) ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_TYPE; + const enum XML_Prop_Error expectedUInt64Error + = (testCase->expectedType == TYPE_UINT64) ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_TYPE; + + assert_true(XML_GetPropertyBool(parser, testCase->key, &dummyBool) + == expectedBoolError); + assert_true(XML_GetPropertyDouble(parser, testCase->key, &dummyDouble) + == expectedDoubleError); + assert_true(XML_GetPropertyUInt64(parser, testCase->key, &dummyUInt64) + == expectedUInt64Error); + } + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_getter_error_invalid_value) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + enum XML_Parser_Property keys[] = { +#if XML_GE == 1 + XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, + XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, + XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, + XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, +#endif + XML_PROP_REPARSE_DEFERRAL_ENABLED, + }; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { + const enum XML_Parser_Property key = keys[i]; + set_subtest("property %d", (int)key); + + // NOTE: Currently the value output pointer is checked for being `NULL` + // before the key is being checked for being valid. + // That precedence among errors is not considered part of the API + // contract: either error would be fine to return. + assert_true(XML_GetPropertyBool(parser, key, NULL) + == XML_PROP_ERROR_INVALID_VALUE); + assert_true(XML_GetPropertyDouble(parser, key, NULL) + == XML_PROP_ERROR_INVALID_VALUE); + assert_true(XML_GetPropertyUInt64(parser, key, NULL) + == XML_PROP_ERROR_INVALID_VALUE); + } + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_setter_effective) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + struct TestCase { + enum XML_Parser_Property key; + enum ExpectedType expectedType; + }; + + struct TestCase cases[] = { +#if XML_GE == 1 + {XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, + {XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, +#endif + {XML_PROP_REPARSE_DEFERRAL_ENABLED, TYPE_BOOL}, + }; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + set_subtest("property %d", (int)testCase->key); + + switch (testCase->expectedType) { + case TYPE_BOOL: { + // Get original value + XML_Bool valueOne = ! g_reparseDeferralEnabledDefault; + assert_true(XML_GetPropertyBool(parser, testCase->key, &valueOne) + == XML_PROP_ERROR_NONE); + const XML_Bool valueTwo = ((valueOne == XML_TRUE) ? XML_FALSE : XML_TRUE); + assert_true(valueTwo != valueOne); // self-test + + // Test: Set with reported success + assert_true(XML_SetPropertyBool(parser, testCase->key, valueTwo) + == XML_PROP_ERROR_NONE); + XML_Bool valueThree = valueOne; + assert_true(valueThree != valueTwo); // self-test + + // Test: Whether the new value has become effective + assert_true(XML_GetPropertyBool(parser, testCase->key, &valueThree) + == XML_PROP_ERROR_NONE); + assert_true(valueThree == valueTwo); + break; + } + case TYPE_DOUBLE: { + // Get original value + double valueOne = 1.23; + assert_true(XML_GetPropertyDouble(parser, testCase->key, &valueOne) + == XML_PROP_ERROR_NONE); + const double valueTwo = 4.56; + assert_true(valueTwo != valueOne); // self-test + + // Test: Set with reported success + assert_true(XML_SetPropertyDouble(parser, testCase->key, valueTwo) + == XML_PROP_ERROR_NONE); + double valueThree = 7.89; + assert_true(valueThree != valueTwo); // self-test + + // Test: Whether the new value has become effective + assert_true(XML_GetPropertyDouble(parser, testCase->key, &valueThree) + == XML_PROP_ERROR_NONE); + assert_true((float)valueThree == (float)valueTwo); + break; + } + case TYPE_UINT64: { + // Get original value + uint64_t valueOne = 123; + assert_true(XML_GetPropertyUInt64(parser, testCase->key, &valueOne) + == XML_PROP_ERROR_NONE); + const uint64_t valueTwo = 456; + assert_true(valueTwo != valueOne); // self-test + + // Test: Set with reported success + assert_true(XML_SetPropertyUInt64(parser, testCase->key, valueTwo) + == XML_PROP_ERROR_NONE); + uint64_t valueThree = 789; + assert_true(valueThree != valueTwo); // self-test + + // Test: Whether the new value has become effective + assert_true(XML_GetPropertyUInt64(parser, testCase->key, &valueThree) + == XML_PROP_ERROR_NONE); + assert_true(valueThree == valueTwo); + break; + } + default: + fail("unsupported type"); + } + } + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_setter_error_parser_null) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + struct TestCase { + enum XML_Parser_Property key; + enum ExpectedType expectedType; + }; + + struct TestCase cases[] = { +#if XML_GE == 1 + {XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, + {XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, +#endif + {XML_PROP_REPARSE_DEFERRAL_ENABLED, TYPE_BOOL}, + }; + + XML_Parser parserNonNull = XML_ParserCreate(NULL); + XML_Parser parsers[] = {parserNonNull, NULL}; + assert_true(parserNonNull != NULL); + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + for (size_t j = 0; j < sizeof(parsers) / sizeof(parsers[0]); j++) { + XML_Parser parser = parsers[j]; + set_subtest("property %d, parser %s", (int)testCase->key, + (parser == NULL) ? "NULL" : "non-NULL"); + + const enum XML_Prop_Error expected + = ((parser == NULL) ? XML_PROP_ERROR_PARSER_NULL + : XML_PROP_ERROR_NONE); + + enum XML_Prop_Error actual + = XML_PROP_ERROR_INVALID_TYPE; // i.e. some value unequal to + // `expected` + // above + assert_true(actual != expected); // self-test + + switch (testCase->expectedType) { + case TYPE_BOOL: + actual = XML_SetPropertyBool(parser, testCase->key, + ! g_reparseDeferralEnabledDefault); + break; + case TYPE_DOUBLE: + actual = XML_SetPropertyDouble(parser, testCase->key, 456.789); + break; + case TYPE_UINT64: + actual = XML_SetPropertyUInt64(parser, testCase->key, 456); + break; + default: + fail("unsupported type"); + } + assert_true(actual == expected); + } + } + + XML_ParserFree(parserNonNull); +} +END_TEST + +START_TEST(test_props_setter_error_parser_not_root) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + struct TestCase { + enum XML_Parser_Property key; + enum ExpectedType expectedType; + bool needsRootParser; + }; + + struct TestCase cases[] = { +#if XML_GE == 1 + {XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, TYPE_UINT64, true}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE, true}, + {XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, TYPE_UINT64, true}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE, true}, +#endif + {XML_PROP_REPARSE_DEFERRAL_ENABLED, TYPE_BOOL, false}, + }; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + XML_Parser subParser = XML_ExternalEntityParserCreate(parser, NULL, NULL); +#if defined(XML_DTD) + assert_true(subParser != NULL); + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + set_subtest("property %d", (int)testCase->key); + + const enum XML_Prop_Error expected + = (testCase->needsRootParser ? XML_PROP_ERROR_PARSER_NOT_ROOT + : XML_PROP_ERROR_NONE); + + enum XML_Prop_Error actual + = XML_PROP_ERROR_INVALID_TYPE; // i.e. some value unequal to `expected` + // above + assert_true(actual != expected); // self-test + + switch (testCase->expectedType) { + case TYPE_BOOL: + actual = XML_SetPropertyBool(subParser, testCase->key, + ! g_reparseDeferralEnabledDefault); + break; + case TYPE_DOUBLE: + actual = XML_SetPropertyDouble(subParser, testCase->key, 456.789); + break; + case TYPE_UINT64: + actual = XML_SetPropertyUInt64(subParser, testCase->key, 456); + break; + default: + fail("unsupported type"); + } + + assert_true(actual == expected); + } + + XML_ParserFree(subParser); +#else // ! defined(XML_DTD) + assert_true(subParser == NULL); + UNUSED_P(cases); +#endif + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_setter_error_parser_started) { + // The test is not doing any (real) parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + struct TestCase { + enum XML_Parser_Property key; + enum ExpectedType expectedType; + }; + + struct TestCase cases[] = { +#if XML_GE == 1 + {XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, + {XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, +#endif + {XML_PROP_REPARSE_DEFERRAL_ENABLED, TYPE_BOOL}, + }; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + // Get the parser into state "parsing started" + XML_ParsingStatus parsingStatus; + XML_GetParsingStatus(parser, &parsingStatus); + assert_true(parsingStatus.parsing == XML_INITIALIZED); // Self-test + assert_true(_XML_Parse_SINGLE_BYTES(parser, "", 0, /*isFInal=*/XML_FALSE) + == XML_STATUS_OK); + XML_GetParsingStatus(parser, &parsingStatus); + assert_true(parsingStatus.parsing == XML_PARSING); + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + set_subtest("property %d", (int)testCase->key); + + switch (testCase->expectedType) { + case TYPE_BOOL: { + // Get original value + XML_Bool valueOne = ! g_reparseDeferralEnabledDefault; + assert_true(XML_GetPropertyBool(parser, testCase->key, &valueOne) + == XML_PROP_ERROR_NONE); + const XML_Bool valueTwo = ((valueOne == XML_TRUE) ? XML_FALSE : XML_TRUE); + assert_true(valueTwo != valueOne); // self-test + + // Test: Set with reported success + assert_true(XML_SetPropertyBool(parser, testCase->key, valueTwo) + == XML_PROP_ERROR_NONE); + XML_Bool valueThree = valueOne; + assert_true(valueThree != valueTwo); // self-test + + // Test: Whether the new value has become effective + assert_true(XML_GetPropertyBool(parser, testCase->key, &valueThree) + == XML_PROP_ERROR_NONE); + assert_true(valueThree == valueTwo); // i.e. new value applied + break; + } + case TYPE_DOUBLE: { + // Get original value + double valueOne = 1.23; + assert_true(XML_GetPropertyDouble(parser, testCase->key, &valueOne) + == XML_PROP_ERROR_NONE); + const double valueTwo = 4.56; + assert_true(valueTwo != valueOne); // self-test + + // Test: Set with reported success + assert_true(XML_SetPropertyDouble(parser, testCase->key, valueTwo) + == XML_PROP_ERROR_NONE); + double valueThree = 7.89; + assert_true(valueThree != valueTwo); // self-test + + // Test: Whether the new value has become effective + assert_true(XML_GetPropertyDouble(parser, testCase->key, &valueThree) + == XML_PROP_ERROR_NONE); + assert_true((float)valueThree + == (float)valueTwo); // i.e. new value applied + break; + } + case TYPE_UINT64: { + // Get original value + uint64_t valueOne = 123; + assert_true(XML_GetPropertyUInt64(parser, testCase->key, &valueOne) + == XML_PROP_ERROR_NONE); + const uint64_t valueTwo = 456; + assert_true(valueTwo != valueOne); // self-test + + // Test: Set with reported success + assert_true(XML_SetPropertyUInt64(parser, testCase->key, valueTwo) + == XML_PROP_ERROR_NONE); + uint64_t valueThree = 789; + assert_true(valueThree != valueTwo); // self-test + + // Test: Whether the new value has become effective + assert_true(XML_GetPropertyUInt64(parser, testCase->key, &valueThree) + == XML_PROP_ERROR_NONE); + assert_true(valueThree == valueTwo); // i.e. new value applied + break; + } + default: + fail("unsupported type"); + } + } + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_setter_error_invalid_key) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + assert_true(XML_SetPropertyBool(parser, XML_PROP_INVALID, + ! g_reparseDeferralEnabledDefault) + == XML_PROP_ERROR_INVALID_KEY); + assert_true(XML_SetPropertyDouble(parser, XML_PROP_INVALID, 123.456) + == XML_PROP_ERROR_INVALID_KEY); + assert_true(XML_SetPropertyUInt64(parser, XML_PROP_INVALID, 123) + == XML_PROP_ERROR_INVALID_KEY); + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_setter_error_invalid_type) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + struct TestCase { + enum XML_Parser_Property key; + enum ExpectedType expectedType; + }; + + struct TestCase cases[] = { +#if XML_GE == 1 + {XML_PROP_ALLOC_TRACKER_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, + {XML_PROP_BILLION_LAUGHS_ACTIVATION_THRESHOLD, TYPE_UINT64}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, TYPE_DOUBLE}, +#endif + {XML_PROP_REPARSE_DEFERRAL_ENABLED, TYPE_BOOL}, + }; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + set_subtest("property %d", (int)testCase->key); + + const enum XML_Prop_Error expectedErrorBool + = ((testCase->expectedType == TYPE_BOOL) ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_TYPE); + const enum XML_Prop_Error expectedErrorDouble + = ((testCase->expectedType == TYPE_DOUBLE) + ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_TYPE); + const enum XML_Prop_Error expectedErrorUInt64 + = ((testCase->expectedType == TYPE_UINT64) + ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_TYPE); + + assert_true(XML_SetPropertyBool(parser, testCase->key, + ! g_reparseDeferralEnabledDefault) + == expectedErrorBool); + assert_true(XML_SetPropertyDouble(parser, testCase->key, 456.789) + == expectedErrorDouble); + assert_true(XML_SetPropertyUInt64(parser, testCase->key, 456) + == expectedErrorUInt64); + } + + XML_ParserFree(parser); +} +END_TEST + +START_TEST(test_props_setter_error_invalid_value) { + // The test is not doing any parsing, so a single run + // (with `g_chunkSize == 0`) is enough + if (g_chunkSize != 0) + return; + + XML_Parser parser = XML_ParserCreate(NULL); + assert_true(parser != NULL); + +#if XML_GE == 1 + struct TestCase { + enum XML_Parser_Property key; + // NOTE: Neither `uint64_t`- nor `XML_Bool`-based properties have any + // invalid in-dimension values, so we are only testing `double`-based + // property values here. + double value; + bool valueValid; + }; + + struct TestCase cases[] = { + // XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, -HUGE_VAL, false}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, -1.0, false}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, -0.9, false}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, 0.0, false}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, 0.9, false}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, 1.0, true}, + {XML_PROP_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION, HUGE_VAL, true}, + // XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, -HUGE_VAL, false}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, -1.0, false}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, -0.9, false}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, 0.0, false}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, 0.9, false}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, 1.0, true}, + {XML_PROP_BILLION_LAUGHS_MAXIMUM_AMPLIFICATION, HUGE_VAL, true}, + }; + + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + struct TestCase *const testCase = cases + i; + const enum XML_Prop_Error expected + = (testCase->valueValid ? XML_PROP_ERROR_NONE + : XML_PROP_ERROR_INVALID_VALUE); + assert_true(XML_SetPropertyDouble(parser, testCase->key, testCase->value) + == expected); + } +#endif + + XML_ParserFree(parser); +} +END_TEST + +void +make_props_test_case(Suite *s) { + TCase *const tc_props = tcase_create("properties tests"); + suite_add_tcase(s, tc_props); + + tcase_add_test(tc_props, test_props_getter_defaults); + tcase_add_test(tc_props, test_props_getter_error_parser_null); + tcase_add_test(tc_props, test_props_getter_error_invalid_key); + tcase_add_test(tc_props, test_props_getter_error_invalid_type); + tcase_add_test(tc_props, test_props_getter_error_invalid_value); + + tcase_add_test(tc_props, test_props_setter_effective); + tcase_add_test(tc_props, test_props_setter_error_parser_null); + tcase_add_test(tc_props, test_props_setter_error_parser_not_root); + tcase_add_test(tc_props, test_props_setter_error_parser_started); + tcase_add_test(tc_props, test_props_setter_error_invalid_key); + tcase_add_test(tc_props, test_props_setter_error_invalid_type); + tcase_add_test(tc_props, test_props_setter_error_invalid_value); +} diff -Nru expat-2.8.3/expat/tests/props_tests.h expat-2.9.0/expat/tests/props_tests.h --- expat-2.8.3/expat/tests/props_tests.h 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/tests/props_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,41 @@ +/* Tests related to the public properties API of Expat + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#ifndef XML_PROPS_TESTS_H +#define XML_PROPS_TESTS_H + +#include "minicheck.h" + +extern void make_props_test_case(Suite *s); + +#endif /* XML_PROPS_TESTS_H */ diff -Nru expat-2.8.3/expat/tests/runtests.c expat-2.9.0/expat/tests/runtests.c --- expat-2.8.3/expat/tests/runtests.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/runtests.c 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2003 Greg Stein Copyright (c) 2005-2007 Steven Solie Copyright (c) 2005-2012 Karl Waclawek - Copyright (c) 2016-2023 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Joe Orton Copyright (c) 2017 José Gutiérrez de la Concha @@ -54,12 +54,15 @@ #include "minicheck.h" #include "common.h" +#include "acc_tests.h" +#include "alloc_tests.h" #include "basic_tests.h" -#include "ns_tests.h" +#include "hash_tests.h" #include "misc_tests.h" -#include "alloc_tests.h" +#include "ns_tests.h" #include "nsalloc_tests.h" -#include "acc_tests.h" +#include "props_tests.h" +#include "xcs_tests.h" XML_Parser g_parser = NULL; @@ -67,14 +70,17 @@ make_suite(void) { Suite *s = suite_create("basic"); - make_basic_test_case(s); - make_namespace_test_case(s); - make_miscellaneous_test_case(s); - make_alloc_test_case(s); - make_nsalloc_test_case(s); #if XML_GE == 1 make_accounting_test_case(s); #endif + make_alloc_test_case(s); + make_basic_test_case(s); + make_hash_test_case(s); + make_miscellaneous_test_case(s); + make_namespace_test_case(s); + make_nsalloc_test_case(s); + make_props_test_case(s); + make_xcs_test_case(s); return s; } diff -Nru expat-2.8.3/expat/tests/structdata.c expat-2.9.0/expat/tests/structdata.c --- expat-2.8.3/expat/tests/structdata.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/structdata.c 2026-10-05 15:43:59.000000000 +0000 @@ -7,7 +7,7 @@ |_| XML parser Copyright (c) 2017 Rhodri James - Copyright (c) 2017-2023 Sebastian Pipping + Copyright (c) 2017-2026 Sebastian Pipping Copyright (c) 2022 Sean McBride Copyright (c) 2026 Matthew Fernandez Licensed under the MIT license: @@ -47,23 +47,20 @@ #include "structdata.h" #include "minicheck.h" +#include "../lib/xcs.h" #define STRUCT_EXTENSION_COUNT 8 #ifdef XML_UNICODE_WCHAR_T # include # define XML_FMT_STR "ls" -# define xcstrlen(s) wcslen(s) -# define xcstrcmp(s, t) wcscmp((s), (t)) #else # define XML_FMT_STR "s" -# define xcstrlen(s) strlen(s) -# define xcstrcmp(s, t) strcmp((s), (t)) #endif static XML_Char * xmlstrdup(const XML_Char *s) { - size_t byte_count = (xcstrlen(s) + 1) * sizeof(XML_Char); + size_t byte_count = (xcslen(s) + 1) * sizeof(XML_Char); XML_Char *const dup = malloc(byte_count); assert(dup != NULL); @@ -128,7 +125,7 @@ assert(got != NULL); assert(want != NULL); - if (xcstrcmp(got->str, want->str) != 0) { + if (xcscmp(got->str, want->str) != 0) { StructData_Dispose(storage); fail("structure got bad string"); } else { diff -Nru expat-2.8.3/expat/tests/structdata.h expat-2.9.0/expat/tests/structdata.h --- expat-2.8.3/expat/tests/structdata.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/structdata.h 2026-10-05 15:43:59.000000000 +0000 @@ -8,6 +8,7 @@ |_| XML parser Copyright (c) 2017 Rhodri James + Copyright (c) 2026 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -32,10 +33,6 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - #ifndef XML_STRUCTDATA_H # define XML_STRUCTDATA_H 1 @@ -65,7 +62,3 @@ void StructData_Dispose(StructData *storage); #endif /* XML_STRUCTDATA_H */ - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/tests/xcs_tests.c expat-2.9.0/expat/tests/xcs_tests.c --- expat-2.8.3/expat/tests/xcs_tests.c 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/tests/xcs_tests.c 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,86 @@ +/* Tests related to the XCS (XML character string) functionality +__ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#include "xcs_tests.h" + +#include "ascii.h" +#include "common.h" // for g_chunkSize +#include "xcs.h" // for xcslen, xcscmp, xcsncmp + +static const XML_Char empty[] = {'\0'}; +static const XML_Char test[] = {ASCII_T, ASCII_E, ASCII_S, ASCII_T, '\0'}; +static const XML_Char testing[] + = {ASCII_T, ASCII_E, ASCII_S, ASCII_T, ASCII_I, ASCII_N, ASCII_G, '\0'}; + +START_TEST(test_xcs_len) { + if (g_chunkSize != 0) + return; + + assert_true(xcslen(empty) == 0); + assert_true(xcslen(test) == 4); +} +END_TEST + +START_TEST(test_xcs_cmp) { + if (g_chunkSize != 0) + return; + + assert_true(xcscmp(test, test) == 0); + assert_true(xcscmp(test, testing) < 0); + assert_true(xcscmp(testing, test) > 0); +} +END_TEST + +START_TEST(test_xcs_ncmp) { + if (g_chunkSize != 0) + return; + + assert_true(xcsncmp(test, test, 0) == 0); + assert_true(xcsncmp(test, test, 4) == 0); + + assert_true(xcsncmp(test, testing, 4) == 0); + assert_true(xcsncmp(testing, test, 4) == 0); + + assert_true(xcsncmp(test, testing, 5) < 0); + assert_true(xcsncmp(testing, test, 5) > 0); +} +END_TEST + +void +make_xcs_test_case(Suite *s) { + TCase *const tc_xcs = tcase_create("xcs tests"); + suite_add_tcase(s, tc_xcs); + tcase_add_test(tc_xcs, test_xcs_len); + tcase_add_test(tc_xcs, test_xcs_cmp); + tcase_add_test(tc_xcs, test_xcs_ncmp); +} diff -Nru expat-2.8.3/expat/tests/xcs_tests.h expat-2.9.0/expat/tests/xcs_tests.h --- expat-2.8.3/expat/tests/xcs_tests.h 1970-01-01 00:00:00.000000000 +0000 +++ expat-2.9.0/expat/tests/xcs_tests.h 2026-10-05 15:43:59.000000000 +0000 @@ -0,0 +1,41 @@ +/* Tests related to the XCS (XML character string) functionality +__ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2026 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#ifndef XML_XCS_TESTS_H +#define XML_XCS_TESTS_H + +#include "minicheck.h" + +extern void make_xcs_test_case(Suite *s); + +#endif /* XML_XCS_TESTS_H */ diff -Nru expat-2.8.3/expat/tests/xmltest.sh expat-2.9.0/expat/tests/xmltest.sh --- expat-2.8.3/expat/tests/xmltest.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/tests/xmltest.sh 2026-10-05 15:43:59.000000000 +0000 @@ -31,6 +31,7 @@ # Copyright (c) 2008-2019 Sebastian Pipping # Copyright (c) 2017 Rhodri James # Copyright (c) 2025 Hanno Böck +# Copyright (c) 2026 Huang Wenbin # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining @@ -172,3 +173,4 @@ echo "Passed: $SUCCESS" echo "Failed: $ERROR" +test "$ERROR" -eq 0 diff -Nru expat-2.8.3/expat/win32/expat.iss expat-2.9.0/expat/win32/expat.iss --- expat-2.8.3/expat/win32/expat.iss 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/win32/expat.iss 2026-10-05 15:43:59.000000000 +0000 @@ -38,7 +38,7 @@ ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE ; USE OR OTHER DEALINGS IN THE SOFTWARE. -#define expatVer "2.8.3" +#define expatVer "2.9.0" [Setup] AppName=Expat diff -Nru expat-2.8.3/expat/xmlwf/Makefile.am expat-2.9.0/expat/xmlwf/Makefile.am --- expat-2.8.3/expat/xmlwf/Makefile.am 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/Makefile.am 2026-10-05 15:43:59.000000000 +0000 @@ -6,7 +6,7 @@ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # -# Copyright (c) 2017-2021 Sebastian Pipping +# Copyright (c) 2017-2026 Sebastian Pipping # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining @@ -32,6 +32,7 @@ xmlwf_LDADD = ../lib/libexpat.la xmlwf_SOURCES = \ + ../lib/xcs.c \ xmlwf.c \ xmlfile.c \ codepage.c \ diff -Nru expat-2.8.3/expat/xmlwf/unixfilemap.c expat-2.9.0/expat/xmlwf/unixfilemap.c --- expat-2.8.3/expat/xmlwf/unixfilemap.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/unixfilemap.c 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2001-2002 Fred L. Drake, Jr. Copyright (c) 2006 Karl Waclawek - Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2026 Matthew Fernandez Licensed under the MIT license: @@ -37,6 +37,8 @@ SPDX-License-Identifier: MIT */ +#include "expat_config.h" // for off_t + #include #include #include diff -Nru expat-2.8.3/expat/xmlwf/xmlfile.c expat-2.9.0/expat/xmlwf/xmlfile.c --- expat-2.8.3/expat/xmlwf/xmlfile.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/xmlfile.c 2026-10-05 15:43:59.000000000 +0000 @@ -11,7 +11,7 @@ Copyright (c) 2002-2003 Fred L. Drake, Jr. Copyright (c) 2004-2006 Karl Waclawek Copyright (c) 2005-2007 Steven Solie - Copyright (c) 2016-2025 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 David Loffredo Copyright (c) 2021 Donghee Na @@ -45,6 +45,7 @@ #include "expat_config.h" +#include // PRIu64 #include #include #include @@ -98,11 +99,9 @@ enum XML_Error code = XML_GetErrorCode(parser); const XML_Char *message = XML_ErrorString(code); if (message) - ftprintf(stdout, - T("%s") T(":%") T(XML_FMT_INT_MOD) T("u") T(":%") - T(XML_FMT_INT_MOD) T("u") T(": %s\n"), - filename, XML_GetCurrentLineNumber(parser), - XML_GetCurrentColumnNumber(parser), message); + ftprintf(stdout, T("%s") T(":%") T(PRIu64) T(":%") T(PRIu64) T(": %s\n"), + filename, XML_GetCurrentLineNumber64(parser), + XML_GetCurrentColumnNumber64(parser), message); else ftprintf(stderr, T("%s: (unknown message %u)\n"), filename, (unsigned int)code); @@ -266,7 +265,7 @@ return ret; } -int +bool XML_ProcessFile(XML_Parser parser, const XML_Char *filename, unsigned flags) { int result; @@ -299,5 +298,5 @@ } } else result = processStream(filename, parser); - return result; + return (bool)result; } diff -Nru expat-2.8.3/expat/xmlwf/xmlfile.h expat-2.9.0/expat/xmlwf/xmlfile.h --- expat-2.8.3/expat/xmlwf/xmlfile.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/xmlfile.h 2026-10-05 15:43:59.000000000 +0000 @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Fred L. Drake, Jr. Copyright (c) 2005 Karl Waclawek - Copyright (c) 2016-2023 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -35,16 +35,14 @@ SPDX-License-Identifier: MIT */ +#include + +#include "expat.h" // for XML_Parser, XML_Char + #define XML_MAP_FILE 01 #define XML_EXTERNAL_ENTITIES 02 -#ifdef XML_LARGE_SIZE -# define XML_FMT_INT_MOD "ll" -#else -# define XML_FMT_INT_MOD "l" -#endif - extern int g_read_size_bytes; -extern int XML_ProcessFile(XML_Parser parser, const XML_Char *filename, - unsigned flags); +extern bool XML_ProcessFile(XML_Parser parser, const XML_Char *filename, + unsigned flags); diff -Nru expat-2.8.3/expat/xmlwf/xmlmime.h expat-2.9.0/expat/xmlwf/xmlmime.h --- expat-2.8.3/expat/xmlwf/xmlmime.h 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/xmlmime.h 2026-10-05 15:43:59.000000000 +0000 @@ -8,7 +8,7 @@ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd Copyright (c) 2002 Fred L. Drake, Jr. - Copyright (c) 2016-2017 Sebastian Pipping + Copyright (c) 2016-2026 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -33,10 +33,6 @@ SPDX-License-Identifier: MIT */ -#ifdef __cplusplus -extern "C" { -#endif - /* Registered charset names are at most 40 characters long. */ #define CHARSET_MAX 41 @@ -48,7 +44,3 @@ */ void getXMLCharset(const char *buf, char *charset); - -#ifdef __cplusplus -} -#endif diff -Nru expat-2.8.3/expat/xmlwf/xmlwf.c expat-2.9.0/expat/xmlwf/xmlwf.c --- expat-2.8.3/expat/xmlwf/xmlwf.c 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/xmlwf.c 2026-10-05 15:43:59.000000000 +0000 @@ -50,6 +50,7 @@ #include "expat_config.h" #include +#include // for PRIu64 #include #include #include @@ -72,7 +73,7 @@ # include #endif -#include "../lib/xcsinc.c" +#include "../lib/xcs.h" enum ExitCode { XMLWF_EXIT_SUCCESS = 0, @@ -360,23 +361,6 @@ } static int -xcscmp(const XML_Char *xs, const XML_Char *xt) { - while (*xs != 0 && *xt != 0) { - if (*xs < *xt) - return -1; - if (*xs > *xt) - return 1; - xs++; - xt++; - } - if (*xs < *xt) - return -1; - if (*xs > *xt) - return 1; - return 0; -} - -static int notationCmp(const void *a, const void *b) { const NotationList *const n1 = *(const NotationList *const *)a; const NotationList *const n2 = *(const NotationList *const *)b; @@ -587,12 +571,11 @@ puttc(T('"'), fp); } ftprintf(fp, - T(" byte=\"%") T(XML_FMT_INT_MOD) T("d\"") T(" nbytes=\"%d\"") - T(" line=\"%") T(XML_FMT_INT_MOD) T("u\"") T(" col=\"%") - T(XML_FMT_INT_MOD) T("u\""), - XML_GetCurrentByteIndex(parser), XML_GetCurrentByteCount(parser), - XML_GetCurrentLineNumber(parser), - XML_GetCurrentColumnNumber(parser)); + T(" byte=\"%") T(PRId64) T("\"") T(" nbytes=\"%") T(PRIu64) T("\"") + T(" line=\"%") T(PRIu64) T("\"") T(" col=\"%") T(PRIu64) T("\""), + XML_GetCurrentByteIndex64(parser), XML_GetCurrentByteCount64(parser), + XML_GetCurrentLineNumber64(parser), + XML_GetCurrentColumnNumber64(parser)); } static void @@ -933,7 +916,7 @@ T(" -w enable support for [W]indows code pages\n") T(" -r disable memory-mapping and use [r]ead calls instead\n") T(" -g BYTES buffer size to request per call pair to XML_[G]etBuffer and read (default: 8 KiB)\n") - T(" -k when processing multiple files, [k]eep processing after first file with error\n") + T(" -k when processing multiple files, [k]eep processing after first file with error, and make the last error determine the exit code.\n") T("\n") T("output control arguments:\n") T(" -d DIRECTORY output [d]estination directory\n") @@ -969,7 +952,7 @@ T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") T(" 1 could not allocate data structures, signals a serious problem with execution environment\n") T(" 2 one or more input files were not well-formed\n") - T(" 3 could not create an output file\n") + T(" 3 one or more output files could not be written\n") T(" 4 command-line argument error\n") T("\n") T("xmlwf of libexpat is software libre, licensed under the MIT license.\n") @@ -1194,7 +1177,6 @@ } for (; i < argc; i++) { XML_Char *outName = 0; - int result; XML_Parser parser; if (useNamespaces) parser = XML_ParserCreateNS(encoding, NSSEP); @@ -1350,19 +1332,29 @@ } if (windowsCodePages) XML_SetUnknownEncodingHandler(parser, unknownEncoding, 0); - result = XML_ProcessFile(parser, useStdin ? NULL : argv[i], processFlags); + const bool processingSuccess + = XML_ProcessFile(parser, useStdin ? NULL : argv[i], processFlags); + bool closingSuccess = true; if (outputDir) { if (outputType == 'm') metaEndDocument(parser); - fclose(userData.fp); - if (! result) { + closingSuccess = (fclose(userData.fp) == 0); + if (! closingSuccess) + tperror(outName); + if (! processingSuccess || ! closingSuccess) { tremove(outName); } free(outName); } XML_ParserFree(parser); - if (! result) { - exitCode = XMLWF_EXIT_NOT_WELLFORMED; + if (! processingSuccess || ! closingSuccess) { + // NOTE: If both failed, the last error should determine the exit code. + // Failure to close happened after failure to process. + if (! closingSuccess) + exitCode = XMLWF_EXIT_OUTPUT_ERROR; + else if (! processingSuccess) + exitCode = XMLWF_EXIT_NOT_WELLFORMED; + cleanupUserData(&userData); if (! continueOnError) { break; diff -Nru expat-2.8.3/expat/xmlwf/xmlwf_helpgen.py expat-2.9.0/expat/xmlwf/xmlwf_helpgen.py --- expat-2.8.3/expat/xmlwf/xmlwf_helpgen.py 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/xmlwf_helpgen.py 2026-10-05 15:43:59.000000000 +0000 @@ -50,7 +50,7 @@ 0 the input files are well-formed and the output (if requested) was written successfully 1 could not allocate data structures, signals a serious problem with execution environment 2 one or more input files were not well-formed - 3 could not create an output file + 3 one or more output files could not be written 4 command-line argument error xmlwf of libexpat is software libre, licensed under the MIT license. @@ -116,7 +116,10 @@ input_related.add_argument( "-k", action="store_true", - help="when processing multiple files, [k]eep processing after first file with error", + help=( + "when processing multiple files, [k]eep processing after first file with error" + ", and make the last error determine the exit code." + ), ) output_related = parser.add_argument_group("output control arguments") diff -Nru expat-2.8.3/expat/xmlwf/xmlwf_helpgen.sh expat-2.9.0/expat/xmlwf/xmlwf_helpgen.sh --- expat-2.8.3/expat/xmlwf/xmlwf_helpgen.sh 2026-08-10 21:49:33.000000000 +0000 +++ expat-2.9.0/expat/xmlwf/xmlwf_helpgen.sh 2026-10-05 15:43:59.000000000 +0000 @@ -6,7 +6,7 @@ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # -# Copyright (c) 2019-2021 Sebastian Pipping +# Copyright (c) 2019-2026 Sebastian Pipping # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining @@ -30,7 +30,11 @@ # # SPDX-License-Identifier: MIT -./xmlwf/xmlwf_helpgen.py | sed \ +set -e + +cd "$(dirname "$(type -P "$0")")" + +./xmlwf_helpgen.py | sed \ -e 's,usage: ,usage:,' \ -e 's, xmlwf, %s,' \ -e 's, \[-h | -v\],,' \