Skip to content

fix: a worker sees a multipart body cut short as PHP sees it - #66

Merged
kwhorne merged 1 commit into
mainfrom
fix/upload-err-partial
Oct 26, 2026
Merged

kwhorne merged 1 commit into
mainfrom
fix/upload-err-partial

Conversation

@kwhorne

@kwhorne kwhorne commented Oct 26, 2026

Copy link
Copy Markdown
Owner

In worker mode a multipart body that ended before its closing boundary was a 400 and the app never ran. PHP never refuses such a body:

  • a file it ends in is UPLOAD_ERR_PARTIAL (3): no temp file, no size, no type
  • a field it ends in keeps what arrived (short of a tail that could still have become the next boundary)
  • the parts before it are kept

How: once the body has ended, Askr appends the start of one more part, named for this request alone. Whatever part the body ended in then ends as a part does, and that extra part is the sign the body stopped inside the last one. The size limit moved into the same wrapper, so the appended tail is not counted against it. A body that stops making sense part-way now leaves the parts before that point instead of a 400. A body that cannot be read (client gone) is still refused.

Tests

  • e2e a_worker_sees_a_body_cut_short_as_php_sees_it: 15 cut-short bodies, each compared with per-request PHP as the oracle. It fails on main with a 400.
  • Unit tests in upload.rs cover chunk sizes 1/3/7/4096, PHP's tail trimming, cuts between parts and in headers, a read error, and post_max_size to the byte. 11 mutations were tried and every one is caught.
  • scripts/phpt-uploads.py: rfc1867_missing_boundary_2 passes in worker mode and is gone from phpt-uploads.known (worker 9 pass, 7 known).

A body that ended before its closing boundary was a 400 in worker mode, and
the app never ran. PHP's parser never refuses a body: a file the body ends in
is UPLOAD_ERR_PARTIAL, a field it ends in keeps what arrived (short of a tail
that could still have become the next boundary), and the parts before are kept.

The body now gets the start of one more part appended once it has ended, so
the part it ended in ends as a part does, and the parser shows where the body
stopped. The body limit moves into that wrapper so the tail is not counted.
A body that stops making sense part-way leaves the parts before that point
instead of a 400; a body that cannot be read is still refused.

php-src's rfc1867_missing_boundary_2 now passes in worker mode.
@kwhorne
kwhorne merged commit 0a4a511 into main Oct 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant