Re: Bindings and guard-based auditing

Kevin Reid <kpreid-M/[email protected]>
Newsgroups gmane.comp.lang.e.general
Message-ID <[email protected]>
On Jul 24, 2010, at 3:38, Thomas Leonard wrote:

> OK, so a summary of the problems with getting DeepFrozen working (with
> the goal of letting ImportLoader cache things):

Please remember that DeepFrozen *is* fully implemented in E-on-CL.

> - Verbose syntax for defining objects ("implements DeepFrozen"  
> everywhere)

More often than not you're using 'as' instead of 'implements', and you  
can rebind the variable if you want. I admit it's still somewhat  
bothersome.

> - No syntax for writing DeepFrozen functions (the most common thing we
> need to audit!)

Yes there is.

pragma.enable("function-implements")
def f(x) as DeepFrozen {
   return x + 1
}

> - Unguarded values can't be audited:
>
>  def x := 3
>  def myObject implements DeepFrozen { to getX() { return x } }
>
>  (audit fails because 'x' has no guard)

This is a deliberate choice. Any auditor, DeepFrozen included, is not  
allowed information it has not been given by the guard.

With the rangeSupersetOf mechanic these are all equally valid ways of  
guarding x:

  def x :DeepFrozen := 3
  def x :int := 3
  def x :Same[3] := 3

> - Auditing everything is hugely slow anyway because the auditing
> mechanism uses Java exceptions. Just calling
> DeepFrozenAuditor.THE_ONE.audit on each object increases E's startup
> time from 6s to 22s, even when all it does is just throw an exception.

I am confused by this statement. We normally expect an audit to  
succeed, in which case the auditor returns true and does not throw.

> - Not clear how to audit objects that refer to themselves.

The auditor can recognize the self-reference because it has the  
object's name-pattern in the ObjectExpr. In the case of DeepFrozen, it  
does not even have to do that because the object's own name is not a  
*free* variable.

> - Many Java objects will need to be tagged as DeepFrozen, which is
> likely to be error-prone and tricky. Is a broken reference DeepFrozen,
> for example? It contains an arbitrary Throwable...

A broken reference is a sort of transparent object (and Selfless iff  
it is Unconnected rather than Disconnected, the more common case). As  
such it is DeepFrozen iff its problem is DeepFrozen. Throwables need  
to be PassByCopy in order to sensibly handle passing broken refs over  
the network, so we need to tame away their mutability and declare them  
PassByCopy, if we haven't already.

> - DeepFrozenAuditor is in elib, so it can't access the types it needs
> (ObjectExpr, etc), as they're in elang.

Insofar as one wants to maintain an elib/elang separation, the answer  
is that the DeepFrozen auditor dispatches on the type of its audition  
and forwards to an E-specific implementation.

> - Much of safeScope is populated with lazy-slots to objects with
> recursive definitions (e.g. "int"). Unless the auditor can handle
> these complex cases, almost nothing will pass.

Slots: The lazy slots must be transparently lazy, else the safeScope  
is not DeepFrozen. As such auditing an object which mentions int need  
only observe that &int is a DeepFrozen slot.

The definition of int is not recursive (in E-on-CL anyway); do you  
mean the definition of makeOrderedSpace? It is handled by the  
EventuallyDeepFrozen auditor which defers the examination of the state  
until the construction has finished. It is a bit of a kludge, I admit.

> On 26 May 2010 15:53, Thomas Leonard <tal-v5nx5w6akNyLE8xUarVfuPLx9OUvmyODWmv/[email protected]>  
> wrote:
>> Why would leaking that something isn't DeepFrozen be a problem?

Because the policy in E is that an object's implementation is not  
exposed (other than in __getAllegedType which may be replaced). It is  
not possible to distinguish these two objects:

   { var x := 3; fn { x } }
   fn { 3 }

but if we DF-audited everything then the latter would be auto-DF but  
the former would not.


>>> def b :a {
>>>    to this() {
>>>      return b
>>>    }
>>> }
>>>
>>> The value of b is not determined until the guard a returns, but a
>>> invokes b. Therefore the naming pattern of an object expression must
>>> not have a guard.
>>
>> That is annoying, but it's a problem E already has in other places  
>> (e.g.
>> "def foo extends makeBar(foo)"). For the cases supported by the "as"
>> syntax, there's no reason why the guard would call the object anyway.

I suppose the guard could receive a promise for b. I'm uncertain about  
whether this is actually sound/useful; we've gone through a lot of  
ideas before reaching the current object expression syntax and I don't  
recall the issues.

>> I find it odd that we're being so careful to restrict what auditors  
>> can see and do, yet guards are so powerful:
>>
>> def obj as Auditor { ... }
>>
>> def process(x :Guard) { ... }
>> process(obj)
>>
>> The guard is free to substitute a replacement for obj, changing its
>> behaviour completely or getting access to every value passed to it.

The difference is that you're using the guard in your code, and it is  
typically from the same source as the implementation of the type  
you're guarding for anyway, so you're not relying on more. Auditors,  
however, are hung on *your* objects that you're going to hand to  
*other* parties who can then look at your objects' audits, and you  
don't *need* to rely on the auditors' good behavior since they can't  
change the object. Auditors can leak arbitrary amounts of information  
(bits) through ask()ing specially constructed auditors, so we want to  
constrain them to not have any information we didn't give to them.

-- 
Kevin Reid                                  <http://switchb.org/kpreid/>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.