From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS43289 178.175.128.0/17 X-Spam-Status: No, score=-3.3 required=3.0 tests=AWL,BAYES_00, RCVD_IN_MSPIKE_BL,RCVD_IN_MSPIKE_ZBI,RCVD_IN_XBL,SPF_FAIL,SPF_HELO_FAIL, TO_EQ_FM_DOM_SPF_FAIL shortcircuit=no autolearn=no autolearn_force=no version=3.4.0 Received: from 80x24.org (torsrv6.snydernet.net [178.175.131.194]) by dcvr.yhbt.net (Postfix) with ESMTP id E833820958 for ; Sun, 26 Mar 2017 22:42:04 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] socket: reload fptr->fd after blocking operations Date: Sun, 26 Mar 2017 22:42:01 +0000 Message-Id: <20170326224201.26531-1-e@80x24.org> List-Id: IO objects may be closed by other threads while inside an I/O blocking region, setting fptr->fd to -1. So we must reload it to avoid waiting on an invalid FD. --- ext/socket/basicsocket.c | 3 ++- ext/socket/unixsocket.c | 6 ++++-- test/socket/test_unix.rb | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c index 3b6f22f36a..f79ab2d7cf 100644 --- a/ext/socket/basicsocket.c +++ b/ext/socket/basicsocket.c @@ -551,7 +551,8 @@ rsock_bsock_send(int argc, VALUE *argv, VALUE sock) arg.flags = NUM2INT(flags); while (rsock_maybe_fd_writable(arg.fd), (n = (ssize_t)BLOCKING_REGION_FD(func, &arg)) < 0) { - if (rb_io_wait_writable(arg.fd)) { + if (rb_io_wait_writable(fptr->fd)) { + arg.fd = fptr->fd; continue; } rb_sys_fail("send(2)"); diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c index 5a44b552f8..669b33428b 100644 --- a/ext/socket/unixsocket.c +++ b/ext/socket/unixsocket.c @@ -264,8 +264,9 @@ unix_send_io(VALUE sock, VALUE val) arg.fd = fptr->fd; while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) { - if (!rb_io_wait_writable(arg.fd)) + if (!rb_io_wait_writable(fptr->fd)) rsock_sys_fail_path("sendmsg(2)", fptr->pathv); + arg.fd = fptr->fd; } return Qnil; @@ -359,8 +360,9 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock) arg.fd = fptr->fd; while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) { - if (!rb_io_wait_readable(arg.fd)) + if (!rb_io_wait_readable(fptr->fd)) rsock_sys_fail_path("recvmsg(2)", fptr->pathv); + arg.fd = fptr->fd; } #if FD_PASSING_BY_MSG_CONTROL diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb index 7edb5e5d4f..ff72a6990f 100644 --- a/test/socket/test_unix.rb +++ b/test/socket/test_unix.rb @@ -699,4 +699,20 @@ def test_accept_nonblock assert_equal :wait_readable, serv.accept_nonblock(exception: false) } end + + def test_fd_passing_blocked + s1, s2 = UNIXSocket.pair + s1.nonblock = true + th = Thread.new do + begin + s1.recv_io + rescue => e + e + end + end + Thread.pass until th.stop? + s1.close + s2.close + assert_instance_of IOError, th.value + end end if defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM -- EW