Bug 687 - JarUtil can't locate native library JARs if a custom classloader is being used (e.g. inside Eclipse RCP apps)
Summary: JarUtil can't locate native library JARs if a custom classloader is being use...
Status: RESOLVED FIXED
Alias: None
Product: Gluegen
Classification: JogAmp
Component: core (show other bugs)
Version: 2
Hardware: All all
: --- normal
Assignee: Sven Gothel
URL:
Depends on:
Blocks:
 
Reported: 2013-02-10 21:21 CET by Wade Walker
Modified: 2013-03-19 01:17 CET (History)
1 user (show)

See Also:
Type: ---
SCM Refs:
f3894c9fa1904572ee21b5c3aa2ca9e26a5d5d1e 45a84db7739aba2ab4526d7ef87850b9eb824740 27424d9f36659a09195eaae77cf774f7ba3c0eec
Workaround: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Wade Walker 2013-02-10 21:21:33 CET
JarUtil assumes that the URL of a JOGL class file will start with "jar:" if it's being run from a JAR file. However, this is not always the case. In an Eclipse RCP app, each plugin is given a custom classloader, which returns URLs beginning with "bundleresource:", even though JOGL really is being run from a JAR. These URLs cause JarUtil.getJarURL() to throw a IllegalArgumentException, which prevents JOGL from finding the native library JARs.

I have a solution to this that I've tested in Eclipse RCP apps (both launched from within Eclipse and from a binary installation). I'm currently writing a unit test for this fix, then I'll submit a pull request for review.
Comment 1 Wade Walker 2013-02-14 03:18:43 CET
I submitted a pull request that fixes this bug: https://github.com/WadeWalker/gluegen/commit/f3894c9fa1904572ee21b5c3aa2ca9e26a5d5d1e

A unit test is included, and I verified that the unit test (plus my own tests) run on Windows 7 64-bit, Mac OS X 10.7.5, and Ubuntu 12 64-bit.

With this fix, it will be possible to create an Eclipse RCP app that uses the new native library JARs! This should remove the last necessity for anyone to use the "raw" native libraries.
Comment 2 Sven Gothel 2013-02-14 03:41:34 CET
Who shall be allowed to invoke JarUtil.setResolver(..) ?
Security concerns of hijacking the whole show ?
Comment 3 Wade Walker 2013-02-14 16:07:32 CET
To address security, you could do something like this in JarUtil.getJarURL():

// only call resolver if URL contains an unknown protocol
if(resolver != null) {
    try {
        new URL(url.toExternalForm());
    }
    catch(MalformedURLException e) {    		
        url = resolver.resolve(url);
    }
}

That way, the resolver is only called if JOGL/GlueGen is being classloaded by an unknown protocol (like OSGi's "bundleresource" protocol).

Or you could just check that the URL doesn't start with some known protocol:

if(resolver != null
   && !url.toString().startsWith("jar:")
   && !url.toString().startsWith("file:")
   && !url.toString().startsWith("http:")
   && !url.toString().startsWith("https:")) {
    url = resolver.resolve(url);
}

Note that since URL.setURLStreamHandlerFactory() exists, nothing that uses URLs will ever be safe from code within the same JVM :)
Comment 4 Sven Gothel 2013-02-15 15:26:45 CET
(In reply to comment #3)
> Note that since URL.setURLStreamHandlerFactory() exists, nothing that uses
> URLs will ever be safe from code within the same JVM :)

Good point .. but pro security:

http://docs.oracle.com/javase/1.5.0/docs/api/java/net/URL.html#setURLStreamHandlerFactory%28java.net.URLStreamHandlerFactory%29

"Throws:
    SecurityException - if a security manager exists and its checkSetFactory method doesn't allow the operation."

So we could restrict the caller to be signed w/ same certificate as gluegen .. or a certificate at all,
if gluegen is signed ? If gluegen is not signed - we could allow it always.
Almost similar mechanism is impl. in JARUtil.validateCertificate().
Comment 5 Wade Walker 2013-02-16 23:11:21 CET
Well, I could enhance JarUtil.setResolver() a bit, so that it can't be set twice, and so the setter requires the same permissions as for URL.setURLStreamHandlerFactory(), like this:

public static void setResolver(Resolver r) {
    if(resolver != null) {
        throw new Error("Resolver already set!");
    }
    SecurityManager security = System.getSecurityManager();
    if(security != null) {
        security.checkSetFactory();
    }
    resolver = r;
}

As far as the certificates, I think we should be OK, since even if evil use of JarUtil.setResolver() may point us to an evil JAR, its certificate will still be checked and rejected when the TempJarCache tries to load it.

Please let me know which of these changes you'd like me to put in the pull request -- I'll need to re-test the code and unit test on all three platforms before I re-submit it to you :)
Comment 6 Sven Gothel 2013-02-17 02:07:31 CET
(In reply to comment #5)
> Well, I could enhance JarUtil.setResolver() a bit, so that it can't be set
> twice, and so the setter requires the same permissions as for
> URL.setURLStreamHandlerFactory(), like this:
> 
> public static void setResolver(Resolver r) {
>     if(resolver != null) {
>         throw new Error("Resolver already set!");
>     }
>     SecurityManager security = System.getSecurityManager();
>     if(security != null) {
>         security.checkSetFactory();
>     }
>     resolver = r;
> }
> 
> As far as the certificates, I think we should be OK, since even if evil use
> of JarUtil.setResolver() may point us to an evil JAR, its certificate will
> still be checked and rejected when the TempJarCache tries to load it.
> 
> Please let me know which of these changes you'd like me to put in the pull
> request -- I'll need to re-test the code and unit test on all three
> platforms before I re-submit it to you :)

The above code looks perfect and gives equal security as URL.setURLStreamHandlerFactory().
Great!

I will merge such pull request .. gogo :)
Comment 7 Sven Gothel 2013-02-17 02:08:35 CET
.. maybe add a null check .. to the argument 'r', but .. doesn't matter that much.
Comment 8 Wade Walker 2013-02-17 21:02:29 CET
I updated the pull request on GitHub. The branch is

https://github.com/WadeWalker/gluegen/tree/bug_687_jar_resolver

There are two commits in the branch. The first one is my initial version, and the second one (commit 45a84db7739aba2ab4526d7ef87850b9eb824740) adds the changes you requested.

I've tested the latest commit on Windows, Linux, and Mac OS X, and it works properly on all of them.
Comment 9 Julien Gouesse 2013-02-21 13:18:15 CET
(In reply to comment #8)
> I updated the pull request on GitHub. The branch is
> 
> https://github.com/WadeWalker/gluegen/tree/bug_687_jar_resolver
> 
> There are two commits in the branch. The first one is my initial version,
> and the second one (commit 45a84db7739aba2ab4526d7ef87850b9eb824740) adds
> the changes you requested.
> 
> I've tested the latest commit on Windows, Linux, and Mac OS X, and it works
> properly on all of them.

Has your fix been integrated?
Comment 10 Sven Gothel 2013-02-21 14:05:44 CET
Thank you Wade. Added minor edit with last commit 27424d9f36659a09195eaae77cf774f7ba3c0eec,
it should not break functionality.
Comment 11 Wade Walker 2013-02-21 15:34:26 CET
Looks good! I'll pull the latest daily build and confirm that it works with my RCP projects.

Thanks for merging this!
Comment 12 Wade Walker 2013-03-19 01:17:31 CET
Tested with the latest autobuild, and the fix is present and working.