Issue #2250 has been updated by Eregon (Benoit Daloze).

Description updated

Here is my opinion, from the experience of chasing many IO.for_fd bugs due to this behavior.
I agree with the reporter, it doesn't make sense to autoclose a fd which is not owned (for_fd didn't create it, something else did, and it's extremely likely something else should/will close it).

In tests/specs, this is easily the worse kind of bug to track down, because the autoclose on GC will close another fd, randomly, and it's very hard to track down, even with LeakChecker.
Essential, I believe *every* use of for_fd without autoclose=false is a bug.
And I would argue the same for applications.

@hongli Do you have an actual example where the current behavior is useful?

I think it's OK in the very rare cases where we want to have ownership of a fd we did not open to do io.autoclose = true.

I think the Socket#sysaccept documentation example is silly and not good practice.
It should just do client_socket.close, which is what the doc of TCPServer#sysaccept already does.
Closing reliably is anyway better for many reasons, such as avoiding fd exhaustion, quick release of resources, etc.

So, I want to challenge the default here to be sane behavior.
Applications which do not care about fast release of resources (as they rely on finalization close) and want to get ownership of a FD should set io.autolose = true by default.
I expect they are extremely rare.
And it's easier to debug a FD leak than randomly-closed unrelated fds at a later point in time by the GC.

----------------------------------------
Feature #2250: IO::for_fd() objects' finalization dangerously closes underlying fds
https://bugs.ruby-lang.org/issues/2250#change-73605

* Author: pilcrow (Mike Pomraning)
* Status: Closed
* Priority: Normal
* Assignee: nobu (Nobuyoshi Nakada)
* Target version: 
----------------------------------------
=begin
 1. Expected behavior:
 
 An IO object created by IO::for_fd(a_socket_fd) should *not* call close(2) on its underlying fd upon finalization.  The object did not allocate the fd, and so cannot safely nor politely close it.
 
 2. Observed behavior:
 
 Instead, an IO object created by IO::for_fd(a_socket_fd) will attempt to close(2) its underlying socket fd upon finalization.
 
 3. How to reproduce:
 
 The attached script and trivial extension module demonstrate that an IO::for_fd()-created object may secretly close(2) the fd behind an innocent File.new(...) object, causing operations on that File object to fail for no reason apparent in (nor knowable to) the ruby code.  On 32-bit Linux under ruby-1.9 (or 1.8. for that matter), it produces:
 
   $ ruby io-finalize.rb
   #<File::Stat dev=0x11, ino=1193, ....
   Finalizing an IO::for_fd() object...
   io-finalize.rb:29:in `stat': Bad file descriptor - /dev/null (Errno::EBADF)
           from io-finalize.rb:29
 
 
 4. Why this is very bad:
 
 In practice, fds passed to for_fd() may be exposed by extension modules, which modules are responsible for the cleanup of the file descriptor.  Thus, close(2)ing upon finalization may rudely and dangerously close a file descriptor already closed and reassigned to some unrelated bit of code, causing baffling, "action at a distance" failures.
 
 Indeed, the script to reproduce this failure simulates the experience of two different users of two different ruby bindings to PostgreSQL's libpq (ruby-pg and dbd-altpg), whose uses of Kernel.select( [ IO.for_fd(pg_underlying_socket) ] ... ) thus caused failures elsewhere.
 
 Casual testing suggests that this behavior also applies to at least one other non-regular file type, the FIFO.
=end


---Files--------------------------------
syssocket.c (605 Bytes)
io-finalize.rb (853 Bytes)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>