Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to brake a Tcl script

1,618 views
Skip to first unread message

Dariusz Bednarczyk

unread,
Dec 27, 1999, 3:00:00 AM12/27/99
to
I'm using Tcl on WindowsNT.

My application written in C++ executes Tcl scripts with
Tcl_Eval. How can I break the script execution from C++?

Dariusz Bednarczyk
dare...@poczta.wp.pl

Bob Techentin

unread,
Dec 27, 1999, 3:00:00 AM12/27/99
to

Stand-alone Tcl scripts can be run with the tclsh or wish shells. You
can run it from the command line with something like:

c:\tcl\bin\tclsh82 myScript.tcl

If you still want to run this from within your C++ program, you can use
exec, spawn, or system calls to execute tclsh.

Is that what you are after?

Bob
--
Bob Techentin techenti...@mayo.edu
Mayo Foundation (507) 284-2702
Rochester MN, 55905 USA http://www.mayo.edu/sppdg/sppdg_home_page.html

Scott Redman

unread,
Dec 27, 1999, 3:00:00 AM12/27/99
to
There is no way to break the execution of a Tcl script
in an embedded Tcl interpreter. (actually, there probably
is, but it would involve a lot of work and would seriously
slow down the interpreter).

You're best bet is to periodically call a function in
you C++ code that checks a flag and halts the interp.
But, this probably isn't what you want.

If you want to kill the interp, put it in another thread
and kill the thread off to halt it (this may leak memory
though).

-- Scott

Dariusz Bednarczyk

unread,
Dec 28, 1999, 3:00:00 AM12/28/99
to
Scott Redman wrote:
>
> You're best bet is to periodically call a function in
> you C++ code that checks a flag and halts the interp.
> But, this probably isn't what you want.
>

Next question is, how to halt the interp from C++ code.
Is there any example for this?

Dariusz Bednarczyk
dare...@poczta.wp.pl

Mike Weiblen

unread,
Dec 28, 1999, 3:00:00 AM12/28/99
to
I'm interested along similar lines...

For my application I'd like to use two interpreters: a "master" for the
main app and GUI, and a "sandbox" for the user to execute arbitrary
code.

The sandbox provides a safe evaluation environment, where the user can
to crazy things, but from which the master can recover. I'm willing to
hinder the performance of the sandbox, in favor of safety and
recoverability.

One of the crazy things a user might do is cause an infinite loop in the
sandbox (e.g.: while 1 {}), so I'd like the master to be able to force
the sandbox to stop interpreting. I don't want to just delete/kill the
sandbox (because it may contain valuable state), but rather just zap it
into returning. Something along the lines of ctrl-C signal support in
TclX's shell, which terminates interpretation but doesn't kill the
process. Ideally the master's GUI could have a "stop" button for the
runaway sandbox.

Scott Redman wrote:
>
> There is no way to break the execution of a Tcl script
> in an embedded Tcl interpreter. (actually, there probably
> is, but it would involve a lot of work and would seriously
> slow down the interpreter).

Could you elaborate on this? Isn't there a reasonable point in the
Tcl_Eval main loop that could poll for an interrupt, without a severe
performance hit?

> You're best bet is to periodically call a function in
> you C++ code that checks a flag and halts the interp.

Could you elaborate on this as well? What's the right way to halt an
interp from C code?

FYI I'm using tcl82 on WinNT4.

Thanks very much for any help.
-- mew

Scott Redman

unread,
Dec 28, 1999, 3:00:00 AM12/28/99
to Mike Weiblen

> Scott Redman wrote:
> >
> > There is no way to break the execution of a Tcl script
> > in an embedded Tcl interpreter. (actually, there probably
> > is, but it would involve a lot of work and would seriously
> > slow down the interpreter).
>
> Could you elaborate on this? Isn't there a reasonable point in the
> Tcl_Eval main loop that could poll for an interrupt, without a severe
> performance hit?

Take a look at the TUBA debugger. It will cause a performance
hit.

You might be able to tweak the Tcl core to provide your own
hooks.

I haven't given signals any thought, but you will need to
run the "sandbox" in another thread.

> > You're best bet is to periodically call a function in
> > you C++ code that checks a flag and halts the interp.
>
> Could you elaborate on this as well? What's the right way to halt an
> interp from C code?

You cause the interp to exit (or return an error which you don't
catch) from inside your C/C++ function. It's tricky, but it can
be done.

-- Scott

Frederic BONNET

unread,
Dec 29, 1999, 3:00:00 AM12/29/99
to
Hi,

Mike Weiblen wrote:
> For my application I'd like to use two interpreters: a "master" for the
> main app and GUI, and a "sandbox" for the user to execute arbitrary
> code.

[...]


> One of the crazy things a user might do is cause an infinite loop in the
> sandbox (e.g.: while 1 {}), so I'd like the master to be able to force
> the sandbox to stop interpreting. I don't want to just delete/kill the
> sandbox (because it may contain valuable state), but rather just zap it
> into returning. Something along the lines of ctrl-C signal support in
> TclX's shell, which terminates interpretation but doesn't kill the
> process. Ideally the master's GUI could have a "stop" button for the
> runaway sandbox.

If your primary concern is about safety and not performance, you could
redefine the loop commands inside your slave interpreter that would link to
"safe" loops in the master interpreter. In your example, redefine the
"while" command so that it periodically calls update or performs other
checks such as user interrupt. It adds extra overhead but you can limit its
impact if you perform the check every 1,000 iterations for instance.

See you, Fred
--
Frédéric BONNET frederi...@ciril.fr
---------------------------------------------------------------
"Theory may inform, but Practice convinces"
George Bain

Mike Weiblen

unread,
Dec 29, 1999, 3:00:00 AM12/29/99
to
Frederic BONNET wrote:
> If your primary concern is about safety and not performance, you could
> redefine the loop commands inside your slave interpreter that would link to
> "safe" loops in the master interpreter. In your example, redefine the
> "while" command so that it periodically calls update or performs other
> checks such as user interrupt. It adds extra overhead but you can limit its
> impact if you perform the check every 1,000 iterations for instance.

That would do as a proof of concept, but a creative user could still
find ways to lock up the interp.

Scott Redman wrote:
> Take a look at the TUBA debugger. It will cause a performance
> hit.
> You might be able to tweak the Tcl core to provide your own
> hooks.

IIRC, TUBA is a pure-tcl implementation. IMHO the right place to hook
out-of-band interrupts is not in tcl, but within the interpreter core.
Sounds like might be fertile ground here, so I'll explore.

Thanks for the feedback.
-- mew

Peter V. Morch

unread,
Dec 29, 1999, 3:00:00 AM12/29/99
to
Hi,

I'd like to raise a warning with this approach:

Frederic BONNET wrote:
> If your primary concern is about safety and not performance, you could
> redefine the loop commands inside your slave interpreter that would link to
> "safe" loops in the master interpreter. In your example, redefine the
> "while" command so that it periodically calls update or performs other
> checks such as user interrupt. It adds extra overhead but you can limit its
> impact if you perform the check every 1,000 iterations for instance.

This could easily change the execution sequence of a script too with odd
side effects. E.g if you had a script like this pseudo code:

proc myWidgetSet {} {
prepare_widgets
set finished 0
while { ! $finished } {
set finished [do_a_widget]
}
finish_widget_setup
update
}

Here, with the original while implementation you can rely on update not
being called in the middle of the while loop, but if you change the
while implementation to call update in the middle of it, odd things
could happen. Of course, if you are coding your entire application with
this in mind, you could be OK, but if you are using 3rd party code, this
may or may not be a problem...

Depending on the application, you could have fileevent callbacks all of
a sudden firing in the middle of the while loop, or GUI interactions
etc. Any of with these could currently assume that myWidgetSet is fully
initialized and finish_widget_setup has been called. This assumption
won't hold with a rewritten "proc while", hence the risk of odd side
effects.

But I guess its a tradeoff between being able to break and living with
changed behaviour, or getting none of them! ;-)

Peter

Donal K. Fellows

unread,
Dec 29, 1999, 3:00:00 AM12/29/99
to
In article <386A5C4B...@sitera.com>, Mike Weiblen
<mi...@sitera.com> writes

>That would do as a proof of concept, but a creative user could still
>find ways to lock up the interp.

One of the most amusing alternatives, and among the hardest to block, is
the following little script:

set var "Mary had a little lamb; it's fleece was white as snow."
foreach dummy {1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0} {
append var " $var $var $var"
}

This will bring almost every computer system currently in existence
either to a shuddering halt, or will cause the process to crash
(depending on whether your OS supports per-process memory accounting!)
The only way to make this safe is to implement per-thread/interpreter
memory accounting, which is really tough to get right. CPU time is much
easier to limit...

BTW, AFAIK no programming language in the world actually supports this
properly, and yet it is crucial to providing a truly safe execution
environment that prevents a whole class of DoS attacks. Will Tcl get
there first?

Donal.
--
Donal K. Fellows (at home)
--
FOOLED you! Absorb EGO SHATTERING impulse rays, polyester poltroon!!

Alexandre Ferrieux

unread,
Dec 30, 1999, 3:00:00 AM12/30/99
to
Donal K. Fellows wrote:
>
> [Warp a string by 1L<<24]

>
> This will bring almost every computer system currently in existence
> either to a shuddering halt, or will cause the process to crash
> (depending on whether your OS supports per-process memory accounting!)
> The only way to make this safe is to implement per-thread/interpreter
> memory accounting, which is really tough to get right.

Really tough ? Granted, we need to add an extra field to allocated
blocks to hold their size, in order to keep track of the total amount
when they are freed (which is also of interest for introspection, see
[info memory]). But then, keeping a per-interp/thread/whatever count is
trivial. Or am I missing something ?

> BTW, AFAIK no programming language in the world actually supports this
> properly,

It is more a library than a language feature... Are you positive that no
GNUmalloc-alike supports the size field ?

> and yet it is crucial to providing a truly safe execution
> environment that prevents a whole class of DoS attacks. Will Tcl get
> there first?

Possibly. Does anyone out here remember the final conclusion about the
extra cost of this field ? (Yes I could look for [info memory] in
Deja...).

-Alex

Paul Duffin

unread,
Dec 30, 1999, 3:00:00 AM12/30/99
to
"Donal K. Fellows" wrote:
>
> In article <386A5C4B...@sitera.com>, Mike Weiblen
> <mi...@sitera.com> writes
> >That would do as a proof of concept, but a creative user could still
> >find ways to lock up the interp.
>
> One of the most amusing alternatives, and among the hardest to block, is
> the following little script:
>
> set var "Mary had a little lamb; it's fleece was white as snow."
> foreach dummy {1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0} {
> append var " $var $var $var"
> }
>
> This will bring almost every computer system currently in existence
> either to a shuddering halt, or will cause the process to crash

This is one of the many things which mainframes are much better at than
PCs simply because they have to be.

> (depending on whether your OS supports per-process memory accounting!)
> The only way to make this safe is to implement per-thread/interpreter

> memory accounting, which is really tough to get right. CPU time is much
> easier to limit...
>

Or, you could port Tcl to OS/390 or something similar which will do all
of that for you.

> BTW, AFAIK no programming language in the world actually supports this

> properly, and yet it is crucial to providing a truly safe execution


> environment that prevents a whole class of DoS attacks. Will Tcl get
> there first?
>

Is this something which a programming language needs to manage, or
is it something the OS needs to do ? IMO system programming
languages like C (Java not included in this list) need not concern
themselves with this but cross platform application languages like
Java and Tcl which have their own virtual machine do. Java in
particular needs to do this because of the way it uses threads.

Jeffrey Hobbs

unread,
Dec 30, 1999, 3:00:00 AM12/30/99
to Donal K. Fellows
"Donal K. Fellows" wrote:
...

> The only way to make this safe is to implement per-thread/interpreter
> memory accounting, which is really tough to get right. CPU time is much
> easier to limit...
>
> BTW, AFAIK no programming language in the world actually supports this
> properly, and yet it is crucial to providing a truly safe execution
> environment that prevents a whole class of DoS attacks. Will Tcl get
> there first?

Actually, since you can make all allocs and free go through
ckalloc/Tcl_Alloc and ckfree/Tcl_Free, you could actually
track mem usage overall without too much problem. Narrowing
it to per interp/thread would be harder, but feasible.

--
Jeffrey Hobbs The Tcl Guy
jeffrey.hobbs at scriptics.com Scriptics Corp.

Darren New

unread,
Dec 31, 1999, 3:00:00 AM12/31/99
to
Donal K. Fellows wrote:
> One of the most amusing alternatives, and among the hardest to block, is
> the following little script:
>
> set var "Mary had a little lamb; it's fleece was white as snow."
> foreach dummy {1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0} {
> append var " $var $var $var"
> }

But the cool thing about Tcl is that it *is* possible to block, simply by
rewriting "append" in the safe interpreter. Try *that* in Java. ;-)

--
Darren New / Senior Software Architect / Dai Ye
San Diego, CA, USA (PST). Cryptokeys on demand.
Wenglish: "What's a sud?"

Donal K. Fellows

unread,
Jan 2, 2000, 3:00:00 AM1/2/00
to
In article <386C103A...@san.rr.com>, Darren New <dn...@san.rr.com>
writes

>But the cool thing about Tcl is that it *is* possible to block, simply by
>rewriting "append" in the safe interpreter. Try *that* in Java. ;-)

I hate to be the bearer of bad news, but it is possible so long as you
have any mechanism whereby you can set a variable to a string. It will
work perfectly well with a few [set]s alone, though I don't fancy
writing the code here (too boring!) Or even just bounded-depth
recursion with no variable assignment commands at all. The thing is
this is not really a command-related problem, but rather a general
problem with variables and particularly strings. The only place it
makes sense to do memory limiting is at a very low level indeed, but
then you run into problems when you consider inter-thread communication;
who owns the memory then, and what about when a master interpreter is
performing some operation on behalf of a safe slave?

Not that you can't block it if you are really determined; you need to
hide every command that can update a variable and every command that
could induce a loop. There's very little you can do without [while],
[proc], [set], etc. but such an incredibly restricted interpreter is
sometimes useful...

Darren New

unread,
Jan 4, 2000, 3:00:00 AM1/4/00
to
Donal K. Fellows wrote:
>
> In article <386C103A...@san.rr.com>, Darren New <dn...@san.rr.com>
> writes
> >But the cool thing about Tcl is that it *is* possible to block, simply by
> >rewriting "append" in the safe interpreter. Try *that* in Java. ;-)
>
> I hate to be the bearer of bad news, but it is possible so long as you
> have any mechanism whereby you can set a variable to a string.

You can rewrite "set" and "append" and all that to keep track of how much
memory the interpreter is using. You can rewrite "proc" to throw an error if
the arguments are too long or recursion is too deep. You can rewrite "while"
to not loop more than a certain number of times.

Ugly, but doable, I think. Certainly it's *better* to have it in the core.
The only thing you might not be able to limit is the $-substitutions, so
someone might be able to do [$y$y$y$y$y$y$y$y$y$y$y$y$y] or something and
suck up a bunch of memory for the intermediate result. Of course, you could
write a program to make a string as long as that, so ... hmmm ... Well,
you're closer than you are in Java.

> Not that you can't block it if you are really determined; you need to
> hide every command that can update a variable and every command that
> could induce a loop. There's very little you can do without [while],
> [proc], [set], etc. but such an incredibly restricted interpreter is
> sometimes useful...

Yeah, that's what I meant. But you don't have to block it. You just have to
check how much memory is being used and block it if it's too much, or block
it if the loop goes on too long, just like the core blocks excessive
recursion even if it's correct.

Dariusz Bednarczyk

unread,
Jan 4, 2000, 3:00:00 AM1/4/00
to
My solution is a rewriten in VC++ while command (see extra code).


int WhileCmd(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
// ###### extra code start ######
CSingleLock sLock(&mutexMtCmd);
sLock.Lock();
BOOL bBreak = bTclBreakFlag; // bTclBreakFlag is set in another
thread
sLock.Unlock();
if(bBreak)
return(TCL_BREAK)
// ###### extra code end ######


int result, value;

if (argc != 3)
{
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " test command\"", (char *) NULL);
return TCL_ERROR;
}

while (1)
{
result = Tcl_ExprBoolean(interp, argv[1], &value);
if (result != TCL_OK)
{
return result;
}
if (!value)
{
break;
}
result = Tcl_Eval(interp, argv[2]);
if ((result != TCL_OK) && (result != TCL_CONTINUE))
{
if (result == TCL_ERROR)
{
char msg[60];
sprintf(msg, "\n (\"while\" body line %d)",
interp->errorLine);
Tcl_AddErrorInfo(interp, msg);
}
break;
}

// ###### extra code start ######
sLock.Lock();
bBreak = bTclBreakFlag; // bTclBreakFlag is set in another thread
sLock.Unlock();
if(bBreak)
return(TCL_BREAK)
// ###### extra code end ######
}
if (result == TCL_BREAK)
{
result = TCL_OK;
}
if (result == TCL_OK)
{
Tcl_ResetResult(interp);
}
return result;
}

When I set the bTclBreakFlag in another thread then the interpreter
stops with status 1 and result string:

invoked "break" outside of a loop


In the same way you can modify other Tcl control commands.


Dariusz Bednarczyk
dare...@poczta.wp.pl

0 new messages