[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Binding.of_caller examples don't work.

Doug

1/10/2009 8:47:00 PM

I'm trying to use the Binding class in the extensions library
http://extensions.rubyforge.org/rdoc/...

In particular, I want the method of_caller, but none of the examples
work for me
get 'undefined method or variable' for all that should be in the
caller's scope.

local_variable returns an empty array.

Is this broken?
....using Ruby 1.8.6

thanks,
-doug.



16 Answers

Trans

1/11/2009 1:56:00 AM

0



On Jan 10, 3:49=A0pm, Doug <dou...@pacbell.net> wrote:
> I'm trying to use the Binding class in the extensions libraryhttp://exten=
sions.rubyforge.org/rdoc/index.html
>
> In particular, I want the method of_caller, but none of the examples
> work for me
> get 'undefined method or variable' for all that should be in the
> caller's scope.
>
> local_variable returns an empty array.
>
> Is this broken?
> ...using Ruby 1.8.6

Binding.of_caller is no longer possible.

T.

Phlip

1/11/2009 2:59:00 AM

0

Trans wrote:

> Binding.of_caller is no longer possible.

I just thought of a use for it!

Is a workaround available?

Pit Capitain

1/11/2009 6:12:00 PM

0

2009/1/11 Phlip <phlip2005@gmail.com>:
> Trans wrote:
>> Binding.of_caller is no longer possible.
>
> Is a workaround available?

It seems that the original idea of Florian Gro=DF is still valid. Here's
a version working for my Ruby 1.8.6 on Windows:

##
# Re-implementation of Binding.of_caller for Ruby 1.8.6
# (C) 2009 Pit Capitain
# Original idea and implementation by Florian Gross
#
def Binding.of_caller

old_critical =3D Thread.critical
Thread.critical =3D true

restart_cc =3D result =3D error =3D nil

expected_events =3D %w"line c-call c-return return return"
tracer =3D lambda do |event, file, line, id, binding, classname|
if expected_events.empty?
set_trace_func(nil)
result =3D binding
restart_cc.call
elsif event !=3D expected_events.shift
set_trace_func(nil)
error =3D
"Binding.of_caller used in non-method context" +
" or trailing statements of method using it aren't in the block."
restart_cc.call
end
end

callcc { |cc| restart_cc =3D cc }

if result
yield(result)
elsif error
raise(ArgumentError, error)
else
set_trace_func(tracer)
end

ensure
Thread.critical =3D old_critical
end

Regards,
Pit

Trans

1/11/2009 7:13:00 PM

0



On Jan 11, 1:11=A0pm, "Pit Capitain" <pit.capit...@gmail.com> wrote:
> 2009/1/11 Phlip <phlip2...@gmail.com>:
>
> > Trans wrote:
> >> Binding.of_caller is no longer possible.
>
> > Is a workaround available?
>
> It seems that the original idea of Florian Gro=DF is still valid. Here's
> a version working for my Ruby 1.8.6 on Windows:
>
> =A0 ##
> =A0 # =A0Re-implementation of Binding.of_caller for Ruby 1.8.6
> =A0 # =A0(C) 2009 Pit Capitain
> =A0 # =A0Original idea and implementation by Florian Gross
> =A0 #
> =A0 def Binding.of_caller
>
> =A0 =A0 old_critical =3D Thread.critical
> =A0 =A0 Thread.critical =3D true
>
> =A0 =A0 restart_cc =3D result =3D error =3D nil
>
> =A0 =A0 expected_events =3D %w"line c-call c-return return return"
> =A0 =A0 tracer =3D lambda do |event, file, line, id, binding, classname|
> =A0 =A0 =A0 if expected_events.empty?
> =A0 =A0 =A0 =A0 set_trace_func(nil)
> =A0 =A0 =A0 =A0 result =3D binding
> =A0 =A0 =A0 =A0 restart_cc.call
> =A0 =A0 =A0 elsif event !=3D expected_events.shift
> =A0 =A0 =A0 =A0 set_trace_func(nil)
> =A0 =A0 =A0 =A0 error =3D
> =A0 =A0 =A0 =A0 =A0 "Binding.of_caller used in non-method context" +
> =A0 =A0 =A0 =A0 =A0 " or trailing statements of method using it aren't in=
the block."
> =A0 =A0 =A0 =A0 restart_cc.call
> =A0 =A0 =A0 end
> =A0 =A0 end
>
> =A0 =A0 callcc { |cc| restart_cc =3D cc }
>
> =A0 =A0 if result
> =A0 =A0 =A0 yield(result)
> =A0 =A0 elsif error
> =A0 =A0 =A0 raise(ArgumentError, error)
> =A0 =A0 else
> =A0 =A0 =A0 set_trace_func(tracer)
> =A0 =A0 end
>
> =A0 ensure
> =A0 =A0 Thread.critical =3D old_critical
> =A0 end

Cool. But word or warning to Doug, I would not recommend using
#set_trace_func in a production application. It can slow things down
considerably.

T.

Ken Bloom

1/12/2009 2:24:00 AM

0

On Sun, 11 Jan 2009 14:13:29 -0500, Trans wrote:

> On Jan 11, 1:11 pm, "Pit Capitain" <pit.capit...@gmail.com> wrote:
>> 2009/1/11 Phlip <phlip2...@gmail.com>:
>>
>> > Trans wrote:
>> >> Binding.of_caller is no longer possible.
>>
>> > Is a workaround available?
>>
>> It seems that the original idea of Florian GroÃ? is still valid.
>
> Cool. But word or warning to Doug, I would not recommend using
> #set_trace_func in a production application. It can slow things down
> considerably.
>
> T.

First, the version in the extensions gem uses Florian GroÃ?'s idea, so
it's the same as what Philip posted, modulo the organization of
initialization code and the like.

Second, the trace function is only being used for a couple of calls
before the continuation is called, and execution jumps back into
Binding.of_caller at which point the trace function is removed. It
wouldn't appear to me like it would hurt things too much. (Maybe it
requires turning on an otherwise disabled feature in JRuby?)

Third: it looks like this should be portable to Ruby 1.9. Ruby 1.9 no
longer has Thread.critical, replacing it with Thread.exclusive (which
takes a block). Although Thread.exclusive is not suitable for this
function because the block exits before the function exits, it should be
unnecessary to lock out other threads anymore, since you should be able
to do a Thread.current.set_trace_func instead of Kernel#set_trace_func
and have it affect only the current thread.

--Ken



--
Chanoch (Ken) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu...

Charles Oliver Nutter

1/12/2009 3:27:00 AM

0

Ken Bloom wrote:
> Second, the trace function is only being used for a couple of calls
> before the continuation is called, and execution jumps back into
> Binding.of_caller at which point the trace function is removed. It
> wouldn't appear to me like it would hurt things too much. (Maybe it
> requires turning on an otherwise disabled feature in JRuby?)

Tracing works, but JRuby and IronRuby do not support continuations, and
I think they work differently in 1.9. It's a hack that's unlikely to
work reliably, even across minor versions of a given implementation.

In general I think Binding.of_caller is a really, really bad idea
because it exposes internal details every method call in *your* code to
the whims of *someone else's* code. Basically, it breaks the most sacred
encapsulation possible, a method call's local variables.

Imagine a case like this:

def secure_action(encrypted_password)
decrypted_password = decrypt(encrypted_password)
authorize(decrypted_password)
do_secure_task
end

If it were possible to modify do_secure task, or if you were simply
calling a third-party library, it could access the runtime variables of
the caller:

def do_secure_task
binding = Binding.of_caller
vars = eval 'local_variables', binding
vars.each do |name|
if name =~ /password/
use_for_evil(eval name, binding)
end
end
end

I even implemented Binding.of_caller for fun once in JRuby, and removed
it when I realized how invasive and dangerous it was.

Just like retry was limited in 1.9 to restarting a rescued block of code
(rather than being able to reevaluate the original arguments and
receiver), Binding.of_caller should remain unsupported.

- Charlie


Bill Kelly

1/12/2009 9:11:00 AM

0


From: "Charles Oliver Nutter" <charles.nutter@sun.com>
>
> In general I think Binding.of_caller is a really, really bad idea
> because it exposes internal details every method call in *your* code to
> the whims of *someone else's* code. Basically, it breaks the most sacred
> encapsulation possible, a method call's local variables.
>
> Imagine a case like this:
>
> def secure_action(encrypted_password)
> decrypted_password = decrypt(encrypted_password)
> authorize(decrypted_password)
> do_secure_task
> end
>
> If it were possible to modify do_secure task, or if you were simply
> calling a third-party library, it could access the runtime variables of
> the caller:
>
> def do_secure_task
> binding = Binding.of_caller
> vars = eval 'local_variables', binding
> vars.each do |name|
> if name =~ /password/
> use_for_evil(eval name, binding)
> end
> end
> end

If we're calling untrusted code and giving it full execution privileges, then
I think Binding.of_caller is the least of our worries. The untrusted code
could as easily `rm -rf ~` or innumerable other evil things.

Untrusted code needs to be sandboxed.

Seems we could either make Binding.of_caller not work at all if $SAFE
is >= 3 ... or preferably, if it's possible for the interpreter to determine
whether the caller's binding would have a lower $SAFE level than the
current binding, then only raise a SecurityException in that case.

Would this address your security concerns?


Regards,

Bill



Ken Bloom

1/12/2009 2:47:00 PM

0

On Sun, 11 Jan 2009 22:27:29 -0500, Charles Oliver Nutter wrote:

> Ken Bloom wrote:
>> Second, the trace function is only being used for a couple of calls
>> before the continuation is called, and execution jumps back into
>> Binding.of_caller at which point the trace function is removed. It
>> wouldn't appear to me like it would hurt things too much. (Maybe it
>> requires turning on an otherwise disabled feature in JRuby?)
>
> Tracing works, but JRuby and IronRuby do not support continuations, and
> I think they work differently in 1.9. It's a hack that's unlikely to
> work reliably, even across minor versions of a given implementation.
>
> In general I think Binding.of_caller is a really, really bad idea
> because it exposes internal details every method call in *your* code to
> the whims of *someone else's* code. Basically, it breaks the most sacred
> encapsulation possible, a method call's local variables.
>
> Imagine a case like this:
>
> def secure_action(encrypted_password)
> decrypted_password = decrypt(encrypted_password)
> authorize(decrypted_password)
> do_secure_task
> end
>
> If it were possible to modify do_secure task, or if you were simply
> calling a third-party library, it could access the runtime variables of
> the caller:
>
> def do_secure_task
> binding = Binding.of_caller
> vars = eval 'local_variables', binding vars.each do |name|
> if name =~ /password/
> use_for_evil(eval name, binding)
> end
> end
> end
>
> I even implemented Binding.of_caller for fun once in JRuby, and removed
> it when I realized how invasive and dangerous it was.
>
> Just like retry was limited in 1.9 to restarting a rescued block of code
> (rather than being able to reevaluate the original arguments and
> receiver), Binding.of_caller should remain unsupported.
>
> - Charlie

It turns out that Binding.of_caller isn't a hack I need. It's a hack I
though I needed when I thought I'd have to use Ripper to fix s-expression
generation in the SqlStatement for Ruby 1.9. Then I discovered that all
the operators I needed were now overloadable, so I went with that instead.

Which reminds me of another question I had. You were discussing somewhere
else how Thread.critical works in JRuby. Does Thread.critical /
Thread.exclusive in JRuby protect other threads against changes to the
core class methods? Can I run Thread.critical=true , dramatically
redefine operators on the core classes, redefine the operators back
their original implementations, run Thread.critical=false, and expect
that no other JRuby thread can get in and be broken becuase I've
dramatically gutted the core classes?

--
Chanoch (Ken) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu...

Charles Oliver Nutter

1/12/2009 3:21:00 PM

0

Bill Kelly wrote:
> If we're calling untrusted code and giving it full execution privileges,
> then
> I think Binding.of_caller is the least of our worries. The untrusted code
> could as easily `rm -rf ~` or innumerable other evil things.
>
> Untrusted code needs to be sandboxed.

Sandboxing is made nearly impossible if calls in your own chain can
escalate privileges by executing code from your binding. This is the
primary reason why prototype continuation support for the JVM probably
will never be shipped...the ability to implicitly access things
elsewhere in the call stack has all sorts of really nasty security
issues, even in a pretty secure environment like the JVM.

> Seems we could either make Binding.of_caller not work at all if $SAFE
> is >= 3 ... or preferably, if it's possible for the interpreter to
> determine
> whether the caller's binding would have a lower $SAFE level than the
> current binding, then only raise a SecurityException in that case.
>
> Would this address your security concerns?

No, because JRuby doesn't support $SAFE. It's a blunt instrument that I
don't think anyone has ever audited for actual safety. So as a result,
anyone believing $SAFE is actually safe is probably introducing even
more risk into their apps.

Also, $SAFE is a global thing, and you can't localize it to a given
binding, so that wouldn't work anyway.

Honestly, if you want a binding, pass one in.

- Charlie

Charles Oliver Nutter

1/12/2009 3:34:00 PM

0

Ken Bloom wrote:
> Which reminds me of another question I had. You were discussing somewhere
> else how Thread.critical works in JRuby. Does Thread.critical /
> Thread.exclusive in JRuby protect other threads against changes to the
> core class methods? Can I run Thread.critical=true , dramatically
> redefine operators on the core classes, redefine the operators back
> their original implementations, run Thread.critical=false, and expect
> that no other JRuby thread can get in and be broken becuase I've
> dramatically gutted the core classes?

You could up until about a week ago, when I proactively modified
critical= to just be a reentrant global lock. So basically..

Before:
* critical= causes the current thread to be the only thread running
* ...except on JRuby where threads run in parallel and have to reach a
checkpoint before they stop
* ...or if code you call from within a critical section itself spins up
threads

After:

* critical= acquires or releases a lock on a global mutex
* threads that don't have critical sections continue running
* threads that try to acquire the lock while another thread has it block

This was actually proposed by Shri Borde of IronRuby and blogged here:

http://blogs.msdn.com/shrib/archive/2009/01/07/proposed-spec-for-ruby-s-thread-cri...

She (he?) proposes that almost all current uses of critical= intend to
simply delimit a critical section of code, and so a single global mutex
is sufficient for those cases. And in light of the fact that native
calls and parallel-threaded impls can't be guaranteed to deschedule
other threads, I think this is a much more concrete and reasonable
definition. So I agreed, said so on a critical-related ruby-core thread,
and went ahead with the change. Nothing has broken so far :)

For your case, I think your best bet is to either make those changes
before other threads start running or just accept that some of them
won't see your changes all at once. I believe what you want to do may
work on MRI, since it doesn't actually run threads in parallel and
critical= stops it from scheduling new ones on its own.

But also note that critical= is gone in 1.9, so you should not use it
anyway. Use a Mutex whenever possible...it's quick, easy, and safe.

- Charlie