Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FileResponse has undefined behavior if the file is changed out from under it between the stat and open calls #8013

Closed
1 task done
bdraco opened this issue Jan 9, 2024 · 5 comments · Fixed by #10101
Closed
1 task done
Assignees
Labels

Comments

@bdraco
Copy link
Member

bdraco commented Jan 9, 2024

This was discovered while fixing another issue in #8012 (comment)

This is a rare case so its not likely we need to prioritize fixing it but I'm sure this race has bitten someone with random unexplained failures who uses aiohttp to serve index or signature files that get changed out frequently.

Describe the bug

We can fix this by opening the file first, doing fstat to get the stat

To Reproduce

replace the file on the FS between the stat and open call

Expected behavior

The handler should still send the original file

Logs/tracebacks

n/a

Python Version

$ python --version

aiohttp Version

$ python -m pip show aiohttp

multidict Version

$ python -m pip show multidict

yarl Version

$ python -m pip show yarl

OS

n/a

Related component

Server

Additional context

n/a

Code of Conduct

  • I agree to follow the aio-libs Code of Conduct
@bdraco bdraco added the bug label Jan 9, 2024
@bdraco bdraco self-assigned this Jan 9, 2024
bdraco added a commit that referenced this issue Dec 3, 2024
We didn't have any benchmarks for file responses. From the benchmarks it
turns out most of the time is creating and processing the executor jobs.

If we combine the stat into a job that returns the open fileobj it will
likely be faster and solve
#8013
patchback bot pushed a commit that referenced this issue Dec 3, 2024
We didn't have any benchmarks for file responses. From the benchmarks it
turns out most of the time is creating and processing the executor jobs.

If we combine the stat into a job that returns the open fileobj it will
likely be faster and solve
#8013

(cherry picked from commit fcce1bf)
patchback bot pushed a commit that referenced this issue Dec 3, 2024
We didn't have any benchmarks for file responses. From the benchmarks it
turns out most of the time is creating and processing the executor jobs.

If we combine the stat into a job that returns the open fileobj it will
likely be faster and solve
#8013

(cherry picked from commit fcce1bf)
@bdraco
Copy link
Member Author

bdraco commented Dec 3, 2024

It looks like this is worth fixing as it will also give a nice performance boost by reducing one executor job per request #10095

@steverep
Copy link
Member

steverep commented Dec 3, 2024

It looks like this is worth fixing as it will also give a nice performance boost by reducing one executor job per request #10095

👍🏻 but I'd add a benchmark for a 304 response as well. Those would end up with a penalty from any overhead opening the file and the extra executor job to close it (4xx responses would also be penalized but less a concern I guess).

bdraco added a commit that referenced this issue Dec 4, 2024
…nse (#10098)

**This is a backport of PR #10095 as merged into master
(fcce1bf).**

We didn't have any benchmarks for file responses. From the benchmarks it
turns out most of the time is creating and processing the executor jobs.

If we combine the stat into a job that returns the open fileobj it will
likely be faster and solve
#8013

Co-authored-by: J. Nick Koston <[email protected]>
bdraco added a commit that referenced this issue Dec 4, 2024
…nse (#10097)

**This is a backport of PR #10095 as merged into master
(fcce1bf).**

We didn't have any benchmarks for file responses. From the benchmarks it
turns out most of the time is creating and processing the executor jobs.

If we combine the stat into a job that returns the open fileobj it will
likely be faster and solve
#8013

Co-authored-by: J. Nick Koston <[email protected]>
bdraco added a commit that referenced this issue Dec 4, 2024
…d open calls

There was a race in ``FileResponse`` where the stat would be incorrect
if the file was changed out between the `stat` and `open` syscalls.
This would lead to various unexpected behaviors such as trying to read
beyond the length of the file or sending a partial file. This problem
is likely to occour when files are being renamed/linked into place.

An example of how this can happen with a system that provides weather
data every 60s:

An external process writes `.weather.txt` at the top of
each minute, and than renames it to `weather.txt`. In this
case `aiohttp` may stat the old `weather.txt`, and than
open the new `weather.txt`, and use the `stat` result from
the original file.

To fix this we now `fstat` the open file on operating systems
where `fstat` is available

fixes #8013
@bdraco
Copy link
Member Author

bdraco commented Dec 4, 2024

It looks like this is worth fixing as it will also give a nice performance boost by reducing one executor job per request #10095

👍🏻 but I'd add a benchmark for a 304 response as well. Those would end up with a penalty from any overhead opening the file and the extra executor job to close it (4xx responses would also be penalized but less a concern I guess).

I kept that in mind when I wrote the fix. The number of executor jobs remains the same for both a success and 304 response (not considering the close one which is no change). If you would like to add a benchmark for 304s as well in a fresh PR, that would be a nice addition to ensure we don't regress it in the future.

@steverep
Copy link
Member

steverep commented Dec 4, 2024

The fix certainly handles the closings smartly, but I would be more worried about the additional overhead of the file openings. If cache is controlled well, many implementations are likely serving 304s much more than 200s. #10101 probably introduced a minor penalty there, but I don't see a better way to fix this issue. 🤷🏻‍♂️

@bdraco
Copy link
Member Author

bdraco commented Dec 5, 2024

It looks like this is worth fixing as it will also give a nice performance boost by reducing one executor job per request #10095

👍🏻 but I'd add a benchmark for a 304 response as well. Those would end up with a penalty from any overhead opening the file and the extra executor job to close it (4xx responses would also be penalized but less a concern I guess).

@steverep
I went ahead and wrote it in #10114 as its needed to prove out #10113

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants