I've got some thoughts about how to isolate a smart proxy, please tell me yours:

  1. Implement an InvocationHandler for java.lang.reflect.Proxy,
     perhaps called SmartProxyIsolate
  2. SmartProxyIsolate has it's own Thread with a custom
     UncaughtExceptionHandler, it deals with any Errors the thread
     might have, such as StackOverflowError.  In the event of a
     StackOverflowError, the smart proxy reference and thread reference
     and method call queue, would be set to null immediately, leaving
     the SmartProxyIsolate in a known state, all method in the isolate
     would then return an IOException.
  3. All method invocations are passed to the smart proxy contained by
     the SmartProxyIsolate, only the SmartProxyIsolate holds a strong
     reference to the smart proxy.  The SmartProxyIsolate would expect
     a MarshalledInstance of the proxy in it's constructor.
  4. All  methods on the smart proxy are only called from the
     SmartProxyIsolate thread, trapping and handling any errors.
  5. A java.lang.reflect.Proxy ( created with the SmartProxyIsolate
     InvocationHandler ) is referenced by the client.
  6. SmartProxyIsolate needs a method to return the ProtectionDomain,
     to enable dynamic grants be granted to the smart proxy's PD by the
     DynamicPolicy, rather than the PD of the SmartProxyIsolate.
  7. To be completely sure, the Service API method returns shouldn't be
     subclasses, unless the smart proxy is trusted, we could create a
     new Permission class for this purpose. (lest a subclass be
     returned containing a reference to the smart proxy).

This structure could be used to isolate any proxy that requires a downloaded codebase, we would also need to place a limit on the size of the downloaded codebase, to prevent DOS that way too (I'll explain later).

We might also provide a method that allows the calling class to set an event handler, all method calls would return null immediately, and an Event later generated containing the result of the method call. This means the client thread isn't delayed in a wait state, but the default behaviour would be for client threads to wait.

Note that this requires JAVA 5, since each thread must have it's own UncaughtExceptionHandler that knows the specific SmartProxyIsolate.

The smart proxy would be isolated in it's own thread, any objects it creates will be in a thread local stack, it would unlikely be able to cause an OutOfMemoryError, unless the JVM is configured incorrectly. By giving the thread a lower priority, it couldn't freeze a single core client either.

Cheers,

Peter.

Peter Firmstone wrote:
I'm wondering, what can be done about DOS recursion with threads and memory?

A Thread has it's own private memory stack, the downloaded proxy will only have thread local objects, until they're passed to another thread.

What if we use a new thread to unmarshall and interact with the proxy?

A deliberate infinitely recursive loop in a Thread will throw a StackOverflowError, it may also cause problems with the available memory for all threads, so it needs to be tuned correctly with JVM memory options.

>From java.lang.Thread (the discussion is interesting, not the stackSize constructor), also note the UncaughtExceptionHandler method that follows.

  /**
    * Allocates a new <code>Thread</code> object so that it has
    * <code>target</code> as its run object, has the specified
* <code>name</code> as its name, belongs to the thread group referred to
    * by <code>group</code>, and has the specified <i>stack size</i>.
    *
    * <p>This constructor is identical to {...@link
* #Thread(ThreadGroup,Runnable,String)} with the exception of the fact * that it allows the thread stack size to be specified. The stack size * is the approximate number of bytes of address space that the virtual * machine is to allocate for this thread's stack. <b>The effect of the * <tt>stackSize</tt> parameter, if any, is highly platform dependent.</b>
    *
    * <p>On some platforms, specifying a higher value for the
    * <tt>stackSize</tt> parameter may allow a thread to achieve greater
    * recursion depth before throwing a {...@link StackOverflowError}.
    * Similarly, specifying a lower value may allow a greater number of
    * threads to exist concurrently without throwing an {...@link
    * OutOfMemoryError} (or other internal error).  The details of
* the relationship between the value of the <tt>stackSize</tt> parameter
    * and the maximum recursion depth and concurrency level are
    * platform-dependent.  <b>On some platforms, the value of the
    * <tt>stackSize</tt> parameter may have no effect whatsoever.</b>
    *
    * <p>The virtual machine is free to treat the <tt>stackSize</tt>
* parameter as a suggestion. If the specified value is unreasonably low
    * for the platform, the virtual machine may instead use some
* platform-specific minimum value; if the specified value is unreasonably
    * high, the virtual machine may instead use some platform-specific
* maximum. Likewise, the virtual machine is free to round the specified
    * value up or down as it sees fit (or to ignore it completely).
    *
* <p>Specifying a value of zero for the <tt>stackSize</tt> parameter will
    * cause this constructor to behave exactly like the
    * <tt>Thread(ThreadGroup, Runnable, String)</tt> constructor.
    *
    * <p><i>Due to the platform-dependent nature of the behavior of this
    * constructor, extreme care should be exercised in its use.
    * The thread stack size necessary to perform a given computation will
* likely vary from one JRE implementation to another. In light of this * variation, careful tuning of the stack size parameter may be required, * and the tuning may need to be repeated for each JRE implementation on
    * which an application is to run.</i>
    *
* <p>Implementation note: Java platform implementers are encouraged to
    * document their implementation's behavior with respect to the
    * <tt>stackSize parameter</tt>.
    *
    * @param      group    the thread group.
* @param target the object whose <code>run</code> method is called.
    * @param      name     the name of the new thread.
    * @param      stackSize the desired stack size for the new thread, or
    *             zero to indicate that this parameter is to be ignored.
* @exception SecurityException if the current thread cannot create a
    *               thread in the specified thread group.
    * @since 1.4
    */
   public Thread(ThreadGroup group, Runnable target, String name,
                 long stackSize)

  /**
    * Set the handler invoked when this thread abruptly terminates
    * due to an uncaught exception.
    * <p>A thread can take full control of how it responds to uncaught
    * exceptions by having its uncaught exception handler explicitly set.
    * If no such handler is set then the thread's <tt>ThreadGroup</tt>
    * object acts as its handler.
    * @param eh the object to use as this thread's uncaught exception
    * handler. If <tt>null</tt> then this thread has no explicit handler.
    * @throws  SecurityException  if the current thread is not allowed to
    *          modify this thread.
    * @see #setDefaultUncaughtExceptionHandler
    * @see ThreadGroup#uncaughtException
    * @since 1.5
    */
   public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)

If we catch the StackOverflowError, null out all references to the proxy and terminate the thread, we free the memory, the trick is not creating any new objects during recovery and making sure that the Thread running out of local memory doesn't threaten the memory the other threads have.

We can also set the proxy's unmarshalling thread with a low priority on Single processor machines, so other higher priority threads get some cpu time slices.

Once proxy unmarshalling is complete, it's still not safe to return the proxy to the calling thread, the trouble is, the proxy's methods could still contain infinitely recursive loops.

Any ideas for how to best invoke methods on the proxy, via the proxy thread, originating from the application thread?

Cheers,

Peter.

Peter Firmstone wrote:
Perhaps Oracle might have the money to complete the MVM Isolates API for Java SE and EE.

Isolates present the opportunity to restrict resources, which could fix the issues with excess resource consumption as DOS.

Until then the remaining ways to combat the memory consumption problem.

Reboot, report and be careful who you trust.

N.B. They can't do this with a reflective proxy, so don't grant their Principal DownloadPermission if you don't trust them.

Cheers,

Peter.

Christopher Dolan wrote:
Just for the record, Peter is right. I just looked at the JDK 1.5 source
code for java.lang.Thread. The security permission you need to create a
thread or a thread group is
SecurityConstants.MODIFY_THREADGROUP_PERMISSION, aka
java.lang.RuntimePermission("modifyThreadGroup"). I hadn't known about
that. The "modifyThread" permission does not affect one's ability to
create and start threads.

So, that would argue for a tightened security context during
deserialization.  That would at least revert us to Peter's original
statement that only one core could be smacked with 100% load.  The RAM
usage would not be controllable, though.

Chris

-----Original Message-----
From: Peter Firmstone [mailto:[email protected]] Sent: Thursday, September 30, 2010 2:41 AM
To: [email protected]
Subject: Re: Towards Internet Jini Services (dos attacks)

Yes, it certainly can.

Regards,

Peter.

Christopher Dolan wrote:
private void readObject(ObjectInputStream in) {
    new Runnable() {
        public void run() {
            while (true)
                new Thread(this).start();
        }
    }.run();
}

At 1MB of stack RAM per thread, this will thrash most machines in no
time.  Can a SecurityManager block thread creation?
Chris

-----Original Message-----
From: Peter Firmstone [mailto:[email protected]] Sent: Wednesday, September 29, 2010 3:58 PM
To: [email protected]
Subject: Re: Towards Internet Jini Services (dos attacks)

Zoltan Juhasz wrote:
Sim,

I think the important danger in Jini is the use of objects. In simple
messaging communication (especially if non-binary), you don't have to
worry
about objects. In Jini, any method can take and object as a parameter
that
results in serialisation and unmarshalling at the receiver end. When
an
object has something nasty executing during within the readObject()
method,
it's too late to do anything.
This was a big problem in the days of single core, not as bad now. Perhaps we need a software watchdog? Or an easy way to kill and quarantine a misbehaving service? Or an unmarshalling executor thread

pool, which passes the object after it has been deserialized.









Reply via email to