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

Puma 6.0.0 does not close the response bodies of hijacked requests #2999

Closed
aymeric-ledorze opened this issue Oct 25, 2022 · 7 comments · Fixed by #3002
Closed

Puma 6.0.0 does not close the response bodies of hijacked requests #2999

aymeric-ledorze opened this issue Oct 25, 2022 · 7 comments · Fixed by #3002
Labels

Comments

@aymeric-ledorze
Copy link

Describe the bug

When upgrading my rails application to puma 6, I noticed that the request hang for a noticeable amount of time every time I modify one of my views and reload my app. After a lot of digging, I concluded that the issue is due to a request opening a websocket for ActionCable that opens a read lock (in a rack middleware looking for changes in the view files) but never releases it. This lock will eventually be released when ActiveSupport notices that the previous request did not properly close, and completes it.

The request opening the websocket is hijacked and therefore, the close method is never called on its body. I assume that this should not happen, based on this comment in the previous version:

puma/lib/puma/request.rb

Lines 190 to 192 in 1b6b8ad

# Whatever happens, we MUST call `close` on the response body.
# Otherwise Rack::BodyProxy callbacks may not fire and lead to various state leaks
res_body.close if res_body.respond_to? :close

So I wonder if this issue reopens CVE-2022-23634. Moreover, I wonder if there is not an issue with these lines, where the old value of res_body is lost without first being closed:

puma/lib/puma/request.rb

Lines 107 to 116 in 03ed6c8

rescue ThreadPool::ForceShutdown => e
@log_writer.unknown_error e, client, "Rack app"
@log_writer.log "Detected force shutdown of a thread"
status, headers, res_body = lowlevel_error(e, env, 503)
rescue Exception => e
@log_writer.unknown_error e, client, "Rack app"
status, headers, res_body = lowlevel_error(e, env, 500)
end

To Reproduce

I defined this simple application:

require 'rack'
available = true
run ->(env) {
  if available
    available = false
    env['rack.hijack'].call
    [200, { 'Content-Type' => 'text/plain' }, ::Rack::BodyProxy.new(['Hello World']){ available = true }]
  else
    [500, { 'Content-Type' => 'text/plain' }, ['Unavailable']]
  end
}

with this simple Dockerfile:

FROM ruby:3.1.2
RUN gem install puma rack
CMD ["puma", "hello.ru"]

That I run with docker run --rm -it -v ``pwd``/hello.ru:/hello.ru -p 9292:9292 puma_test.

Expected behavior

On the first request, a resource is consumed but should be released even if the request is hijacked. On the next request, we should get "Hello World".

curl http://127.0.0.1:9292 # hangs, this is expected
curl http://127.0.0.1:9292 # should return "Hello World" but returns "Unaivalable"
@MSP-Greg
Copy link
Member

MSP-Greg commented Oct 25, 2022

Working in this, writing a test...

@dentarg
Copy link
Member

dentarg commented Oct 25, 2022

Linking #2896 that touched on the code linked above (note that the diff for lib/puma/request.rb is big so you have to click to see it in the PR diff)

@MSP-Greg
Copy link
Member

@aymeric-ledorze Please see PR #3002 and the test commit 36c3cb2ab2.

I believe this properly closes the app body, including when lowlevel_error is called.

@shayonj
Copy link
Contributor

shayonj commented Oct 28, 2022

Came across this issue too and can confirm the PR/commit resolves the issue 👍🏾

@louim
Copy link

louim commented Nov 15, 2022

Hey @nateberkopec, are you planning on releasing a new version of Puma including this fix?

@evaneykelen
Copy link

I still have puma version-pinned at 5.6.5 because 6.0 brought my Rails app that is using Active Cable down with just a few clicks. A new release would be highly welcomed!

@earnold
Copy link

earnold commented Dec 2, 2022

I am so happy to find this thread! I was just banging my head against this very issue! Thank you all for being so quick to document and fix - open source rules!

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.

8 participants