ruby / ruby/webrick

WEBrick has an unsafe shutdown process it tries to concurrently write and close the @shutdown_pipe

Open
#102 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Ruby
Stars
327
Forks
112
Avg merge
4h 2m
Merged PRs (30d)
1

Description

When WEBrick shutdowns, it tries to concurrently write and close a file descriptor, and even tries to close it from multiple threads:

closing it from the main webrick thread:
https://github.com/ruby/webrick/blob/841b7dadd5d2dc7cd5f5cceca2258ad53023f8dd/lib/webrick/server.rb#L207

closing it from an arbitrary thread:
https://github.com/ruby/webrick/blob/841b7dadd5d2dc7cd5f5cceca2258ad53023f8dd/lib/webrick/server.rb#L237

writing to it (from an arbitrary thread):
https://github.com/ruby/webrick/blob/841b7dadd5d2dc7cd5f5cceca2258ad53023f8dd/lib/webrick/server.rb#L227

A hack:
https://github.com/ruby/webrick/blob/841b7dadd5d2dc7cd5f5cceca2258ad53023f8dd/lib/webrick/server.rb#L353

The problem is if the write_nonblock which calls write(2) ends up happening once the fd is close(2)d then it's EBADF, or worse writing to the wrong file descriptor.
This became such an issue that ruby/spec stopped using WEBrick and rewrote to make its own HTTP server to avoid this issue. Also the commit message of https://github.com/ruby/spec/commit/d8ead5da7158d31660b87f2c9db7d80755a57b27 may be interesting.

Only one thread (e.g. the main webrick thread) should close it, and it should wait all sub-threads before closing it so there are concurrent writes to the close.

CRuby has some very complex logic in IO#close which avoids the issue in most cases but it's not clear if it's fully reliable: https://ruby.slack.com/archives/C02A3SL0S/p1636604027275700?thread_ts=1636592668.266300&cid=C02A3SL0S
IIRC I've seen it fail for ruby/spec too on CRuby.

cc @ioquatix

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the shutdown path in lib/webrick/server.rb, especially the referenced lines around closing, writing, and the shutdown hack. Compare the behavior with ruby/spec's library/net/http/http/fixtures/http_server.rb and verify that one thread closes @shutdown_pipe only after sub-threads finish, with no concurrent writes or closes.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.