eval statement

P

Pascal J. Bourguignon

Christopher Dicely said:
Without taking additional special care, restricting eval to the to a
particular binding doesn't contain it very much. eval can, unless
something is done to stop it, get access to every Object in the top
level environment.

For example try running this in irb:

hash = Hash.new
object = Object.new
def object.get_binding
binding
end
b = object.get_binding
eval('ObjectSpace.each_object {|o| next if o.frozen?; begin def
o.inspect; "pwned"; end; rescue TypeError; end}',a)
hash #=>pwned

If you run untrusted code in eval without being extra careful, it can
reach out of the binding its in and stomp over other objects, even
outside of that binding.


Ok. In CL we say EVAL is evil because it cannot do some things, and in
Ruby you say it's evil because it can. How interesting...


Of couse, when you execute dynamic code, you must trust the source of
that code. This doesn't "evilize" the mechanism to execute it.
 
M

Mike Gold

David A. Black:
p local_variables #=> ["a"]
p a #=> undefined local variable or method `a'

We see that the local exists, but because the parser has not seen
the "a = ..." syntax, we can't access it.

In 1.9 you get:

[]
-:3:in `<main>': undefined local variable or method `a' for
main:Object (NameError)

so I guess that's been changed.

Yes, in 1.9 the local table is inside the parser, and a new parser is
created for each eval. In 1.8 the local table is static (as well as
the parser).
The point about rb_method_missing (as the source of that error
message) is interesting, though since it's be handled by a handler
for non-existent methods, and within that handler it determines that
it also isn't a local variable, I think it's reasonable to say that
it can't tell -- or, perhaps more accurately, it *can* tell that it
is neither a (viable) method call nor a variable, but it can't tell
which you meant (hence the wording of the error message).

No, I don't see a handler which determines that it isn't a local
variable. That was already decided by the parser which created a
method-call node because no "a = 1" syntax was found. The possibility
of a local variable was long since gone.

p local_variables #=> [:a]
a = nil

Is Ruby prescient? No, it's just the parser that determines locals.

Take the 1.9 example again:

eval("a = 3")
p local_variables #=> []
p a #=> undefined local variable or method `a'

But

eval("a = 3")
p local_variables #=> [:a]
a = a
p a #=> 3

The parser looks for local assignment syntax and builds nodes
accordingly, then the interpreter runs.
There's also this interesting difference. In 1.8, given this code:

eval("a=3")
p a rescue p "undef"
eval("p a")

you get this output:

"undef"
3

whereas in 1.9 the output for the same code is:

"undef"
-:3:in `eval': undefined local variable or method `a'

So the business of having eval always return to the same scope seems
to have been done away with. (I always thought of it as similar to the
phenomenon of having to be drunk again to remember what you did when
drunk :)

It's not really a scope issue; it's a parser issue. When we use the
same scope it fails in the same manner,

eval("a=3", binding)
p a rescue p "undef" #=> "undef"
eval("p a", binding) #=> undefined local variable or method `a'

I don't know if there is an official terminology--I would say these
two calls to 'binding' refer to the same scope while technically being
different bindings.

Curiously, we can get the 1.8 behavior in 1.9 by using the same
binding,

bind = binding
eval("a=3", bind)
p a rescue p "undef" #=> "undef"
eval("p a", bind) #=> 3
 
D

David A. Black

Hi --

David A. Black:
The point about rb_method_missing (as the source of that error
message) is interesting, though since it's be handled by a handler
for non-existent methods, and within that handler it determines that
it also isn't a local variable, I think it's reasonable to say that
it can't tell -- or, perhaps more accurately, it *can* tell that it
is neither a (viable) method call nor a variable, but it can't tell
which you meant (hence the wording of the error message).

No, I don't see a handler which determines that it isn't a local
variable. That was already decided by the parser which created a
method-call node because no "a = 1" syntax was found. The possibility
of a local variable was long since gone.

p local_variables #=> [:a]
a = nil

Is Ruby prescient? No, it's just the parser that determines locals.

I'll stop trying to make that particular point, which is simpler than
I'm managing to make it. All I'm really saying is that, one way or
another, Ruby deems it appropriate to tell you that something that
could, syntactically, be a local variable or a method call is in fact
neither. To some extent I think that how it arrives at that conclusion
is an implementation detail (though I'm reading the stuff you're
saying about the implementation with great interest).
Take the 1.9 example again:

eval("a = 3")
p local_variables #=> []
p a #=> undefined local variable or method `a'

But

eval("a = 3")
p local_variables #=> [:a]
a = a
p a #=> 3

The parser looks for local assignment syntax and builds nodes
accordingly, then the interpreter runs.

Somewhere along the line I got the impression you were saying that it
was by parsing the argument-string, prior to executing eval, that Ruby
figured out there was an 'a' local variable. Never mind. I think I'm
back on course.
It's not really a scope issue; it's a parser issue. When we use the
same scope it fails in the same manner,

eval("a=3", binding)
p a rescue p "undef" #=> "undef"
eval("p a", binding) #=> undefined local variable or method `a'

I don't know if there is an official terminology--I would say these
two calls to 'binding' refer to the same scope while technically being
different bindings.

It's reminiscent of what happens in a block: whatever exists already,
by way of locals, is there, but anything created there does not
survive the exit. (Reminiscent, not technically connected.) It's also
a bit like the wrapper nature of things like method or float objects,
where (for example) adding a singleton method to 1.2 doesn't do any
good the next time you write 1.2.
Curiously, we can get the 1.8 behavior in 1.9 by using the same
binding,

bind = binding
eval("a=3", bind)
p a rescue p "undef" #=> "undef"
eval("p a", bind) #=> 3

That's also similar to other wrapper behavior (like saving a
particular float in a variable). It does seem like binding should be a
proxy more than a wrapper, if that makes sense.


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 
A

Albert Schlef

Mike said:
The thing to remember is that local variables are always determined at
parse time.

Yeah. That's why debuggers list local varibales even before we arrive at
the assignment to them.
The contents of 'a=2' is not seen by the parser when the file is read,
so in the second case the 'a' in 'puts a' is determined to be a method
call: "call method 'a', then call method puts".

I don't think you are correct here. The string 'a=2' is first *parsed*,
only then evaluated. If your theory was correct, eval('whatever=567')
would have generated some error (because there's no 'whatever=' method).
 
M

Mike Gold

Albert said:
I don't think you are correct here. The string 'a=2' is first *parsed*,
only then evaluated.

I can't pinpoint where the misunderstanding is. The _contents_ of 'a=2'
is not seen by the parser when the file is read, meaning the parser just
sees a string and nothing more. The AST of the file would be the same
if the string were 'foo'. Because no local assignment syntax is
encountered by the parser _when the file is read_, the 'a' in 'puts a'
is determined to be a method call.

The parsing and evaluating of 'a=2' is done separately at runtime, long
after the 'a' in 'puts a' was determined to be a method call.
If your theory was correct, eval('whatever=567')
would have generated some error (because there's no 'whatever=' method).

I don't know what this theory is, but in any case 'whatever=567' would
not under any circumstances be interpreted as calling method
'whatever='. The syntax x=y is always local assignment.
(self.whatever=567 for the method call.)
 
D

David A. Black

Hi --

Yeah. That's why debuggers list local varibales even before we arrive at
the assignment to them.

It's also the reason for the classic example:

if false
x = 1
end

p x # nil
p y # Error: unknown

which never fails to widen some eyes :)


David

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2)

http://www.wishsight.com => Independent, social wishlist management!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,172
Latest member
NFTPRrAgenncy
Top