javax.jnlp.PersistenceService bug: ArrayIndexOutOfBoundsException

com.sun.jnlp.PersistenceServiceImpl, which is Sun standard implementation of PersistenceService interface, only allows for 255 storage entities for a given codebase.

After some decompiling I found out it keeps an internal array, size 255. Very very dumb move IMHO.

If you try to register a 256th entity, it fails horribly with a ArrayIndexOutOfBoundsException. This even if all the entries are 0-length, and the storage if effectively using no space at all.

This effectively hinders any usage of this API as a 'sandbox friendly' filesystem, as I was trying to do, by making the database for my JWS Application save to the PersistentService storage. This is because any 'serious' application creates many files for data and metadata (logs, etc...), and this make PersistenceService completely unfit for the task.

Moreover this limit is unneeded for security reasons, since size limits are already in, and it would be easily fixed by replacing the fixed size array with an ArrayList, with no consequences at all on the performance or features, except giving user a more capable PersistenceService implementation, that works as intended by the API contract, instead of firing unforeseen exceptions.

I've filed a bug with bugs.sun.com, let's see if they can fix this one.

What do you think about this?

[1346 byte] By [omeroa] at [2007-11-26 22:40:55]
# 1
What version of Java Web Start are you using ?I cannot find any such fixed array in either of the two most recent family versions./Andy
dietz333a at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...
# 2

I've tested it on latest final JDK6 for windows, and on latest JDK6 for Mac OS X (which is based on beta88).

Anyway, this is the same for every single jdk in the last few years, since the bugged class (com.sun.jnlp.PersistenceServiceImpl) hasn't changed for quite a while AFAIK.

By decompiling the class with JAD, I can find this

"private static class MuffinAccessVisitor implements DiskCacheVisitor" with the field "private URL _urls[]" which is initialized this way in constructor "_urls = new URL[255];".

This bug is very easily tested with this code anyway. This code fails on any JDK I've tried with the same error: "ArrayIndexOutOfBoundsException: 255".

import java.net.URL;

import java.util.Random;

import javax.jnlp.BasicService;

import javax.jnlp.PersistenceService;

import javax.jnlp.ServiceManager;

import javax.swing.JFrame;

import javax.swing.JTextField;

public class JNLPTester {

private static PersistenceService persistenceService;

private static BasicService basicService;

private static URL codebase;

static {

try {

// Get PersistenceService for operations, BasicService for codebase

persistenceService = (PersistenceService) ServiceManager.lookup("javax.jnlp.PersistenceService");

basicService = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService");

codebase = basicService.getCodeBase();

} catch (Exception e) {

e.printStackTrace();

}

}

public static void main(String[] args) {

int counter = 0;

try {

for (String url : persistenceService.getNames(codebase)) {

persistenceService.delete(new URL(codebase, url));

}

Random random = new Random();

while (true) {

// Loop and create a new storage entity with a random name

URL url = new URL(codebase, "" + random.nextLong());

persistenceService.create(url, 0L); // zero-length

counter++;

}

} catch (Exception e) {

JFrame frame = new JFrame();

frame.add(new JTextField("Counter: " + counter + " - Exception: " + e + " - Message: " + e.getMessage() ));

frame.setVisible(true);

}

}

}

omeroa at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...
# 3

I've downloaded official latest JDK6 sources from sun (jdk-6u1-ea-src-b03-jrl-19_jan_2007.jar), and I've had a look at the actual sources.

com.sun.jnlp.PersistenceServiceImpl depends on com.sun.deploy.Cache static methods for operations: it's this latter class that handles the persistence entities creation/removal, etc... (they are named 'muffins' in the implementation).

This is PersistenceServiceImpl.create

public long create(final URL url, final long maxsize)

throws MalformedURLException, IOException {

// compute new limit based on this maxsize + maxsize of all other

// entries this application has access to. If this total is

// >= _limit then ask user if they want to increase the _limit

checkAccess(url);

Long l = null;

long newmaxsize = -1;

if ((newmaxsize = checkSetMaxSize(url, maxsize)) < 0) return -1;

final long pass_newmaxsize = newmaxsize;

try {

l = (Long)AccessController.doPrivileged(

new PrivilegedExceptionAction() {

public Object run() throws MalformedURLException, IOException {

Cache.createMuffinEntry(url, PersistenceService.CACHED, pass_newmaxsize);

return new Long(pass_newmaxsize);

}

});

} catch (PrivilegedActionException e) {

Exception ee = e.getException();

if (ee instanceof IOException) {

throw (IOException)ee;

} else if (ee instanceof MalformedURLException) {

throw (MalformedURLException)ee;

}

}

return l.longValue();

}

As you can see the method calls "checkSetMaxSize"

private long checkSetMaxSize(final URL url, final long maxsize)

throws IOException {

// algorithm:

// friendTotalMaxSize = sum of maxsize of all other muffins accessible

// by the host that made this muffin. (i.e. muffins with same

// host as this one)

// if friendTotalMaxSize + maxsize > application maxsize, ask user if

// they want to increase application maxsize.

URL [] friendMuffins = null;

try {

friendMuffins = (URL []) AccessController.doPrivileged(

new PrivilegedExceptionAction() {

public Object run() throws IOException{

return (Cache.getAccessibleMuffins(url));

}

});

} catch (PrivilegedActionException e) {

throw (IOException) e.getException();

}

long friendMuffinsTotalMaxSize = 0;

if (friendMuffins != null) {

for (int i = 0; i < friendMuffins.length; i++) {

if (friendMuffins[i] != null) {

final URL friendMuffin = friendMuffins[i];

Long longFriendMuffinsSize = null;

try {

longFriendMuffinsSize =

(Long) AccessController.doPrivileged(

new PrivilegedExceptionAction() {

public Object run() throws IOException{

return new Long(Cache.getMuffinSize(

friendMuffin));

}

});

} catch (PrivilegedActionException e) {

throw (IOException) e.getException();

}

friendMuffinsTotalMaxSize +=

longFriendMuffinsSize.longValue();

}

}

}

Which in turns calls com.sun.deply.Cache.getAccessibleMuffins

public static URL[] getAccessibleMuffins(URL url) throws IOException {

URL[] urls = new URL[255];

// Get the list of files that match this name

final File[] files = muffinDir.listFiles(new FileFilter() {

public boolean accept(File pathname) {

String filename = pathname.getName();

return filename.endsWith(MUFFIN_FILE_EXT);

}

});

URL u;

int urlCount = 0;

for (int i = 0; i < files.length; i++) {

u = getCachedMuffinURL(files[i]);

if (u.getHost().equals(url.getHost())) {

urls[urlCount] = u;

urlCount += 1;

}

}

return urls;

}

Notice the: URL[] urls = new URL[255];

In this class we can find a fixed size array, size 255, which cause the bug.

You can easily test it with the provided test case in my previous reply. :)

omeroa at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...
# 4

I just got a mail that Sun acknowledged this as a 'new bug' for J2SE.

The bug should appear in a couple of days on the database, I will post here as soon as it does, so ppl interested can vote to get this bug fixed.

Also, I will provide a patch for fixing the bug: it's quite easy to do, we just have to get rid of that URL[255] and use a proper ArrayList instead.

omeroa at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...
# 5

> I just got a mail that Sun acknowledged this as a 'new bug' for J2SE.

Thanks for the update. I have been following

this thread, and played around with your example

till I saw much* the same errors you describe.

* I hesitate to say 'the same' because I could not

get it to fail in the way you described, based on the

original code example. A couple of tweaks later,

the run-time errors supported your assertion of

the '255' limit.

AndrewThompson64a at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...
# 6
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6539564This is the bug. Please vote for it if you want this to be fixed :)
omeroa at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...
# 7

Bug is open but no feedback and very few votes.

Guys, please vote for this but, this is a SERIOUS show stopper bug for any serious usage of the PersistenceService API, and it can be solved with a very simple and small fix.

So, please vote here: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6539564

omeroa at 2007-7-10 11:55:10 > top of Java-index,Desktop,Deploying...