Skip to content

IO#accept raises NullPointerException instead of IOError when the socket is closed concurrently #9714

Description

@aminmansuri

OpenFile#channel() reads the non-volatile field fd twice: checkClosed()
first, then return fd.ch. Another thread closing the IO sets fd = null
(OpenFile.java:953) between the two reads, so the caller gets a Java NPE
rather than the IOError the check was there to raise.

Java::JavaLang::NullPointerException:
  Cannot read field "ch" because "this.fd" is null
  org/jruby/util/io/OpenFile.java:2584 in channel
  org/jruby/RubyIO.java:481 in getChannel
  org/jruby/ext/socket/RubyTCPServer.java:292 in getServerSocketChannel
  org/jruby/ext/socket/RubyTCPServer.java:159 in accept

Reproduced on jruby-10.0 at 6ab9bcc, JDK 25: the suite's own
test_accept_on_closed_server_raises_ioerror (test/jruby/test_io.rb) fails
about 1 run in 50, and 8 in 10 with a Thread.yield() inserted between the
check and the dereference. It shows up as an intermittent CI failure on
rake test:jruby:jit.

The window appeared in b84d783 (the fix for #9324), which replaced
assert(fd != null) with checkClosed(); the assert had the same two-read
shape but was disabled in normal runs.

ready() dereferences fd with no check at all, and setBlocking() has the
same pattern.

Fix: read the field once. PR to follow.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions