Skip to content

werkzeug misdecodes multipart line breaks if 64kb chunk ends in the middle of final CR+LF #3065

Description

@emmanuelthome

The following minimalist flask application and upload code causes a wrongly decoded multipart payload to be saved. The application file upload.py and test case testcase are in the attached zip file reproducer.zip.

  • run application: mkdir upload ; python3 -m flask --app upload.py run -p 32269
  • upload the test case (from a separate shell, of course): netcat 127.0.0.1 32269 < testcase

Behold the fact that the testcase ends in:

000100f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00010100: 0000 0000 0000 000d 0a2d 2d30 3633 6166  .........--063af
00010110: 6334 6362 6534 3661 3734 3035 3930 3938  c4cbe46a74059098
00010120: 3831 3433 3937 3165 3262 642d 2d0d 0a    8143971e2bd--..

whereby the payload should end with a bunch of zeros.

What happens is that the downloaded file has a stray \x0d at the end, leading to a file of length 65432 bytes, while the correct length should be 65431 bytes:

0000ff80: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000ff90: 0000 0000 0000 000d                      ........

My understanding of the cause is that the code here splits the input in chunks of size 65536. Its state machine is updated by what it sees. If the chunk is broken in the middle of the final line break (which is precisely what happens here), the quite tolerant regexp here will recognize a single \n as a legitimate line break marker, and thus let \r be part of the payload.

Environment:

>>> import werkzeug
>>> import sys
>>> import importlib
>>> from importlib.metadata import version
>>> sys.version
'3.13.9 (main, Oct 15 2025, 14:56:22) [GCC 15.2.0]'
>>> version('werkzeug')
'3.1.3'

This bug was observed in the field with the cado-nfs software and nailed down by @akruppa.

Activity

  1. davidism commented on Nov 13, 2025

    @davidism
    Member

    Happy to review a PR

  2. davidism commented on Nov 13, 2025

    @davidism
    Member

    However, that other project needs to stop using the dev server in production. See https://flask.palletsprojects.com/en/stable/deploying/

    When you’re developing locally, you’re probably using the built-in development server, debugger, and reloader. These should not be used in production. Instead, you should use a dedicated WSGI server or hosting platform, some of which will be described here.

    “Production” means “not development”, which applies whether you’re serving your application publicly to millions of users or privately / locally to a single user.

  3. added 2 commits that reference this issue on Nov 13, 2025
    ca0e26f
    0781bee
  4. emmanuelthome commented on Nov 13, 2025

    @emmanuelthome
    ContributorAuthor

    However, that other project needs to stop using the dev server in production.

    Yes, we're aware of it. It's in the works. Thanks for the heads up, though.

  5. emmanuelthome commented on Nov 13, 2025

    @emmanuelthome
    ContributorAuthor

    Happy to review a PR

    Here's an attempt with a test. But frankly, it's not my territory. You might prefer an option that refactors some of the existing logic (e.g. around the incomplete boundary detection) in order to also address this issue. Your call.

  6. added this to the 3.1.4 milestone on Nov 27, 2025
  7. locked as resolved and limited conversation to collaborators on Dec 12, 2025
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions