Complete.Org: Mailing Lists: Archives: freeciv-dev: September 1999:
[Freeciv-Dev] Re: Default/optional entries in rulesets
Home

[Freeciv-Dev] Re: Default/optional entries in rulesets

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: Artur Biesiadowski <abies@xxxxxxxxx>
Cc: Freeciv Dev <freeciv-dev@xxxxxxxxxxxx>
Subject: [Freeciv-Dev] Re: Default/optional entries in rulesets
From: Daniel Burrows <Daniel_Burrows@xxxxxxxxx>
Date: Sun, 19 Sep 1999 17:08:47 -0400

On Thu, Sep 16, 1999 at 10:25:14AM +0200, Artur Biesiadowski was heard to say:
> User provided scripts is very different thing. We would need security
> layer for system access, security layer for game interface so player
> would not know/change too much and quite smart overseer program that
> would ill runaway processes, ones that allocate too many memory etc.
> Debuggin such programs could be horrible thing. Anyway it is not
> possible with guile, not with java (no way to control thread cpu/memory
> usage except imposing global limit on everything), I don't know about
> the others, but I really doubt if any mainstream language has all of it
> implemented.
> 
> It is a lot easier/faster to stay server side only, without any checks,
> aborting on error etc.
> 
> There was a project called ClientX I think that was meant to include
> client side AI and scripting. I think that hooks you are talking about
> should be done in client. Of course it means that they will not always
> run fast enough, for example to attack unit running by, but are a lot
> easier to control for resources (it is client machine after all) and for
> knowledge (client do not know data player shouldn't know)
> 
> 
> Artur

  Just for the sake of argument :-)..
  
  We already have user-provided scripts.  We just call them 'packets'.  All
that a 'normal game packet' is is a script with a special trigger value
(immediate execution) and an atomic operation specified.  I was originally in
favor of Guile, but this design is so nice that I'd be willing to see Freeciv
get its own language if we could pull it off while still having a fairly general
language (so it could be used for unintended things, just not
security-comprimising unintended things :) ).  In fact, I may just go and write
a.. (no, I can't, I don't have time :-( )

  Anyway, there are only a few extensions to Scheme that would be needed for
the unauthorized-code-access bit -- and you wouldn't even have to do all of
them.  Just pick a couple.

  -> Somehow allow a 'security level' to be set in lambda calls.  Code below
    this security level (although perhaps numerically above -- ie, 0=no
    restrictions, 1=some, etc) would be unable to call this procedure (an error
    would be thrown).  This would ensure that internal routines aren't called
    by scripts, except perhaps through 'gateway' functions.  (this is roughly
    equivalent to permissions and suid, and we all know the problems with
    that.. :) )  For example, all filesystem code would be denied to
    normal-level scripts.  This has the disadvantage that handling of global
    variables is tricky -- you probably want to just restrict alteration of
    them to 0-level scripts.  This also is a problem if set! (ick) is allowed,
    since you could do:
       ((lambda (x) (set! x (lambda () (system "rm / -rf")))) turn-end)
    Not so great.

  -> Make a way to have multiple toplevel environments.  This seems like a
    sensible and maybe even doable solution to me; the server's script would
    have a normal global environment with all the file routines and so on, while
    the client script's environment would only have a selected set of procedures
    and variables, perhaps entirely disjoint from the server's global
    environment.  The major problem here is that you'd want to have
    well-defined points at which you transitioned from one global environment
    to another -- my feeling is that it should be stored on the stack so
    it's effective for all subsequent calls.  This would be sufficient to
    entirely restrict Scheme scripts from making calls to procedures they
    shouldn't -- a Scheme script cannot perform actions except through
    procedures and macros, which are looked up in the environment, and by
    making this restrictive procedure call unavailable in the new environment
    you can make it one-way.  The available procedures would be utility routines
    such as cons, cdr, and so on, and game routines -- we could start with
    simply adding hooks for each current packet type, which would make security
    no worse than it currently is.
    (the C code would still be executing in an unrestricted environment, but
     I'm assuming that the C code is correct -- if it isn't you're screwed
     whether you're using scripts or packets)
     This is analagous to chroot and seems to me to be reasonable.  You can
    keep track of whose turn it is with closures -- make the visible
    routines be defined like this, assuming that the-player is the current
    player's Scheme identification:
    (define move-unit (make-restricted-procedure the-player move-unit-real))
    where make-restricted-procedure is defined with:
    (define make-restricted-procedure
       (lambda (player real-proc)
          (lambda args (apply real-proc (cons player args)))))

      You could also use a global variable that's set when you enter the
    barriered environment, but I like this approach better.

  It might even be possible to hack the second option into a common Scheme
interpreter.

  The other problem you mentioned was a DOS-type attack..eg, I send the
following to the server:

(define evil-hack (lambda args (apply evil-hack (cons '() args))))
(set-turn-done-hook! evil-hack)

  and either make it spin forever or make it run out of memory.  I think that
this is the real obstacle that might make it difficult to do this properly.
However, a first approximation would be to simply install a timer signal for,
say, 10 seconds from now before executing a user script; if it's taking longer
trigger an error and abort it.  It's probably a good idea in general to delete
scripts that have errors, and this would be no different.  This would catch the
worst CPU-spinning stuff (hopefully allowing the server admin to kick the user
off) and I'll be surprised if you can overflow someone's memory in 5 seconds,
especially from a Scheme script.  The problem of course is that people might
not like this restriction if they want to write an AI player in Scheme :) --
but most triggers should be simple tasks anyway.
  I think this could be done with alarm() and the Scheme interpreter's error
routine, but alarm() may already be used in Freeciv and there could be issues
with the error routine jumping out of a signal handler.  We could also
use threads (assuming our scheme interpreter is threadsafe) -- this is obviously
pseudocode:

void *executor(SCM procname, SCM environment, SCM args, SCM *retval)
{
  *retval=scheme_eval_in_environment(scheme_cons(procname,args));
}

SCM run_scheme_proc(SCM procname, SCM args, int user_as)
{
  thread_type interpreter,watcher;
  SCM retval,jail;
  int ok=1;

  jail=set_up_environment_jail(user_as);

  interpeter=spawn {/* Set catch here */ executor(procname,jail,args,&retval);
                    /* Now set ok=0 if we caught an error */};

  watcher=spawn {sleep(5); thread_kill(interpreter);}

  thread_join(interpreter);
  thread_kill(watcher);
}

  Daniel
-- 
  Going on about Dungeons and Dragons being tools of the devil is like guarding
the door while *it* is coming up through the floorboards...
  The Demon Lord of Hla'Siloth may want to cut your soul up into a thousand
pieces, but at least he won't try to convince you that you haven't got one.

  -- [ approximately ] Terry Pratchett, _Johnny and the Dead_

[Prev in Thread] Current Thread [Next in Thread]