Simply put, I am wondering where the usability of Reia currently is at.
Is it at the level where reliable applications/modules can be created or
is it still in the fiddly prototype stage. I'm not after a commitment
the system won't change (far from it), just wanting to know if the
primary features are implemented (mutable variables, working classes,
exception handling, etc). Also, though I don't see it mentioned anywhere
in the planned feature list, I'm curious to know if Reia could be
"sandboxed" (i.e. prevent it from accessing modules it doesn't
'explicitly' know about).
The reason for the query is a wager I made in the throws of
alcohol-induced over-confidence. I was having a night out with a mate
who works in the games industry and we were comparing our favourite
"non-standard" languages. His is Meta-Lua (a Lua extension) & mine is
currently Erlang. While he acknowledged that the Erlang VM is capable of
handling a very large number of processes, he is of the opinion that the
Erlang VM could never be used to create a viable gaming back-end because
it is too hard to "script" things for. I thought otherwise and now there
is $300 riding on me being able to put together a basic game server that
can be scripted by someone not "enlightened" in the ways of Erlang.
While I suppose Virding's LFE could technically fit this bill (being a
subset of Lisp), I was hoping Reia would be ready enough to take on the
challenge.
I have two months (in my spare time) to put this together, so I can wait
perhaps another month before I need to get stuck into Reia seriously,
but I wanted to know whether I should start looking elsewhere or putting
together a much more basic scripting language that can be interpreted by
Erlang (slow, and more work than I really want to do for $300!). My
efforts are, at best, only going to garner me $300; but that's more than
enough to celebrate my winning by shouting a round of beer!
--
Regards,
Benjamin Tolputt
Analyst Programmer
Well, unless Reia is up to the task, it would be a waste of time (and
worse, a false hope to those on the list) to create any repo for the
project until I'm sure I'm using the Reia language. On the other hand,
if Reia IS indeed ready enough for a prototype use, I can see no reason
why I shouldn't put it up on github (or similar). As I mentioned, it
only needs to be functional enough to win the bet. I bet alot of people
would love hacking on the result. Who knows, it could even become a
viable project in it's own right (though I'm not counting on it *laugh*)
> I always new that alcohol is the most effective engine for progress! :)
>
Well, it makes people like me make claims they'll do alot to prove
valid. Were it not for my mouth running off with me, I'd not be doing
this :) Still pride & alcohol are great motivators when properly harnessed.
Well, unless Reia is up to the task, it would be a waste of time (and
worse, a false hope to those on the list) to create any repo for the
project until I'm sure I'm using the Reia language. On the other hand,
if Reia IS indeed ready enough for a prototype use, I can see no reason
why I shouldn't put it up on github (or similar).
As I mentioned, it only needs to be functional enough to win the bet. I bet alot of people
would love hacking on the result. Who knows, it could even become a
viable project in it's own right (though I'm not counting on it *laugh*)
Damn, I was afraid of that. Thanks for your prompt & straight-forward reply.
> Yeah I'm not sure if it's up to what you're describing yet. Maybe in
> a few months...
>
> It has the basic set of features you listed (destructive variable
> assignments, mostly-working classes, and very gimpy exception
> handling) but I'm guessing you'll need much more than just that.
Yeah, some form of sand-boxing would be necessary, along with some
method of interrupting the object execution & state saving. Thanks
anyway mate, I'll see if there is a less expressive language I can use :)
Yeah, some form of sand-boxing would be necessary, along with somemethod of interrupting the object execution & state saving. Thanks
anyway mate, I'll see if there is a less expressive language I can use :)
Sure thing, I a little more in the way of clarification is provided
below on each of the points mentioned. I use Lua as the "example
implementation" only because it is the only scripting language I have
experience with.the internals of. I am familiar with others (such as
Python & Ruby) but never played with their virtual machine internals *shrug*
--Sandboxing--
Sand-boxing is where the script code is only able to access
functionality explicitly given to it. Proper sand-boxing allows an
application to be scripted by untrusted sources (i.e. locks down any
ability to adversely affect the underlying system/software running the
script) and usually includes locking down access to the virtual machine,
file system, etc. I'd be happy with Reia classes having access limited
to a predefined set of modules added either at compile time or (in a
perfect world) at the time of creating the object / running the script.
The scripts for the
Lua is a good example of a language that can be sandboxed as all the
module/library accesses must go through a set of functions that can be
overridden prior to launching the untrusted script. If I don't want the
person accessing one of the globally defined modules - I remove the
reference to it in the global symbol table and lock it down in an
overridden "requires" function. Given Reia, currently, seems able to
access any module it can name (as per standard Erlang) - this would be
something I think would need to be built in explicitly (perhaps a
compile time option to allow trusted Reia scripts to ignore the
performance penalty of checking the module against the allowed list?).
--Interrupting Object Execution--
This might be something that can be built into my "base" classes, though
would be an interesting thing to add to the basic build. As I understand
it, Reia objects are mapped to an Erlang process (using a gen_server?)
and, as such, can be thought of as running constantly until killed in
some fashion. Trusted scripts can, of course, do this without a problem
but it is not hard to envisage a malicious (or simply less competent)
developer creating an infinite loop of some form that sucks up CPU
and/or prevents the object/process from receiving further messages. A
way to control this would be good.
Lua has debug facilities that allow for interrupting a script after a
given number of VM instructions processed. There are one or two minor
gotcha's that need to be handled, but it allows for runaway scripts to
be logged and/or crashed should they become a problem. I'm not sure
exactly how Reia could implement this (perhaps a "function call
count"?), but once you start "scripting" something in a production
environment - you need to have some way of ensuring it doesn't strip the
overall system of CPU cycles.
--State Saving / Checkpoint--
This is more an application specific thing, but would be a nice general
feature in Reia as well. Primarily, as the individual objects are
"processes", saving a the state of the world / application needs to be
done using some "checkpoint" system in order to keep the state sane &
consistent. As I said, this might not be something that can / should be
done in the language itself, but at the very least - having access to
the object state at a given point in time could allow for some
application-specific "base class" to implement a checkpoint system
external to Reia.
Lua is able to do this quite easily as it is single-threaded and
stopping, saving, & restoring the virtual machine (and hence object)
state at any point in time does not have to worry about operations
occurring concurrently to the serialisation process.
Thanks at the very least for looking into this. I'm getting the feeling
I was over optimistic about the status of Reia (at least in regards to
using it in my wager), but it is nice to know the head honcho of the
language is at least interested :)
Sure thing, I a little more in the way of clarification is provided
below on each of the points mentioned. I use Lua as the "example
implementation" only because it is the only scripting language I have
experience with.the internals of. I am familiar with others (such as
Python & Ruby) but never played with their virtual machine internals *shrug*
--Sandboxing--
Sand-boxing is where the script code is only able to access
functionality explicitly given to it. Proper sand-boxing allows an
application to be scripted by untrusted sources (i.e. locks down any
ability to adversely affect the underlying system/software running the
script) and usually includes locking down access to the virtual machine,
file system, etc. I'd be happy with Reia classes having access limited
to a predefined set of modules added either at compile time or (in a
perfect world) at the time of creating the object / running the script.
The scripts for the
Lua is a good example of a language that can be sandboxed as all the
module/library accesses must go through a set of functions that can be
overridden prior to launching the untrusted script. If I don't want the
person accessing one of the globally defined modules - I remove the
reference to it in the global symbol table and lock it down in an
overridden "requires" function. Given Reia, currently, seems able to
access any module it can name (as per standard Erlang) - this would be
something I think would need to be built in explicitly (perhaps a
compile time option to allow trusted Reia scripts to ignore the
performance penalty of checking the module against the allowed list?).
--Interrupting Object Execution--
This might be something that can be built into my "base" classes, though
would be an interesting thing to add to the basic build. As I understand
it, Reia objects are mapped to an Erlang process (using a gen_server?)
and, as such, can be thought of as running constantly until killed in
some fashion. Trusted scripts can, of course, do this without a problem
but it is not hard to envisage a malicious (or simply less competent)
developer creating an infinite loop of some form that sucks up CPU
and/or prevents the object/process from receiving further messages. A
way to control this would be good.
Lua has debug facilities that allow for interrupting a script after a
given number of VM instructions processed. There are one or two minor
gotcha's that need to be handled, but it allows for runaway scripts to
be logged and/or crashed should they become a problem. I'm not sure
exactly how Reia could implement this (perhaps a "function call
count"?), but once you start "scripting" something in a production
environment - you need to have some way of ensuring it doesn't strip the
overall system of CPU cycles.
--State Saving / Checkpoint--
This is more an application specific thing, but would be a nice general
feature in Reia as well. Primarily, as the individual objects are
"processes", saving a the state of the world / application needs to be
done using some "checkpoint" system in order to keep the state sane &
consistent. As I said, this might not be something that can / should be
done in the language itself, but at the very least - having access to
the object state at a given point in time could allow for some
application-specific "base class" to implement a checkpoint system
external to Reia.
Lua is able to do this quite easily as it is single-threaded and
stopping, saving, & restoring the virtual machine (and hence object)
state at any point in time does not have to worry about operations
occurring concurrently to the serialisation process.
Thanks at the very least for looking into this. I'm getting the feeling
I was over optimistic about the status of Reia (at least in regards to
using it in my wager), but it is nice to know the head honcho of the
language is at least interested :)
Oh, I 100% agree with you that the "game server" is a completely
separate part of the equation, but once you link Lua, Python, etc into
the mix - you lose the best feature in Erlang's arsenal. Which is, of
course, it's ability to scale massively in terms of concurrent processes
(which map to massive numbers of concurrent objects in Reia).
Just like Lua is an efficient, light-weight layer for scripting
functionality into C programs, I was hoping to use Reia in the same way
for Erlang. I suppose the difference I didn't consider is that Reia is
not so much a "scripting language" as it is a "language that compiles to
native Erlang". As such, there really isn't a "host" layer that can
manipulate the running of Reia-compiled Erlang processes/applications.
> As for the current status of Reia, Its perfect for prototyping. I
> really want to see the testing library Reia/Behave to be in better
> shape for testing, testing improves confident.
>
The only thing that bugs me (currently) about setting up Reia for
prototyping is the need to install Ruby in order to use "rake". It would
be a much "cleaner" installation if I didn't need Ruby solely install a
scripting component/application for Erlang.
That said, I'm looking at what can be done in Reia in order to speed up
development of this project as I don't have much spare time over the
next two months (real world job & family to take care of). Anything Reia
can help me with is going to be a big bonus.
> PS: What kind of game u are trying to build? ping me via my email we
> might have something in common.
>
Check your email :)
I'm a big fan of Lua too. Were it not for Erlang's proven scalability, I
would happily agree that a combination of Lua & C++ would be better for
the development of a game server. Unfortunately for me (& my big mouth),
Erlang is simply incredible in this regard and with it's capability of
talking to other nodes in a "cluster-like" manner - it wins hands down
in everything but "script-ability". Hell, I can't think of another
language that would allow you to swap in new functionality while players
are still having at each-other :)
> --Sandboxing--
>
> Honestly this sort of trust model was nowhere in my head. I'm pretty
> much aiming for an interface that gives as much power to the user as
> possible, rather than trying to restrict it.
I gathered as much and can see the utility of a language that gives you
access to everything Erlang can. I suppose the difference comes in our
perception of what a "scripting language" is. In my mind, a "scripting
language" is something hosted by an application and hence can be limited
as much as the host wants. I don't particularly want to restrict Reia in
all (or even most) circumstances, but for "scripts" (as opposed to
standard developed code) - I think it is a must.
There are a couple of ways in which I think Reia could unobtrusively add
features to allow sand-boxing for those that want it and prevent
measurable performance loss for those that don't. Firstly, a list of
natibve modules accessed by a Reia object could be made available by the
compiler (say a list that could be obtained from the class definition?).
This way the list of modules could be checked before creating/running a
Reia Object for security violations. The other way would be to have an
optional "hook" fun passed in at object creation that would be called
prior to an attempt to access an Erlang module. If the fun is valid
(i.e. is an actual function not an atom, tuple, etc) - the Reia object
calls that to know whether it is allowed to use the module and on a
result of success uses it, crashing in standard Erlang/Reia fashion if
not allowed.
What are your thoughts on these features? They seem to me to be easy
enough to build into the language and relatively performance negligible.
> By virtue of the Actor model, you will only be able to communicate
> with objects whose IDs you are able to ascertain, but that's pretty
> much where the restrictions end.
On this idea, I realise that Reia objects are, in fact, processes and I
assume the "object ID" is actually a process identifier right? This
seems, to me, like we're almost at the "security by capability" concept.
If you don't know the object - you can't call it to do anything. What
would be nice to have (again, possibly in an optional manner) is a
"shim" that has the list of functions that can be called on the object.
That is, you are not only passing over a process identifier but an
interface or "capabilities contract" which specifies explicitly the only
operations allowed on the Reia object. This would seem to me to be a
"filter" process that takes the incoming message, checks that it matches
against the allowed capabilities, and fires back an exception on fail or
passes the message on if allowed.
Again, any thoughts on this would be appreciated.
> --Interrupting Object Execution--
>
> Erlang tracks "reductions" (i.e. function calls) of various processes
> and it wouldn't be too hard to make Reia introspective enough to track
> this as well.
That would be nice to have. The best thing I can think of is to have
some fun passed in on object creation that is called every "X
reductions" to allow for reporting on CPU hogging processes. How this is
handled is probably application specific, but having the capability to
do this would be a great leap forward for analysing "scripted" processes
that may not necessarily have a decent programmer behind them :)
> --State Saving / Checkpoint--
>
> I've seen people requesting to serialize an entire process on the
> erlang-questions mailing list, but I don't believe it's currently
> possible. Perhaps if an appropriate use case were given. Reia will
> rely on Erlang/OTP support in this regard.
It is currently possible to hibernate a process (using
erlang:hibernate/3 or the proc_lib alternative which adds a catch around
the process). The OTP gen_server also allows for a "hibernate" return
along with the state to allow the process to be saved and awoken by
Erlang when it receives a message. This, of course, drops the call-stack
but seems to be the best method currently available.
Saving the "execution state" (i.e. call-stack, process mailbox, etc) is
not only impossible but I think should remain so. It is unlikely that
the execution state could survive a code hot-swap and/or being restored
at a later date from storage and carry on without some problems. So long
as the internal state of the process (and hence, I assume, the Reia
object internals) can be saved - that would be enough.
Providing true capabilities would be a huge win for Reia, IMO.
Providing true capabilities would be a huge win for Reia, IMO.
I think there is an easy solution to the capabilities problem, at least
so long as we trust a process id cannot be forged, and that is by adding
a separate "filter process" to the equation.
Let's illustrate with an example. Let's say you have a Reia object with
methods A, B, & C. You want to give another object (or Erlang process)
access only to methods A & C. The easiest thing to do is create another
"shim" process with the sole task of passing on calls to methods A & C
to the Reia object whilst returning {unfound_capability, ...} or similar
for all other messages. This could easily be tied into the Reia language
itself by making these "shim" processes the equivalent to Java's
"interfaces". We add an "implements InterfaceA" to the Reia class
declaration and whenever we cast the object as the interface in code -
Reia transparently returns the shim process rather than the Reia object
process. This prevents nasty OO hacks such as getting an interface and
trying to "upcast" to the real object.
What do you think?
I think there is an easy solution to the capabilities problem, at least
so long as we trust a process id cannot be forged, and that is by adding
a separate "filter process" to the equation.
Let's illustrate with an example. Let's say you have a Reia object with
methods A, B, & C. You want to give another object (or Erlang process)
access only to methods A & C. The easiest thing to do is create another
"shim" process with the sole task of passing on calls to methods A & C
to the Reia object whilst returning {unfound_capability, ...} or similar
for all other messages. This could easily be tied into the Reia language
itself by making these "shim" processes the equivalent to Java's
"interfaces". We add an "implements InterfaceA" to the Reia class
declaration and whenever we cast the object as the interface in code -
Reia transparently returns the shim process rather than the Reia object
process. This prevents nasty OO hacks such as getting an interface and
trying to "upcast" to the real object.
What do you think?
The process ID cannot be forged on a local node, however it could be forged on a remote node.
Okay, so we have a delegator object/process (let's call it D) that only delegates methods A&C, invoking the methods on the real object/process (let's call it R).
Is your goal to completely prevent someone from calling R? R can still be discovered through reflection e.g. erlang:processes(), although figuring out that D is calling R might be tricky. It's also possible to brute force search the process space e.g. list_to_pid()
My goal is only to secure Reia objects (& their methods) from being insecurely used by other Reia objects in the system. Your example of using erlang:processes() & erlang:list_to_pid() as a way around any security that can be built into the Reia objects is EXACTLY why I want to have some way of optionally limiting Reia's access to "native" Erlang modules. I want to use Reia both as an "unsecured" language for writing parts of a server application AND as a way in in which "secured" scripts (also written in Reia) can be loaded and sand-boxed in said application. By making adding compilation flags and/or optional hooks which are called only on the "secure" Reia - the coder can get the best of both worlds.
Thoughts, comments, ridicule?
An interesting argument, for sure, but I think ultimately a fruitless
one. Once you give a language the kind of reflective capabilities being
talked about here - you lose the security inherent in hiding details
from code using an object.
To use an analogy, a criminal organisation cannot walk into a police
station and demand the details on someone in witness protection. The
"interface" they are provided (a faceless officer) has a set of rules in
place to stop it from happening. Now said criminal organisation decides
to find out who the faceless officer is. Now instead of a strong,
rules-following officer; we have a young man with a family (a new, less
restrictive "interface" to the criminals).
Code is much the same way. Let's say I give a Reia object a
"ReadOnlyFileSystem" object. Malicious code that doesn't particularly
like the "read only" limitations it implies can try to get around this
by looking inside the object for the "ReadWriteFileSystem" object used
to get access to the files (or some similar hack). Sure, this is added
flexibility to trusted code (I can check out the internal state of the
object, see what other functions could have been called, etc), but an
absolute nightmare for code that is foreign to our internal developers.
It's even worse when you consider the fact that I don't even need to
know the internals of my passed in "FileSystem" object when I can simply
access the underlying Erlang libraries and hack to my hearts delight
amongst code that hasn't even heard of the word "security".
Another thing to consider is how often we get bad code in typical OO
languages because we can break out of the mould a library has exposed to
us for use. Let's say the "ReadOnlyFileSystem" wasn't actually a facade
object interacting with a separate "ReadWriteFileSystem" object, but was
in fact some form of "interface". In other words, the
"ReadOnlyFileSystem" was in fact a "ReadWriteFileSystem". In C++, Java,
(I assume) Clojure, etc it is trivial to simply "upcast" the object and
get access to functionality we were not meant to have. Makes for more
flexible programming, sure; but it also violates expectations the
module/library we were using has made and might (generally some time
later) crash the program unexpectedly when this violation is affected by
the module/library code. In Python, JavaScript, etc - it's even easier
because you can just try to use the "writeFileToDrive(...)" function,
regardless of the "class" passed to you.
I am of the mind that reflection is a great thing - provided it only
reflects what the library programmer wants to be seen. For example, to
carry on with my idea of a "Reia Interface", the interface for an object
(having state variables A, B, &, C and functions X, Y, & Z) only exposes
to reflection what is allowed to be used on the interface (i.e. it's
capabilities); say, get_value(A), set_value(B), & use_method(Z). An
"unsecured object" could expose everything (i.e. the object IS the
interface). If necessary, an "interface to Reia object" process could be
automatically started as a part of running Reia in Erlang, with every
interface created adding it's pid & the mapped object pid into this for
a secured "Reia Debugger" process to access for looking at the
underlying object.
The concept means an extra process for every interface exposed (& used)
on a Reia object but, given this is an Erlang scripting language, that
doesn't really seem to be a big issue :)
An interesting argument, for sure, but I think ultimately a fruitless
one. Once you give a language the kind of reflective capabilities being
talked about here - you lose the security inherent in hiding details
from code using an object.
Another thing to consider is how often we get bad code in typical OO
languages because we can break out of the mould a library has exposed to
us for use. Let's say the "ReadOnlyFileSystem" wasn't actually a facade
object interacting with a separate "ReadWriteFileSystem" object, but was
in fact some form of "interface". In other words, the
"ReadOnlyFileSystem" was in fact a "ReadWriteFileSystem". In C++, Java,
(I assume) Clojure, etc it is trivial to simply "upcast" the object and
get access to functionality we were not meant to have.
I am of the mind that reflection is a great thing - provided it only
reflects what the library programmer wants to be seen. For example, to
carry on with my idea of a "Reia Interface", the interface for an object
(having state variables A, B, &, C and functions X, Y, & Z) only exposes
to reflection what is allowed to be used on the interface (i.e. it's
capabilities); say, get_value(A), set_value(B), & use_method(Z).
An "unsecured object" could expose everything (i.e. the object IS the
interface). If necessary, an "interface to Reia object" process could be
automatically started as a part of running Reia in Erlang, with every
interface created adding it's pid & the mapped object pid into this for
a secured "Reia Debugger" process to access for looking at the
underlying object.
Cheers, Phil
Are we trying to invent EJB here?
I'm not sure what an untrusted node exactly is?
A malicious code on the same machine/local network? Doesn't magic cookie work here?
Untrusted environment? SSL should work fine here.