Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby - why don't blocks inherit their caller's $SAFE level like methods?

When a method is called by a thread with $SAFE = 4, that method is run with the same $SAFE level:

def test_method
  raise "value of $SAFE inside the method: #{$SAFE}"
end
t = Thread.new{$SAFE = 4; self.test_method}; t.join
 => RuntimeError: value of $SAFE inside the method: 4

However, when a block is called, it seems to use the $SAFE from its original context instead:

test_lambda = lambda do
  raise "value of $SAFE inside the lambda: #{$SAFE}"
end
t = Thread.new{$SAFE = 4; test_lambda.call}; t.join
 => RuntimeError: value of $SAFE inside the lambda: 0

Can someone explain why it works this way? It seems like a security problem.

(the reason I'm using raise instead of puts is that puts doesn't work at $SAFE = 4)

This can be used to eval a tainted string in a seemingly safe context:

test_lambda = lambda{|s| puts "Tainted: #{s.tainted?}"; eval s}
t = Thread.new{$SAFE = 4; test_lambda.call("puts `date`")}; t.join
=> Tainted: true
=> Fri Mar 30 03:15:33 UTC 2012
like image 773
rcrogers Avatar asked Mar 28 '12 23:03

rcrogers


1 Answers

It's because a lambda operates with the scope it was defined at (including all the local variables!)

Hence, you defined the lambda at safe level 0, and it therefore executed at that level when it was called, as that's what the state of the variable was.

like image 180
Joe Pym Avatar answered Sep 28 '22 16:09

Joe Pym