Version 17 of Safe Interps

Updated 2006-12-07 13:26:04 by LV

A page to discuss the usefulness of safe interps, and things related (such as what to ensure about your extension to make it 'safe' (see also the safe command).

I'm starting this page with a very informative post from the comp.lang.tcl newsgroup by Jacob Levy. Feel free to add more!


29Sep02 Jacob Levy:

The motivation of this discussion is whether adding a SafeInit C procedure to Itcl is justified based on the grounds of security evaluation. I think this discussion applies equally to any C extension. I also explain below why it is always safe to add any Tcl-only extension to a safe slave interpreter.

To start with, anything available in a safe slave interpreter (when it is first created) is safe for that interpreter. Note that I said "available", hidden functions (ones whose name is given in a call to "interp hide") are not available in the safe interpreter. All dangerous stuff like "exec" and "source" is hidden by "interp create -safe". After that, if the user does not add native code packages or "interp expose" or "interp alias", then the interpreter stays safe. The interpreter stays safe even if you do these things, but carefully. The master interpreter is able to relax security to a degree, under its control, by making more functionality available in the slave safe interpreter through aliases.

In general, a native code package should not violate this rule, so a slave safe interpreter should be as safe with the functionality of this package as it is without it. For more relaxed security, use security policies first introduced with the Tcl plugin. Note that by design, if the package is written in 100% Tcl code it is safe because all Tcl primitives that are unsafe are not available in the safe slave interpreter and thus it matters nought if the code tries to use them; the call will just fail.

So the question to be asked by a security review of Itcl is: is Itcl safe enough so that it can be added to a safe interpreter and not compromise the base security policy provided by "interp create -safe". If not, you should write some other mechanism that only allows adding Itcl to the safe slave under control of an explicit security policy. Of course as Itcl provides functionality implemented by native code, that mechanism is going to look awfully much like the SafeInit, but it will be some other API accessible only under control of the master interpreter.

For an example of a safe native code package, my very own e4Graph [L1 ] package has a SafeInit function that makes the package available in a safe interpreter and only does not make the "tgraph::open" function available. The reasoning is that "tgraph::open" would allow the safe interpreter to open new storages, use arbitrary on disk files, etc. Clearly a security violation. Instead, storages are explicitly shared with the slave safe interpreter by its master through "tgraph::transfer". The rest of the functionality *is* safe because the safe interpreter can not obtain access to storages that it wasn't given access to, only to storages that have explicitly been shared with it by its master. Yes, the safe slave interpreter could do arbitrary damage to the storages that the master chose to share, but that's a calculated risk that the master interpreter decided to take. I consider this risk not to be serious, on the same order as the risk taken by the master when "interp transfer"ing Tcl I/O channels into a slave safe interpreter. The end conclusion is that the safe slave interpreter is as safe with as it is without the provided functionality.

On the other hand, if you want to give a safe slave interpreter access to sockets, the risks are more serious. In that case we need to implement a way to protect resources outside of Tcl on the local machine from serious damage, ideally making them inaccessible to the untrusted code. So for that you could use the "outside" security policy that Laurent Demailly and I wrote for the Tcl plugin. This policy protects the local machine against programs running on other machines, but allows the interpreter to communicate with those programs by opening sockets on ports on those machines. The slave safe interpreter cannot open sockets on ports serviced by the local machine, so it cannot connect e.g. to your smtpd to send hate mail from your machine. But it can connect to nearly anything off of your machine. This is a calculated risk that the master interpreter chose to take: while it protects your local machine from damage, it does not protect your reputation from damage.

As another example, Expect should not have a SafeInit. That is because it provides commands like "spawn" which can be used to execute any command provided by the OS. If the SafeInit hid that command from the safe slave interpreter, perhaps it'd be OK, but "spawn" is so central to the functioning of Expect scripts that removing it makes the package nearly unusable.

Finally, does extending a safe interpreter with an explicit security policy make it unsafe to add any Tcl-only extension to that interpreter? I think not, because security policies are (or should be!) written so that the commands they make available are safe (or low risk) under all circumstances. Since Tcl procedures only combine existing commands, any security policy should be safe with any Tcl-only extension. If this is too complicated, don't worry: just remember that it's OK to use any pure Tcl code in a safe interpreter, whether or not that interpreter has been extended with a security policy.

RHS To the question of does extending a safe interpreter with an explicit security policy make it unsafe to add any Tcl-only extension to that interpreter, I think the answer has to be yes. If the interp is truly safe to start with, then any combination of commands available in that interp cannot make it unsafe. Since a pureTcl extension is only a combination of Tcl commands, then it cannot make the interp unsafe.


Should add more about security policies and how to use them to extend safe slave interpreters. Maybe I'll even get motivated to extricate this functionality from the Tcl plug-in and make it useful on its own. Jacob Levy


Another thing that would be useful to describe, not sure whether here or on its own page, is how to write C extensions that work with multiple interpreters. Jacob Levy


29Sep02 Jacob Levy How do you load Tcl code into a safe interpreter? The "source" command is not available, right? So how do you force the interpreter to read the Tcl code?

The answer is simple: Use "interp invokehidden":

 % set slave [interp create -safe]
 % $slave eval {source foo.tcl}
 ==> invalid command name "source"
 % $slave invokehidden source foo.tcl
 %

So, the "source" command cannot be invoked from code evaluated in the safe interpreter, but it *can* be invoked by the master interpreter, using "invokehidden".

The above only works when the whole extension is contained within one Tcl file. The general solution is provided by the safe command written by Laurent Demailly.


30Sep02 Jacob Levy Two more items.

First, Bob Techentin asks about the relative merits of using interp::safeCreate versus using a raw safe interpreter returned by interp create -safe.

The answer is that nilly willy you're going to want to source some stuff into a safe interpreter, open some scratch files etc. So, either you come up with your own idiosyncratic solution, or use safe::interpCreate to do it in a semi standard way.

In other words, [interp create -safe] gives you a raw interpreter without any aliases. That's of course also what safe::interpCreate does internally. And then it adds some aliases that were very very carefully designed to be safe (nearly as safe as the base safe interpreter that [interp create -safe] returns. The Safe Base is therefore simply one way to extend the raw safe interpreter, and has been carefully reviewed and used extensively, so it's believed to be safe.

If you were to roll your own, you could easily introduce some of the bugs that the war tested Safe Base has already avoided.

Second, Cameron Laird asks what criteria to use for evaluating whether a native code extension is safe.

The answer is that when you add a SafeInit procedure, you're saying that the native code in this extension does not add *any* unsafe capabilities to the interpreter. Defining a SafeInit procedure for an extension is a guarantee of absolute safety, you're saying this code is as safe as the raw safe interpreter, because the extension can now be loaded into any safe interpreter into which other code has already been injected. Thus, not only must this extension be in and of itself safe, it must also be safe in conjunction with any other code that may already be present in a safe interpreter.

As a general rule, if it writes some files, opens sockets, opens windows on the display through which it can talk to the luser, or executes any external commands, I would be suspicious of its safety. Such an extension should NOT have a SafeInit procedure. The explanation in the "SAFE INTERPRETERS" section of the interp man page is a good place to start.


AMG: It's a shame [willHalt] (from Halting Problem) cannot exist. It would be useful for ensuring that arbitrary scripts executed in safe interps do not block the execution of the remainder of the program.

Imagine a web site for experimenting with the Tcl language. It has a form for submitting scripts and getting back the results as calculated by the web server. This would let people play around with Tcl without downloading and installing it themselves, plus the form would incorporate documentation, tutorials, detailed explanations accompanying error results, and so on. The server software would be single-threaded TclHttpd, and the user-submitted scripts would run in safe interpreters.

Can you guess what script will be submitted first? while {1} {}.

My point is that safe interpreters (as created by [interp create -safe]) don't protect against wasting CPU time. I'm curious how to fix this problem. Would it be sufficient to hide [for] and [while]? Those are the only two "safe" commands I see that are capable of unbounded looping. I'd also have to wrap or hide [after] and probably also [vwait].

Thankfully infinite recursions aren't a problem, or I'd have to also outlaw [proc] and even [eval].

 % interp create -safe child
 % interp eval child {proc recurse {} {recurse}; recurse}
 too many nested evaluations (infinite loop?)
 % interp eval child {set x {eval $x}; eval $x}
 too many nested evaluations (infinite loop?)
 % interp eval child {vwait forever}
 can't wait for variable "forever":  would wait forever
 % interp eval child {while {1} {}}
 *** PROGRAM HALTS ***

Not so safe after all?

KBK 2004-08-13 - The original idea of safe interpreters was support for browser applets, where resource exhaustion attacks weren't much of an issue (the user, after all, can always close the page). Needless to say, server-side scripting has to be rather more careful, and Tcl 8.5 makes while and for (and hashtable attacks, and several other things) a good bit safer. It's still wise, however, to launch servlets in separate processes, because (a) we don't have a memory-limiting framework (that's a good bit harder than limiting CPU time), and (b) you really want them run chroot() and so on - even in a safe interp. Multiple layers of protection, and all that. The spec for resource limiting is over at [L2 ].


SYStems In case you are trying to learn Tcl/Tk from Book Practical Programming in Tcl and Tk and reached chapter 19 (skipping few chapters in the middle) and started to play with interp and ...

 #You did this
 interp create -safe aisha
 aisha eval [list puts "My name is Aisha"]
 # You will than come across this
 ==> can not find channel named "stdout"
 # Which means you must do this
 interp share {} stdout aisha
 # To be able to do this 
 aish eval [list puts "My name is Aisha"]
 # and get this
 My name is Aisha

Note that for a reason unknow to me till now, if you try in Tkcon (on debian sarge the output to stdout won't appear, but the error message will definitly disappear.

The above also means you should have read page 299 where I/O from safe interpreter is explained


See also safely source a file. serializable safe slave interp


Category Security