Skip to content

fix infinite loop parsing the Forwarded header - #13229

Draft
arshsmith1 wants to merge 3 commits into
aio-libs:masterfrom
arshsmith1:forwarded-parse-infinite-loop
Draft

fix infinite loop parsing the Forwarded header#13229
arshsmith1 wants to merge 3 commits into
aio-libs:masterfrom
arshsmith1:forwarded-parse-infinite-loop

Conversation

@arshsmith1

Copy link
Copy Markdown
Contributor

What do these changes do?

BaseRequest.forwarded walks each Forwarded header with a hand-written cursor loop. When a segment neither matches _FORWARDED_PAIR_RE nor has a following ;, field_value.find(";", pos) returns -1, so the empty-value branch sets pos = -1 + 1 = 0 and the cursor jumps back to the start. The loop then never makes progress and spins at 100% CPU on the event loop thread. A single header such as Forwarded: a (also for=1.2.3.4; a, for=_; x, ; a) is enough to hang the worker permanently. Reading request.forwarded is common in reverse-proxy setups, so any middleware or handler that touches it exposes this.

The loop now breaks when there is no further ; to consume, since a trailing empty-or-malformed value ends the field-value either way. That also drops the off-by-one where the old [pos : field_value.find(";", pos)] slice quietly cut the last character when no ; was present.

Are there changes in behavior for the user?

No API change. Well-formed and previously-terminating headers parse exactly as before; the only difference is that the pathological inputs now terminate and return the valid prefix instead of hanging.

Is it a substantial burden for the maintainers to support this?

No. It touches the one parse loop and adds a parametrized regression test next to the existing Forwarded tests.

Related issue number

None.

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes — N/A, no public API change
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
  • Add a new news fragment into the CHANGES/ folder

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.99%. Comparing base (76bb64e) to head (209a74b).
⚠️ Report is 77 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #13229      +/-   ##
==========================================
+ Coverage   98.98%   98.99%   +0.01%     
==========================================
  Files         131      132       +1     
  Lines       48993    49296     +303     
  Branches     2550     2567      +17     
==========================================
+ Hits        48496    48801     +305     
+ Misses        373      371       -2     
  Partials      124      124              
Flag Coverage Δ
Autobahn 22.07% <25.00%> (-0.06%) ⬇️
CI-GHA 98.90% <100.00%> (+<0.01%) ⬆️
OS-Linux 98.68% <100.00%> (+<0.01%) ⬆️
OS-Windows 97.03% <100.00%> (+<0.01%) ⬆️
OS-macOS 97.93% <100.00%> (-0.01%) ⬇️
Py-3.10 98.12% <100.00%> (+<0.01%) ⬆️
Py-3.11 98.38% <100.00%> (+<0.01%) ⬆️
Py-3.12 98.47% <100.00%> (+<0.01%) ⬆️
Py-3.13 98.45% <100.00%> (+<0.01%) ⬆️
Py-3.14 98.47% <100.00%> (+0.01%) ⬆️
Py-3.14t 97.56% <100.00%> (+<0.01%) ⬆️
Py-pypy-3.11 97.40% <100.00%> (+0.01%) ⬆️
VM-macos 97.93% <100.00%> (-0.01%) ⬇️
VM-ubuntu 98.68% <100.00%> (+<0.01%) ⬆️
VM-windows 97.03% <100.00%> (+<0.01%) ⬆️
cython-coverage 38.12% <0.00%> (+0.18%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@codspeed-hq

codspeed-hq Bot commented Jul 23, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 84 untouched benchmarks
⏩ 83 skipped benchmarks1


Comparing arshsmith1:forwarded-parse-infinite-loop (209a74b) with master (d8b943b)

Open in CodSpeed

Footnotes

  1. 83 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@arshsmith1
arshsmith1 force-pushed the forwarded-parse-infinite-loop branch from 8b5fd88 to 7cbe0aa Compare July 23, 2026 08:50
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Jul 23, 2026
Comment thread aiohttp/web_request.py Outdated
Comment thread tests/test_web_request.py Outdated
Co-authored-by: Sam Bull <aa6bs0@sambull.org>
@Dreamsorcerer Dreamsorcerer added backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot backport-3.15 Trigger automatic backporting to the 3.15 release branch by Patchback robot labels Aug 3, 2026
Comment thread aiohttp/web_request.py
pos = semi + 1
else:
# bad syntax here, skip to next field value
break

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test covers this line now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, the new break stole the inputs that used to land here. Added a "bad; for=_real" case to the parametrized test so the bad-syntax break is exercised again.

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

Labels

backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot backport-3.15 Trigger automatic backporting to the 3.15 release branch by Patchback robot bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants