[comp.lang.forth] Preventing Crashes

willner@cfa.harvard.EDU (Steve Willner) (07/22/87)

In article <sUzP8Vy00Us=wNM0-x@andrew.cmu.edu>, 
lord+@andrew.cmu.edu (Tom Lord) writes:
> 2) What work has been done on `crash proof' forth systems?  At what cost to
> execution speed?

Magic/L is a forth-like language with memory management that should
make it nearly crash-proof.  But I've never used it myself, so don't
know how it actually works out in practice.  The vendor is Loki
Engineering, 55 Wheeler St., Cambridge, MA 02138, phone 617-576-0666.
I believe they have a Unix-compatible version of some sort, but you
should contact them for more information.

A forth _application_ (as distinct from a _system_) can be
essentially crash-proof if the programmer does a good job.  I'd
suggest four areas for attention.  None of these compromises
execution speed within the program.

1) The main thing is to prevent storing garbage in unfortunate
locations (like the dictionary).  At a minimum, write extra words so
that users never have to use "!" directly.  If you want to be even
more careful, you can end your program by redefining "!" and related
words so certain parts of memory cannot be changed.  In practice, I
have not found this extreme to be necessary.

2) The second most important thing is to make unavailable any words
that are dangerous (e.g. words that should only be called after
something else has happened.)  A variety of techniques can be used,
depending on the system.  At a minimum, give dangerous words names
that are not likely to be typed by accident.  Most systems have a
smudge bit or some way of (reversibly) making words unrecognizable;
use it to prevent a user from accessing dangerous words.  An easy
alternative is to redefine dangerous words after your program has
used them for the last time so the new word just gives an error
message.  My own technique is to name dangerous words in lower case
and to set the system bit that converts terminal input to upper case.
(Telling users to always set the "caps lock" on the terminal would be
just as effective but would prevent users from typing readable text.)
Or if you're really paranoid, write a parser that only recognizes and
executes selected, safe words and does so only after checking that
all arguments (on stack or elsewhere) are appropriate.

3) Limit the damage.  If "@", "!", or any other words don't check the
stack before or after every execution, redefine them at the end of
the application code so they do.  That way a user-written routine
that makes a stack error will abort immediately rather than calling
more words with wrong values on the stack.

4) For a multitasking system, make sure users can't do something that
hogs the CPU.  Our system, for example, can only switch processes
when it encounters a ";", and all long loops must therefore contain
at least one colon-word so the process can be suspended.  The
solution is to make sure that all loops defined within the
application contain a colon-word, and at the end redefine "DO",
"BEGIN", etc. so they always compile "DUM" (a no-op colon word)
within any loop a user might execute.

These suggestions are not perfect protection, but as a practical
matter they have worked very well in preventing crashes caused by
careless users.  Now if only I could figure out how to prevent
crashes from broken hardware and from bad programs :-) 
-- 
Steve Willner              Phone 617-495-7123        Bitnet: willner@cfa1
60 Garden St.              FTS:      830-7123         UUCP:   willner@cfa
Cambridge, MA 02138 USA  Telex:  921428 satellite cam

mj@myrias.UUCP (Michal Jaegermann) (07/23/87)

    To all fine advice how to make Forth *application* crash-proof one
may add the following:
Strip headers from all words but user interface.  You will save a lot of
memeory, which now is wasted, and nobody will find anything which s/he is
not supposed to see :-)..

     Michal Jaegermann
     Myrias Research Corporation
     ...ihnp4!alberta!myrias!mj

andrew@teletron.UUCP (Andrew Scott) (07/24/87)

In article <629@cfa.cfa.harvard.EDU>, willner@cfa.harvard.EDU (Steve Willner) writes:
> 
> 4) For a multitasking system, make sure users can't do something that
> hogs the CPU.  Our system, for example, can only switch processes
> when it encounters a ";", and all long loops must therefore contain
> at least one colon-word so the process can be suspended.  The
> solution is to make sure that all loops defined within the
> application contain a colon-word, and at the end redefine "DO",
> "BEGIN", etc. so they always compile "DUM" (a no-op colon word)
> within any loop a user might execute.

Another technique is to allow the system to switch processes during the
call to NEXT at the end of every primitive.  This works especially well
if NEXT is implemented as an indirect jump through an address register -
just patch the register value to point to the context switching code and
restore it for the next process.  Then, the only restriction is to forbid
primitives to use up a lot of CPU time.  Also, any shared resource used
by multiple processes may be accessed from within a primitve without having
to use any kind of semaphores.

These techniques assume that there is some kind of process scheduler.
Many FORTH multitasking systems (especially process control type systems)
use cooperative multitasking schemes.  That is, it is up to each process
to relenquish CPU control during long loops etc.  This reduces quite a bit
of overhead.  Note that *multiuser* systems should still have some kind of
scheduler to guard against runaway processes.

	Andrew