Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I have a piece of code running as an Apache::Registry script under mod_perl that accepts user input and evaluates it. Hopefully, the input is a mathematical expression. In case it is not, I am trying to execture it in a 'Safe' compartment, a la:

use Safe; $compartment = new Safe('userMath'); $result = $compartment->reval($expressionToEval);

The problem is, I want to use the overloaded trig functions from Math::Complex to evaluate things such as sqrt(-4). How can I tell Safe that it is allowed to use Math::Complex?

If I use Math::Complex in the calling package, I can do

$compartment->permit(qw(sqrt));

and the code will then apparently try to use the sqrt() from Math::Complex. However, since it is unaware of things not in the compartment, it fails with

Can't locate object method "make" via package "Math::Complex" at /usr/lib/perl5/5.6.0/Math/Complex.pm line 165.

I can't seem to find a way to make Safe aware of the entire Math::Complex package. I have tried strategies like:

$compartment->share_from('Math::Complex', [ '&make' ]);
and
$compartment->reval('use Math::Complex');

to no avail. Any suggestions?

Replies are listed 'Best First'.
Re: Using Safe to reval complex math
by diotalevi (Canon) on Jun 05, 2003 at 21:23 UTC

    I don't know how to import Math::Complex into a compartment but you should be very careful not to allow use() as an option. In looking at the example on Safe I note that exporting functions is done without the leading ampersand. Try the $compartment->share_from( 'Math::Complex', [ 'make' ] ) syntax instead. If you do that you may find you have to import a number of symbols from Math::Complex.

    Also be sure not to re-use your Safe compartments - there's a known bug in perl versions (less than 5.8.0, I think) which makes re-using the compartment unsafe.

      Thanks for the tip about Safe reuse. That is unfortunate, since I was hoping to do just that so as to not have to set up the environment every time (if I can ever figure out how to set it up in the first place).

      The doc for share sez you are welcome to use '&foo' or 'foo' for a subroutine. I have tried both, just for the hell of it, although my preference would be to agree with you and forgo the ampersand, because they make me nervous.

      Certainly I don't want to let users have access to 'use' or require! But I do need to get Math::Complex in there somehow.

      The aforementioned line 165, btw, is:

      return __PACKAGE__->make($re, defined $im ? $im : 0);

      I believe because it explicitly references __PACKAGE__ it is always going to be trying to look explicitly at the Math::Complex namespace, which will be unavailable to the compartment. I am beginning to suspect that no amount of sharing or sharing from will solve the problem... Mzaybe I will have to just drag the guts of Math::Complex into the compartment without a use?

      --Anonymous Original Poster Monk

Re: Using Safe to reval complex math
by djantzen (Priest) on Jun 05, 2003 at 23:35 UTC

    Okay, according to the docs, you can use an AUTOLOAD to load modules into the compartment.

    You can 'use' the module outside the compartment and share an (autoloaded) function with the compartment. If an autoload is triggered by code in the compartment, or by any code anywhere that is called by any means from the compartment, then the eval in the .... module's AUTOLOAD function happens in the namespace of the compartment. Any variables created or used by the eval'd code are now under the control of the code in the compartment ... A similar effect applies to all runtime symbol lookups in code called from a compartment but not compiled within it.

    use Safe; package userMath::Math::Complex; sub AUTOLOAD { print "AUTOLOAD $AUTOLOAD\n"; # test require '/usr/lib/perl5/5.8.0/Math/Complex.pm'; *Math::Complex::AUTOLOAD = sub { die "Method undefined\n" }; $AUTOLOAD =~ s/userMath//; goto &$AUTOLOAD; } sub DESTROY {}

    The above almost works, except that the compilation of Math::Complex requires that you permit caller, eval and require at least, but still chokes on the overloaded operators. I've gotten roughly the above to work with a simple test module that doesn't do anything special.

    Another option is to return a subroutine reference that can be run from within the calling scope, and which therefore has access to packages compiled there. This is however inherently less secure in case a clever attacker probes the packages available and constructs a potentially dangerous combination. Perhaps a better option would be some sort of proxy object that will limit access to modules outside of the compartment.


    "The dead do not recognize context" -- Kai, Lexx
Re: Using Safe to reval complex math
by djantzen (Priest) on Jun 05, 2003 at 21:30 UTC

    Safe::permit takes an Opcode, and "sqrt" is part of the :base_math category, so double check that you're calling the version you think you are. Also try checking the value of $@ after the call to reval.


    "The dead do not recognize context" -- Kai, Lexx

      Thanks for the feedback.

      I am actually checking $@, just not in the snippet. That is how i caught the above griping about Can't locate object method "make".

      In different permutations of things I have tried, I have had it using the base sqrt as well as the Math::Complex::sqrt. It is easy to tell the difference, because base sqrt will return something like Can't take sqrt of -4 whereas Math::Complex will give you 2i

      --Anonymous Original Poster Monk