GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — test.test_xml_etree*.XMLPullParserTest.test_simple_xml fails with (system) expat 2.6.0 · Issue #115133 · python/cpython

test.test_xml_etree*.XMLPullParserTest.test_simple_xml fails with (system) expat 2.6.0 #115133

New issue
Copy link
New issue
Copy link
Closed
Closed
test.test_xml_etree*.XMLPullParserTest.test_simple_xml fails with (system) expat 2.6.0#115133
Copy link
Labels
topic-XMLtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@mgorny
mgorny
opened on Feb 7, 2024
Issue body actions

Bug report

Bug description:

Expat 2.6.0 was released yesterday, with CVE fixes. After upgrading the system library and building CPython --with-system-expat, I'm getting the following test failures:

======================================================================
FAIL: test_simple_xml (test.test_xml_etree.XMLPullParserTest.test_simple_xml) (chunk_size=1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1495, in test_simple_xml
    self.assert_event_tags(parser, [('end', 'element')])
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1480, in assert_event_tags
    self.assertEqual([(action, elem.tag) for action, elem in events],
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                     expected)
                     ^^^^^^^^^
AssertionError: Lists differ: [] != [('end', 'element')]

Second list contains 1 additional elements.
First extra element 0:
('end', 'element')

- []
+ [('end', 'element')]

======================================================================
FAIL: test_simple_xml (test.test_xml_etree.XMLPullParserTest.test_simple_xml) (chunk_size=5)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1498, in test_simple_xml
    self.assert_event_tags(parser, [
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
        ('end', 'element'),
        ^^^^^^^^^^^^^^^^^^^
        ('end', 'empty-element'),
        ^^^^^^^^^^^^^^^^^^^^^^^^^
        ])
        ^^
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1480, in assert_event_tags
    self.assertEqual([(action, elem.tag) for action, elem in events],
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                     expected)
                     ^^^^^^^^^
AssertionError: Lists differ: [('end', 'element')] != [('end', 'element'), ('end', 'empty-element')]

Second list contains 1 additional elements.
First extra element 1:
('end', 'empty-element')

- [('end', 'element')]
+ [('end', 'element'), ('end', 'empty-element')]

----------------------------------------------------------------------
======================================================================
FAIL: test_simple_xml (test.test_xml_etree_c.XMLPullParserTest.test_simple_xml) (chunk_size=1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1495, in test_simple_xml
    self.assert_event_tags(parser, [('end', 'element')])
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1480, in assert_event_tags
    self.assertEqual([(action, elem.tag) for action, elem in events],
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                     expected)
                     ^^^^^^^^^
AssertionError: Lists differ: [] != [('end', 'element')]

Second list contains 1 additional elements.
First extra element 0:
('end', 'element')

- []
+ [('end', 'element')]

======================================================================
FAIL: test_simple_xml (test.test_xml_etree_c.XMLPullParserTest.test_simple_xml) (chunk_size=5)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1498, in test_simple_xml
    self.assert_event_tags(parser, [
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
        ('end', 'element'),
        ^^^^^^^^^^^^^^^^^^^
        ('end', 'empty-element'),
        ^^^^^^^^^^^^^^^^^^^^^^^^^
        ])
        ^^
  File "/home/mgorny/git/cpython/Lib/test/test_xml_etree.py", line 1480, in assert_event_tags
    self.assertEqual([(action, elem.tag) for action, elem in events],
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                     expected)
                     ^^^^^^^^^
AssertionError: Lists differ: [('end', 'element')] != [('end', 'element'), ('end', 'empty-element')]

Second list contains 1 additional elements.
First extra element 1:
('end', 'empty-element')

- [('end', 'element')]
+ [('end', 'element'), ('end', 'empty-element')]

----------------------------------------------------------------------

I have reproduced with 3.11.8, 3.12.8 and main as of 2afc718, both using Gentoo ebuild and raw git repository. I've tested the latter like this:

./configure -C --with-system-expat
make -j12
./python -u -W default -bb -E -m test -vv test_xml_etree{,_c}

CC @hartwork

CPython versions tested on:

3.11, 3.12, CPython main branch

Operating systems tested on:

Linux

Linked PRs

  • gh-115133: test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral (fixes #115133) #115138
  • gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 #115164
  • [3.12] gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (GH-115164) #115288
  • [3.11] gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (GH-115164) #115289
  • [3.10] gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (GH-115164) #115525
  • [3.9] Fix tests for XMLPullParser with Expat 2.6.0 (GH-115133) #115535
  • [3.8] gh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (GH-115164 #115536
Reacted by Pascal Ernster, Sebastian Pipping, Holger Hoffstätte and Martin Weinelt

Activity

Next
  1. mgorny
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Feb 7, 2024
  2. AlexWaygood
    added
    topic-XML
    on Feb 7, 2024
  3. hardfalcon commented on Feb 7, 2024

    @hardfalcon
    hardfalcon
    on Feb 7, 2024
    More actions

    I've run into the exact same issue when trying to build/package Python 3.11.8 in a clean chroot on Archlinux with testing repos enabled (expat 2.6.0 is currently only available from the core-testing repo). The error did not occur anymore when I forced expat 2.5.0 (but I'd obviously prefer to avoid that because expat 2.6.0 is a security update).

  4. serhiy-storchaka commented on Feb 7, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on Feb 7, 2024
    Member
    More actions

    Note that tests are passed for chunk_size=None. I.e. it works as previously when feed the parser a whole line, but fails when split it on 1- or 5-character chunks.

    I wonder whether it was an intentional change (and what was the reason) or a bug in expat.

  5. hardfalcon commented on Feb 7, 2024

    @hardfalcon
    hardfalcon
    on Feb 7, 2024
    More actions

    @serhiy-storchaka: I think this might be the result of the fix for CVE-2023-52425:
    https://github.com/libexpat/libexpat/blob/R_2_6_0/expat/Changes#L7C22-L7C27

    Not sure if it's intentional, though.

  6. hartwork commented on Feb 7, 2024

    @hartwork
    hartwork
    on Feb 7, 2024
    Contributor
    More actions

    Just a quick note that libexpat upstream is aware by now. I'll get back to you for more soon-ish.

    Reacted by Pascal Ernster and Serhiy Storchaka
  7. hartwork commented on Feb 7, 2024

    @hartwork
    hartwork
    on Feb 7, 2024
    Contributor
    More actions

    I have created candidate pull request #115138 now for review. It fixes the test suite in CPython for Expat >=2.6.0.

  8. serhiy-storchaka commented on Feb 7, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on Feb 7, 2024
    Member
    More actions

    I would make the Expat buffering more context depending. For example, when the parser waits for the end of the long attribute, " in new input should set enough=1.

  9. Snild-Sony commented on Feb 7, 2024

    @Snild-Sony
    Snild-Sony
    on Feb 7, 2024
    More actions

    I wonder whether it was an intentional change (and what was the reason) or a bug in expat.

    Expat previously took quadratic time when parsing tokens that required multiple buffer fills (a.k.a. "feeds" in the Python code). Expat 2.6.0 introduces a mitigation for this that does exponential backoff when repeatedly failing to parse the same token due to not having enough data.

    This is why it doesn't affect the non-chunked case. It will also not affect the vast majority of usecases, since tokens do not usually require multiple feeds.

    While I don't believe Expat ever guaranteed immediate events, I recognize that it is a change in Expat's behavior in practice. However, I expect that the vast majority of apps will not depend on getting immediate feedback (as they won't know what their input is) -- and this was the least intrusive way I could think of to fix this DoS.

    when the parser waits for the end of the long attribute, " in new input should set enough=1.

    @hartwork and I have discussed something along those lines, but concluded that it would be unnecessarily complex, and possibly difficult to get 100% right.

    For example, if we're looking for the end of <hello , we want >. But we can't just force a reparse every time a > shows up in the input: attributex=">>>>>>>>>>> would trivially allow a bad XML file to bypass the mitigation. That means that we need to look at/remember more context (possibly from previous buffer fills), which AFAICT would require a whole lot of new infrastructure in Expat.


    If this behavior is absolutely unacceptable, it is possible (but not recommended) to disable the mitigation using XML_SetReparseDeferralEnabled(). It is even possible to change mid-document, e.g. if you want to try to flush what's in Expat's buffer, but still mostly benefit from the new attack mitigation.

  10. serhiy-storchaka commented on Feb 7, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on Feb 7, 2024
    Member
    More actions

    Does it need to restart parsing from <hello rather of at most ">>>? If it is so, then you have no other way with the current design. Ideally, Expact could be rewritten as a state machine that does not need reparsing, but it may be too much work.

    Could you at least only block reparsing for large enough data? I mean that there is a difference between reparsing 100 bytes and 1000000 bytes.

    Reducing the factor from 2 to 1.5 or 1.8 could help in some use cases. For example, if the stream consists of massages of approximately equal size N, and we feed the parser by chunks of approximately N bytes, then there is a large chance to block three messages in row (the first chunk is slightly smaller than the first message, and the second chunk is slightly smaller than the first chunk), but if the factor is less than 2, most likely only one message be blocked.

    Or maybe combine both approaches: have_now >= A * had_before - B.

  11. Snild-Sony commented on Feb 7, 2024

    @Snild-Sony
    Snild-Sony
    on Feb 7, 2024
    More actions

    Does it need to restart parsing from <hello rather of at most ">>>?

    Yes, that's what it does.

    Could you at least only block reparsing for large enough data?

    It's possible, but won't that just make the behavior harder to grasp for a user of the library?

    Also, since the cost depends on the feed size, it's hard to set a definite threshold. Even a 1000-byte token, fed into Expat 1 byte at a time, will incur an amplification of 500x.

    For example, if the stream consists of massages of approximately equal size N ...

    Yes, a smaller factor could help that case. But I'm not sure it's a good idea to try to hide this behavior from apps -- for those that don't really need the behavior, it's unnecessary, and for those that do need it, it's not a 100% guarantee.

    Do note that the whole first chunk would need to consist of a single token (essentially one big tag or comment) for this example to trigger the deferral logic, since consuming any bytes will reset the heuristic.

  12. gentoo-bot
    added a commit that references this issue on Feb 7, 2024

    dev-lang/python: add link to expat bug

    9ee56bf
  13. serhiy-storchaka
    added a commit that references this issue on Feb 8, 2024

    pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0

    e27eafb
  14. serhiy-storchaka commented on Feb 8, 2024

    @serhiy-storchaka
    serhiy-storchaka
    on Feb 8, 2024
    Member
    More actions

    #115164 keeps testing with small chunks, so we can test that our code does not introduce additional buffering, but makes some failures tolerable with Expat 2.6.0.

    Am I correct that the test with chunk_size=8 is passed with Expat 2.6.0?

  15. lazka
    added a commit that references this issue on Feb 10, 2024

    pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0

    db24c80
  16. 44 remaining items

  17. mcepl
    added 3 commits that reference this issue on May 10, 2024

    test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral

    86b1431

    Fix etree XMLPullParser tests for Expat >=2.6.0 with reparse deferral

    d7133c7

    test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral

    6b33f55
  18. naveen521kk
    added 3 commits that reference this issue on Jul 11, 2024

    [3.12] pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (p…

    e6aeef3

    [3.12] pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (p…

    76ea190

    [3.12] pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (p…

    95790f9
  19. naveen521kk
    added a commit that references this issue on Aug 5, 2024

    [3.12] pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0 (p…

    ad679e5
  20. mcepl
    added a commit that references this issue on Nov 1, 2024

    test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral

    245a008
  21. mcepl
    added 3 commits that reference this issue on Dec 2, 2024

    test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral

    79271b4

    test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral

    e089e28

    test_xml_etree.py: Fix for Expat >=2.6.0 with reparse deferral

    e6b1bc8
  22. hrnciar
    added a commit that references this issue on Apr 23, 2025

    00422: pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0

    a293fda
  23. hroncok
    added a commit that references this issue on Jul 4, 2025

    00422: pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0

    b4445c4
  24. frenzymadness
    added a commit that references this issue on Aug 12, 2025

    00422: pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0

    4667cf4
  25. hroncok
    added a commit that references this issue on Feb 3, 2026

    00422: pythongh-115133: Fix tests for XMLPullParser with Expat 2.6.0

    585c406
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic-XMLtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.