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.