[Vm-dev] VM handle management

Andreas Raab andreas.raab at gmx.de
Wed Mar 24 03:10:18 UTC 2010

Folks -

[Warning: Long post. Please try to stay on topic and don't get into any 
SmartSyntaxInterpreterPlugin / FFI syntax digressions that always come 
up with these issues. This is a practical proposal that I'd like to 

For the third time this month I ran into the situation that I was 
writing a plugin that would create an internal data structure 
(containing pointers etc) and where I wanted to return a handle to this 
structure and pass that back to the VM.

Since I'm a lazy bastard, what I usually do is something like this in 
the support code:

int sqNewFooBar(void) {
   /* creates a new foobar and returns a handle */
   FooBar *newFoo = malloc(1, sizeof(FooBar));

   /* return a handle, sort of */
   return (int) newFoo;

void sqDeleteFooBar(int handle) {
   FooBar *theFoo = (FooBar*) handle;

I don't have to tell you how horrible this is and what all the problems 
with the approach are - I'm sure you're all well aware of it. The 
question is, how do we fix this?

Here's my proposal. I say let's extend the VM with 4 new proxy 
functions, namely:

   /* Registers a pointer and returns a handle.
      The type argument ensures that the handle cannot be forged. */
   OOP registerHandleType(void *ptr, char *type);

   /* Unregister a handle */
   void unregisterHandle(void *ptr);

   /* Look up the pointer for a given handle/pair type */
   void* lookupHandleType(OOP handle, char *type);

   /* Ditto, just based on a stack index */
   void* stackHandleValueType(int stackIndex, char *type);

With these functions the support code can *always* deal with pointers, 
never with OOPs. The glue code does the translation between OOP and 
pointer. As a consequence the above support code becomes:

FooBar *sqNewFooBar(void) {
   /* creates a new foobar and returns it */
   FooBar *newFoo = malloc(1, sizeof(FooBar));
   return newFoo;

void sqDeleteFooBar(FooBar *theFoo) {
   /* deletes the FooBar */

and the glue code in the plugin becomes:

     "Creates a new foobar"
     | foobar |
     <var: #foobar type: 'FooBar*'>
     foobar := self sqNewFooBar.
     handle := interpreterProxy registerHandle: foobar type: 'FooBar';
     interpreterProxy pop: interpreterProxy methodArgumentCount+1 
thenPush: handle.

     "Destroys a foobar"
     | foobar |
     <var: #foobar type: 'FooBar*'>
     foobar := interpreterProxy lookupHandle: (interpreterProxy 
stackValue: 0) type: 'FooBar'.
     "or simply:
       foobar = interpreterProxy stackHandleValue: 0 type: 'FooBar'
     foobar ifNotNil:[
         interpreterProxy unregisterHandle: foobar.
         self sqDestroy: foobar.
     interpreterProxy pop: interpreterProxy methodArgumentCount.

What is interesting is that handles are completely opaque structures. 
They could be integers, byte arrays, whatever representation we choose, 
the only requirement is that the VM has a handleTable that can look up 
these pointers.

The obvious advantages of such a scheme are:

* It's safe. We don't expose a pointer but rather a handle that the VM 
knows about and that does not expose any dangerous internals.

* It's secure. By passing the additional "type" argument you can ensure 
that only handles registered as that type are used. I.e., no passing a 
SocketPtr into the FilePlugin.

* It's simple. I've written at least three handle management 
implementations and I'm getting sick and tired of it.

* It allows sharing. For example, OSProcessPlugin likes to do manipulate 
and register / deregister file handles, and can now safely look up the 
internal structure from the handle. (there are some issues here but they 
are no worse than what OSProcess already does)

If you agree, I'd like to implement this scheme and rewrite SocketPlugin 
and FilePlugin as examples for how to use such handles. They are already 
places that expose a ton of pretty risky information and it'd be a good 
example for how to utilize handles.

   - Andreas

More information about the Vm-dev mailing list