On Sun, Oct 15, 2006 at 05:33:16PM +0900, Eric Hodel wrote:
> 
> Please attach the testcase.


OK, i have stripped down the testcase further. The program does not
make much sense anymore, but still shows the bug:

The program calls 

  IPAddrRange.new("192.168.0.0-192.168.255.255")

twice, once directly and once hidden within the

  Whois.get("192.168.155.1")

call. Althoug both calls to IPAddrRange.new do the very same, it works
for the first time and then it fails for the second time, because the
regular expression engine seems to have gone into a state where it
keeps tainting any results. 


When I run the attached program the output looks like this:



% ruby WhoisBug.rb
IPAddrRangeInit 192.168.0.0-192.168.255.255 Tainted false  SAFE=1
Tainted arg false 192.168.0.0-192.168.255.255  SAFE=1
Tainted v   false |192.168.0.0|
Tainted b   false |192.168.255.255|

192.168.0.0-192.168.255.255

IPAddrRangeInit 192.168.0.0-192.168.255.255 Tainted false  SAFE=1
Tainted arg false 192.168.0.0-192.168.255.255  SAFE=1
Tainted v   false |192.168.0.0|
Tainted b   false |192.168.255.255|
Exception: invalid address  (ArgumentError)
/usr/lib/ruby/1.8/ipaddr.rb:422:in `initialize'
WhoisBug.rb:27:in `new'
WhoisBug.rb:27:in `initialize'
WhoisBug.rb:159:in `new'
WhoisBug.rb:159:in `parse'
WhoisBug.rb:137:in `each'
WhoisBug.rb:137:in `parse'
WhoisBug.rb:94:in `get_whois_net'
WhoisBug.rb:76:in `get'
WhoisBug.rb:186


So calling the same thing twice works only in the first time.

regards
Hadmut
# Copyright 2003-2006 Hadmut Danisch, hadmut / danisch.de

require 'thread'
require 'monitor'
require 'ipaddr'


##########################################################################

class IPAddrRange
  attr_reader :von, :bis, :mask_len


  def initialize(arg)
    @von,@bis,@mask=nil,nil,nil
    
    $stderr.puts "IPAddrRangeInit #{arg} Tainted #{arg.tainted?}  SAFE=#{$SAFE}"

    case arg

      when /^([\da-f\.:]+)\s*-\s*([\da-f\.:]+)$/i
        v,b=$1.untaint,$2.untaint
        $stderr.puts "Tainted arg #{arg.tainted?} #{arg}  SAFE=#{$SAFE}"
	$stderr.puts "Tainted v   #{v.tainted?} |#{v}|"
	$stderr.puts "Tainted b   #{b.tainted?} |#{b}|"
        @von=IPAddr.new(v)
        @bis=IPAddr.new(b)

      else
        raise "Address Range Syntax"
    end
    $stderr.puts
  end


  def to_s
    @von.to_s + "-" + @bis.to_s
  end

end






###########################################################################


class WhoisData
  attr_reader :country,:org,:range,:id,:typ

  def initialize(country,org,range,id=nil,typ=nil)
    @country,@org,@range=country,org,range
    @id=id
    @typ=1
  end


  def to_s
    "#{@country}:  #{@org}   #{@range}"
  end

end


###########################################################################

module Whois


  def Whois.get(ipaddr,dbmutex=nil)

    ipaddr  = IPAddr.new(ipaddr)
    addrkey = ipaddr.native
    result  = Whois.get_whois_net(addrkey)

    return result
  end



###########################################################################

  def Whois.get_whois_net(net,host=nil)
    #whois = `jwhois -d #{net}`
    # jwhois call replaced with static data for debugging purposes
    whois=<<ENDE
OrgName:    Internet Assigned Numbers Authority 
Country:    US
NetRange:   192.168.0.0 - 192.168.255.255 
ENDE
    whois.taint
    Whois.parse(whois)
  end




###########################################################################


  def Whois.parse(whois)

    case whois
      when String
	whois=whois.split("\n")
      when Array
	# ok
      else
	raise "Argument #{whois.class}"
    end


      wh = {}
      for zeile in whois do 
	zeile.delete!("\r\n")
	#puts "Z #{zeile}"

	case zeile 

	  when /^(\w[\w ]+\w)\s*:\s*([-_\s\w\.\,\(\)\/]+)$/
	    wh[$1]=$2 unless wh.has_key?($1)
	  
        end
      end

      #wh.keys.sort.each{|k| printf "%-20.20s %s\n",k,wh[k]}




      # Here the bug begins:
      # Problem does not occur if we pass back the same results directly
      # return "US","IANA",IPAddrRange.new("192.168.0.0 - 192.168.255.255")
      
      for i in [
  	  ["NetRange",     "OrgName",  "Country"],
  	  ["NetRange",     "NetName",  "Country"],
  	] do
  	
   	  netkey,orgkey,countrykey=*i
          if    wh.has_key?(netkey) &&
         	wh.has_key?(orgkey)  &&
  	        wh.has_key?(countrykey)	

	    addr=wh[netkey].gsub(" ","")
	 
	    case addr 
	      when /^[-\.\d]+$/
		addr=addr.untaint
	      else
	        raise "Address syntax"
	    end

	    #$stderr.puts "Angekommen #{wh[orgkey]} |#{wh[netkey]}|#{addr}|#{addr.tainted?}"
	    return  WhoisData.new(wh[countrykey],  
				  wh[orgkey],
				  IPAddrRange.new(addr)) 
	  end #if
      end #for

      return nil
    
  end









end


###########################################################################

if __FILE__ == $0
  begin

    $SAFE=1
    puts IPAddrRange.new("192.168.0.0-192.168.255.255")
    puts
    puts Whois.get("192.168.155.1")

  rescue SystemExit
    raise
  rescue Exception => exc
    $stderr.puts "Exception: #{exc}  (#{exc.class.to_s})"
    $stderr.puts exc.backtrace
    exit 99
  end

end