Skip to content

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

@eregon

Description

@eregon

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:

cleanup_shutdown_pipe(shutdown_pipe)

closing it from an arbitrary thread:

alarm_shutdown_pipe(&:close)

writing to it (from an arbitrary thread):

alarm_shutdown_pipe {|f| f.write_nonblock("\0")}

A hack:

rescue IOError # closed by another thread.

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 ruby/spec@d8ead5d 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions