JPMS Access Checks, Verification and the Security Manager

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

JPMS Access Checks, Verification and the Security Manager

Volker Simonis
Hi,

my question is if it is OK for a program which otherwise runs fine to
fail during class verification if running with a security manager
because of module access restrictions?

As the following write-up got a little lengthy, I've also uploaded a
html version for a nicer reading experience :)

http://cr.openjdk.java.net/~simonis/webrevs/2017/veri_with_secman/sm.html

Consider the following small, self-contained program which statically
references com.sun.crypto.provider.SunJCE:

import java.security.Provider;
import com.sun.crypto.provider.SunJCE;

public class Tricky {

  public static void main(String args[]) throws Exception {
    try {
      System.out.println(Tricky.class + " (" +
Tricky.class.getClassLoader() + ")" + args[0]);
    }
    catch (Exception e) {
      Provider p = new SunJCE();
      System.out.println(p + " (" + p.toString() + ")");
    }
  }
}

This program easily compiles and runs with Oracle/OpenJDK 8:

$ javac Tricky
$ java Tricky ""
class Tricky (sun.misc.Launcher$AppClassLoader@4e0e2f2a)
$ java Tricky
SunJCE version 1.8 (SunJCE version 1.8)

The second invocation (without argument) will cause an exception (when
trying to access the first element of the zero length argument array
at 'args[0]') which will be caught in the exception handler where we
create a new 'SunJCE' object and print its string representation to
stdout. The first invocation (with an empty argument) doesn't provoke
an exception and will just print the class itself together with its
class loader.

When we run the same (jdk8 compiled) program with jdk9, we'll see the
following result:

$ jdk9/java Tricky ""
class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@ba8a1dc)
$ jdk9/java Tricky
Exception in thread "main" java.lang.IllegalAccessError: class Tricky
(in unnamed module @0x3b192d32) cannot access class
com.sun.crypto.provider.SunJCE (in module java.base) because module
java.base does not export com.sun.crypto.provider to unnamed module
@0x3b192d32
  at Tricky.main(Tricky.java:11)
$ jdk9/java --add-exports java.base/com.sun.crypto.provider=ALL-UNNAMED Tricky
SunJCE version 9 (SunJCE version 9)

The first invocation (with an empty argument) still works because we
have lazy class loading and 'SunJCE' provider is actually never used.
The second invocation (without argument) obviously fails with jdk9
because 'java.base' doesn't export the package com.sun.crypto.provider
anymore. This can be fixed by adding '--add-exports
java.base/com.sun.crypto.provider=ALL-UNNAMED' to the command line as
demonstrated in the third invocation.

So far so good - everything worked as expected till now. Now let's run
the same program with the default security manager:

$ java -Djava.security.manager Tricky ""
class Tricky (sun.misc.Launcher$AppClassLoader@4e0e2f2a)
$ java -Djava.security.manager Tricky
SunJCE version 1.8 (SunJCE version 1.8)

The security manager doesn't change anything for jdk8! So let's try with jdk9:

$ jdk9/java -Djava.security.manager Tricky ""
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.security.AccessControlException:
access denied ("java.lang.RuntimePermission"
"accessClassInPackage.com.sun.crypto.provider")
        at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:471)
        at java.base/java.security.AccessController.checkPermission(AccessController.java:894)
        at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:561)
        at java.base/java.lang.SecurityManager.checkPackageAccess(SecurityManager.java:1534)
        at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:671)
        at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:669)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:669)
        at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
        at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3129)
        at java.base/java.lang.Class.getMethodsRecursive(Class.java:3270)
        at java.base/java.lang.Class.getMethod0(Class.java:3256)
        at java.base/java.lang.Class.getMethod(Class.java:2057)
        at java.base/sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:712)
        at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:570)

The first invocation (with an empty argument) crashes with a strange
JNI error. The call stack unveils that we haven't even entered the
main method of our program. Instead we've crashed in the jdk-internal
launcher 'sun.launcher.LauncherHelper' during 'checkAndLoadMain()'
with a 'java.security.AccessControlException' because we couldn't
access the 'com.sun.crypto.provider' package. That's strange because
we shouldn't need to load SunJCE provider for this invocation because
of lazy class loading.

A little reasoning and the right Xlog parameter unveils that the
exception happens during class verification:

$ jdk9/java -Djava.security.manager -Xlog:verification Tricky ""
...
[1,382s][info][verification] locals: { '[Ljava/lang/String;',
'java/lang/Exception', 'com/sun/crypto/provider/SunJCE' }
[1,382s][info][verification] stack: { 'java/io/PrintStream',
'java/lang/StringBuilder', 'com/sun/crypto/provider/SunJCE' }
[1,382s][info][verification] offset = 77,  opcode = invokevirtual
[1,589s][info][verification] Verification for Tricky has exception
pending java.security.AccessControlException
[1,589s][info][verification] End class verification for: Tricky
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.security.AccessControlException:
access denied ("java.lang.RuntimePermission"
"accessClassInPackage.com.sun.crypto.provider")
...

And indeed, switching off class verification will fix the problem:

$ jdk9/java -Djava.security.manager -Xlog:verification -noverify Tricky ""
class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@1b9e1916)

But switching off class verification is not something we usually want to do!

So we can try to use '--add-exports
java.base/com.sun.crypto.provider=ALL-UNNAMED' (although this wasn't
needed without security manager):

$ jdk9/java -Djava.security.manager --add-exports
java.base/com.sun.crypto.provider=ALL-UNNAMED Tricky ""
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.security.AccessControlException:
access denied ("java.lang.RuntimePermission"
"accessClassInPackage.com.sun.crypto.provider")
...

but the result is still the same. If you feel that '--add-exports
java.base/com.sun.crypto.provider=ALL-UNNAMED' should have fixed the
problem, you're not alone. That issue is tracked under JDK-8174766
[1]. It was discussed in the review [2] for JDK-8055206 [3] which
introduced the problem.

Currently, the only way to fix the problem is to add additional
permissions through a custom security policy:

my.policy
---------
grant {
  permission java.lang.RuntimePermission
"accessClassInPackage.com.sun.crypto.provider";
};

$ jdk9/java -Djava.security.manager -Djava.security.policy=my.policy Tricky ""
class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@1b9e1916)

But the real question remains: why does class verification fails with
a security manager and succeeds without?

With the right Xlog parameters we can verify, that even without
security manager, the 'SunJCE' class is successfully loaded by the
verifier for the purpose of verification, and the execution of the
program succeeds if we don't reference 'SunJCE' during execution:

$ jdk9/java -Xlog:verification -Xlog:class+load Tricky ""
...
[1,193s][info][verification] locals: { '[Ljava/lang/String;',
'java/lang/Exception', 'com/sun/crypto/provider/SunJCE' }
[1,193s][info][verification] stack: { 'java/io/PrintStream',
'java/lang/StringBuilder', 'com/sun/crypto/provider/SunJCE' }
[1,193s][info][verification] offset = 77,  opcode = invokevirtual
[1,196s][info][class,load  ] java.security.Provider source: jrt:/java.base
[1,197s][info][class,load  ] com.sun.crypto.provider.SunJCE source:
jrt:/java.base
...
[1,198s][info][verification] End class verification for: Tricky
...
class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@ba8a1dc)

So how does the presence of a security manager changes the class
verification process?

With a security manager, there are two additional
'accessClassInPackage' checks during class loading. If we recall the
verifier log:

[1,193s][info][verification] locals: { '[Ljava/lang/String;',
'java/lang/Exception', 'com/sun/crypto/provider/SunJCE' }
[1,193s][info][verification] stack: { 'java/io/PrintStream',
'java/lang/StringBuilder', 'com/sun/crypto/provider/SunJCE' }
[1,193s][info][verification] offset = 77,  opcode = invokevirtual
[1,196s][info][class,load  ] java.security.Provider source: jrt:/java.base
[1,197s][info][class,load  ] com.sun.crypto.provider.SunJCE source:
jrt:/java.base

we see that the verifier has to prove that
'com/sun/crypto/provider/SunJCE' (which is on top of the stack) can be
assigned (i.e. is assign-compatible) to 'java.security.Provider' and
it is therefor safe to call 'Provider::toString()' on it. Therefor, in
order to load the class 'com.sun.crypto.provider.SunJCE', the verifier
calls 'SystemDictionary::resolve_or_fail()' which in the end calls
'ClassLoader.loadClass(_class_name)' on the class loader of the class
under verification. If that class is a user class, this will call
'jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass()' in Java
9 because that's the actual application class loader (aka system class
loader) and ClassLoaders$AppClassLoader.loadClass() will perform the
following check if the VM runs with a security manager:

 @Override
 protected Class loadClass(String cn, boolean resolve) throws
ClassNotFoundException
 {
     SecurityManager sm = System.getSecurityManager();
     if (sm != null) {
         int i = cn.lastIndexOf('.');
         if (i != -1) {
             sm.checkPackageAccess(cn.substring(0, i));
         }
     }
     return super.loadClass(cn, resolve);
 }

Here's the corresponding VM stack trace:

j  java.lang.SecurityManager.checkPackageAccess(Ljava/lang/String;)V+46
java.base@9-internal
j  jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Ljava/lang/String;Z)Ljava/lang/Class;+8
java.base@9-internal
j  java.lang.ClassLoader.loadClass(Ljava/lang/String;)Ljava/lang/Class;+3
java.base@9-internal
v  ~StubRoutines::call_stub
V  [libjvm.so+0xc6c75d]  JavaCalls::call_helper(JavaValue*,
methodHandle const&, JavaCallArguments*, Thread*)+0x6a5
V  [libjvm.so+0x1055bab]  os::os_exception_wrapper(void
(*)(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*),
JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x41
V  [libjvm.so+0xc6c0a2]  JavaCalls::call(JavaValue*, methodHandle
const&, JavaCallArguments*, Thread*)+0xaa
V  [libjvm.so+0xc6b1b3]  JavaCalls::call_virtual(JavaValue*,
KlassHandle, Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x1f1
V  [libjvm.so+0xc6b3f0]  JavaCalls::call_virtual(JavaValue*, Handle,
KlassHandle, Symbol*, Symbol*, Handle, Thread*)+0xd2
V  [libjvm.so+0x1201c56]
SystemDictionary::load_instance_class(Symbol*, Handle, Thread*)+0x93c
V  [libjvm.so+0x11feda0]
SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle,
Handle, Thread*)+0x8c0
V  [libjvm.so+0x11fd298]  SystemDictionary::resolve_or_null(Symbol*,
Handle, Handle, Thread*)+0x262
V  [libjvm.so+0x11fcca3]  SystemDictionary::resolve_or_fail(Symbol*,
Handle, Handle, bool, Thread*)+0x45
V  [libjvm.so+0x1286586]
VerificationType::resolve_and_check_assignability(instanceKlassHandle,
Symbol*, Symbol*, bool, bool, bool, Thread*)+0x258
V  [libjvm.so+0x128682e]
VerificationType::is_reference_assignable_from(VerificationType
const&, ClassVerifier*, bool, Thread*) const+0x212
V  [libjvm.so+0x118fa55]
VerificationType::is_assignable_from(VerificationType const&,
ClassVerifier*, bool, Thread*) const+0x191
V  [libjvm.so+0x129a98f]  StackMapFrame::pop_stack(VerificationType,
Thread*)+0x81
V  [libjvm.so+0x12978f3]
ClassVerifier::verify_invoke_instructions(RawBytecodeStream*, unsigned
int, StackMapFrame*, bool, bool*, VerificationType, constantPoolHandle
const&, StackMapTable*, Thread*)+0x1249
V  [libjvm.so+0x129096b]  ClassVerifier::verify_method(methodHandle
const&, Thread*)+0x6b71
V  [libjvm.so+0x1289d1a]  ClassVerifier::verify_class(Thread*)+0x12a
V  [libjvm.so+0x1287c9e]  Verifier::verify(instanceKlassHandle,
Verifier::Mode, bool, Thread*)+0x2c0
V  [libjvm.so+0xc2b9b0]
InstanceKlass::verify_code(instanceKlassHandle, bool, Thread*)+0x7c
V  [libjvm.so+0xc2c228]
InstanceKlass::link_class_impl(instanceKlassHandle, bool,
Thread*)+0x55e
V  [libjvm.so+0xc2bb3d]  InstanceKlass::link_class(Thread*)+0xdf
V  [libjvm.so+0xcf5f80]  get_class_declared_methods_helper(JNIEnv_*,
_jclass*, unsigned char, bool, Klass*, Thread*)+0x155
V  [libjvm.so+0xcf6570]  JVM_GetClassDeclaredMethods+0x1d9
j  java.lang.Class.getDeclaredMethods0(Z)[Ljava/lang/reflect/Method;+0
java.base@9-internal
j  java.lang.Class.privateGetDeclaredMethods(Z)[Ljava/lang/reflect/Method;+34
java.base@9-internal
j  java.lang.Class.getMethodsRecursive(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/PublicMethods$MethodList;+2
java.base@9-internal
j  java.lang.Class.getMethod0(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+14
java.base@9-internal
j  java.lang.Class.getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+26
java.base@9-internal
j  sun.launcher.LauncherHelper.validateMainClass(Ljava/lang/Class;)V+12
java.base@9-internal
j  sun.launcher.LauncherHelper.checkAndLoadMain(ZILjava/lang/String;)Ljava/lang/Class;+54
java.base@9-internal

This check succeeds, because class-loading is triggered from
sun.launcher.LauncherHelper which is in the 'java.base' package, just
like the 'com.sun.crypto.provider.SunJCE' package.

But when running with a security manager, there's also a second check,
which is performed in the VM by
'SystemDictionary::resolve_instance_class_or_null()', right before it
returns the loaded class, by calling
'SystemDictionary::validate_protection_domain()'. This method in turn
calls 'java.lang.ClassLoader.checkPackageAccess()' to validate the
package access:

    // Invoked by the VM after loading class with this loader.
    private void checkPackageAccess(Class cls, ProtectionDomain pd) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ...
            final String name = cls.getName();
            final int i = name.lastIndexOf('.');
            if (i != -1) {
                AccessController.doPrivileged(new PrivilegedAction<>() {
                    public Void run() {
                        sm.checkPackageAccess(name.substring(0, i));
                        return null;
                    }
                }, new AccessControlContext(new ProtectionDomain[] {pd}));
            }
        }
    }

This check is done with the protection domain of the initial class
under verification ('Tricky' in our case) which is from the unnamed
module and doesn't have the rights to access the unexported classes
from java.base. Therefor the check fails the second time. Here's the
corresponding VM stack trace:

j  java.lang.ClassLoader.checkPackageAccess(Ljava/lang/Class;Ljava/security/ProtectionDomain;)V+106
java.base@9-internal
v  ~StubRoutines::call_stub
V  [libjvm.so+0xc6c75d]  JavaCalls::call_helper(JavaValue*,
methodHandle const&, JavaCallArguments*, Thread*)+0x6a5
V  [libjvm.so+0x1055bab]  os::os_exception_wrapper(void
(*)(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*),
JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x41
V  [libjvm.so+0xc6c0a2]  JavaCalls::call(JavaValue*, methodHandle
const&, JavaCallArguments*, Thread*)+0xaa
V  [libjvm.so+0xc6b6a9]  JavaCalls::call_special(JavaValue*,
KlassHandle, Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x173
V  [libjvm.so+0xc6ba04]  JavaCalls::call_special(JavaValue*, Handle,
KlassHandle, Symbol*, Symbol*, Handle, Handle, Thread*)+0xf6
V  [libjvm.so+0x11fdcb0]
SystemDictionary::validate_protection_domain(instanceKlassHandle,
Handle, Handle, Thread*)+0x22e
V  [libjvm.so+0x11ff40a]
SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle,
Handle, Thread*)+0xf2a
V  [libjvm.so+0x11fd298]  SystemDictionary::resolve_or_null(Symbol*,
Handle, Handle, Thread*)+0x262
V  [libjvm.so+0x11fcca3]  SystemDictionary::resolve_or_fail(Symbol*,
Handle, Handle, bool, Thread*)+0x45
V  [libjvm.so+0x1286586]
VerificationType::resolve_and_check_assignability(instanceKlassHandle,
Symbol*, Symbol*, bool, bool, bool, Thread*)+0x258
V  [libjvm.so+0x128682e]
VerificationType::is_reference_assignable_from(VerificationType
const&, ClassVerifier*, bool, Thread*) const+0x212
V  [libjvm.so+0x118fa55]
VerificationType::is_assignable_from(VerificationType const&,
ClassVerifier*, bool, Thread*) const+0x191
V  [libjvm.so+0x129a98f]  StackMapFrame::pop_stack(VerificationType,
Thread*)+0x81
V  [libjvm.so+0x12978f3]
ClassVerifier::verify_invoke_instructions(RawBytecodeStream*, unsigned
int, StackMapFrame*, bool, bool*, VerificationType, constantPoolHandle
const&, StackMapTable*, Thread*)+0x1249
V  [libjvm.so+0x129096b]  ClassVerifier::verify_method(methodHandle
const&, Thread*)+0x6b71
V  [libjvm.so+0x1289d1a]  ClassVerifier::verify_class(Thread*)+0x12a
V  [libjvm.so+0x1287c9e]  Verifier::verify(instanceKlassHandle,
Verifier::Mode, bool, Thread*)+0x2c0
V  [libjvm.so+0xc2b9b0]
InstanceKlass::verify_code(instanceKlassHandle, bool, Thread*)+0x7c
V  [libjvm.so+0xc2c228]
InstanceKlass::link_class_impl(instanceKlassHandle, bool,
Thread*)+0x55e
V  [libjvm.so+0xc2bb3d]  InstanceKlass::link_class(Thread*)+0xdf
V  [libjvm.so+0xcf5f80]  get_class_declared_methods_helper(JNIEnv_*,
_jclass*, unsigned char, bool, Klass*, Thread*)+0x155
V  [libjvm.so+0xcf6570]  JVM_GetClassDeclaredMethods+0x1d9
j  java.lang.Class.getDeclaredMethods0(Z)[Ljava/lang/reflect/Method;+0
java.base@9-internal
j  java.lang.Class.privateGetDeclaredMethods(Z)[Ljava/lang/reflect/Method;+34
java.base@9-internal
j  java.lang.Class.getMethodsRecursive(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/PublicMethods$MethodList;+2
java.base@9-internal
j  java.lang.Class.getMethod0(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+14
java.base@9-internal
j  java.lang.Class.getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+26
java.base@9-internal
j  sun.launcher.LauncherHelper.validateMainClass(Ljava/lang/Class;)V+12
java.base@9-internal
j  sun.launcher.LauncherHelper.checkAndLoadMain(ZILjava/lang/String;)Ljava/lang/Class;+54
java.base@9-internal

It is clear that the verifier sometimes has to load classes in order
to accomplish its duty, even if these classes won't be used later on,
at run-time.

But the question remains if it is OK for an application to fail just
because the verifier is unable to load classes required for
verification because of security manager restrictions or if the
verifier should run with higher privileges which allow such accesses?

If the answer to this question is "Yes" (i.e. it's OK to fail), the
consequence of running with a security manager will be that
'--add-exports/--add-opens/--illegal-access=permit' may be not sharp
enough knifes to achieve Java 8 backward compatibility. We may also
have to grant some additional security permissions to our application
classes.

If the answer will be "No" (i.e. it's not OK to fail in the verifier)
we may have to fix the VM to elevate the permissions used to load
classes from within the verifier.

Any comments?

Thank you and best regards,
Volker

[1] https://bugs.openjdk.java.net/browse/JDK-8174766
[2] http://mail.openjdk.java.net/pipermail/security-dev/2017-January/thread.html#15416
[3] https://bugs.openjdk.java.net/browse/JDK-8055206
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

forax
Hi Volker,
this is the behavior of the verifier since 6 (when the split verifier becomes the default one).

Let say you have a code like this,

public class TestVerifier {
  static class B extends A { }
  static class A { }

  public static void main(String[] args) {
    A a;
    if (args.length == 0) {
      a = new A();
    } else {
      a = new B();
    }
    System.out.println(a.toString());
  }
}

if you compile and then remove TestVerifier$B.class, you will get
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: TestVerifier$B
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
        at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
        at java.lang.Class.getMethod0(Class.java:3018)
        at java.lang.Class.getMethod(Class.java:1784)
        at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: TestVerifier$B
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 7 more

whatever the comand line arguments because it's the verifier that throws an error.

There is a workaround for you, if you use an interface instead of a class (in my example, make A an interface), it will work !
The verifier does not verify interface at verification time, the checks in done once by the VM at runtime.

so if there is a bug, it's in the way the verifier actually works i.e. if it can not find a class or do not see a class because of the module encapsulation, it should postpone the check at runtime instead of reporting an error at compile time. Thats said, i'm not sure it's a good idea.

cheers,
Rémi

----- Mail original -----
> De: "Volker Simonis" <[hidden email]>
> À: "jigsaw-dev" <[hidden email]>, "security-dev" <[hidden email]>
> Envoyé: Mardi 23 Mai 2017 09:27:22
> Objet: JPMS Access Checks, Verification and the Security Manager

> Hi,
>
> my question is if it is OK for a program which otherwise runs fine to
> fail during class verification if running with a security manager
> because of module access restrictions?
>
> As the following write-up got a little lengthy, I've also uploaded a
> html version for a nicer reading experience :)
>
> http://cr.openjdk.java.net/~simonis/webrevs/2017/veri_with_secman/sm.html
>
> Consider the following small, self-contained program which statically
> references com.sun.crypto.provider.SunJCE:
>
> import java.security.Provider;
> import com.sun.crypto.provider.SunJCE;
>
> public class Tricky {
>
>  public static void main(String args[]) throws Exception {
>    try {
>      System.out.println(Tricky.class + " (" +
> Tricky.class.getClassLoader() + ")" + args[0]);
>    }
>    catch (Exception e) {
>      Provider p = new SunJCE();
>      System.out.println(p + " (" + p.toString() + ")");
>    }
>  }
> }
>
> This program easily compiles and runs with Oracle/OpenJDK 8:
>
> $ javac Tricky
> $ java Tricky ""
> class Tricky (sun.misc.Launcher$AppClassLoader@4e0e2f2a)
> $ java Tricky
> SunJCE version 1.8 (SunJCE version 1.8)
>
> The second invocation (without argument) will cause an exception (when
> trying to access the first element of the zero length argument array
> at 'args[0]') which will be caught in the exception handler where we
> create a new 'SunJCE' object and print its string representation to
> stdout. The first invocation (with an empty argument) doesn't provoke
> an exception and will just print the class itself together with its
> class loader.
>
> When we run the same (jdk8 compiled) program with jdk9, we'll see the
> following result:
>
> $ jdk9/java Tricky ""
> class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@ba8a1dc)
> $ jdk9/java Tricky
> Exception in thread "main" java.lang.IllegalAccessError: class Tricky
> (in unnamed module @0x3b192d32) cannot access class
> com.sun.crypto.provider.SunJCE (in module java.base) because module
> java.base does not export com.sun.crypto.provider to unnamed module
> @0x3b192d32
>  at Tricky.main(Tricky.java:11)
> $ jdk9/java --add-exports java.base/com.sun.crypto.provider=ALL-UNNAMED Tricky
> SunJCE version 9 (SunJCE version 9)
>
> The first invocation (with an empty argument) still works because we
> have lazy class loading and 'SunJCE' provider is actually never used.
> The second invocation (without argument) obviously fails with jdk9
> because 'java.base' doesn't export the package com.sun.crypto.provider
> anymore. This can be fixed by adding '--add-exports
> java.base/com.sun.crypto.provider=ALL-UNNAMED' to the command line as
> demonstrated in the third invocation.
>
> So far so good - everything worked as expected till now. Now let's run
> the same program with the default security manager:
>
> $ java -Djava.security.manager Tricky ""
> class Tricky (sun.misc.Launcher$AppClassLoader@4e0e2f2a)
> $ java -Djava.security.manager Tricky
> SunJCE version 1.8 (SunJCE version 1.8)
>
> The security manager doesn't change anything for jdk8! So let's try with jdk9:
>
> $ jdk9/java -Djava.security.manager Tricky ""
> Error: A JNI error has occurred, please check your installation and try again
> Exception in thread "main" java.security.AccessControlException:
> access denied ("java.lang.RuntimePermission"
> "accessClassInPackage.com.sun.crypto.provider")
>        at
>        java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:471)
>        at
>        java.base/java.security.AccessController.checkPermission(AccessController.java:894)
>        at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:561)
>        at
>        java.base/java.lang.SecurityManager.checkPackageAccess(SecurityManager.java:1534)
>        at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:671)
>        at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:669)
>        at java.base/java.security.AccessController.doPrivileged(Native Method)
>        at java.base/java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:669)
>        at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
>        at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3129)
>        at java.base/java.lang.Class.getMethodsRecursive(Class.java:3270)
>        at java.base/java.lang.Class.getMethod0(Class.java:3256)
>        at java.base/java.lang.Class.getMethod(Class.java:2057)
>        at
>        java.base/sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:712)
>        at
>        java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:570)
>
> The first invocation (with an empty argument) crashes with a strange
> JNI error. The call stack unveils that we haven't even entered the
> main method of our program. Instead we've crashed in the jdk-internal
> launcher 'sun.launcher.LauncherHelper' during 'checkAndLoadMain()'
> with a 'java.security.AccessControlException' because we couldn't
> access the 'com.sun.crypto.provider' package. That's strange because
> we shouldn't need to load SunJCE provider for this invocation because
> of lazy class loading.
>
> A little reasoning and the right Xlog parameter unveils that the
> exception happens during class verification:
>
> $ jdk9/java -Djava.security.manager -Xlog:verification Tricky ""
> ...
> [1,382s][info][verification] locals: { '[Ljava/lang/String;',
> 'java/lang/Exception', 'com/sun/crypto/provider/SunJCE' }
> [1,382s][info][verification] stack: { 'java/io/PrintStream',
> 'java/lang/StringBuilder', 'com/sun/crypto/provider/SunJCE' }
> [1,382s][info][verification] offset = 77,  opcode = invokevirtual
> [1,589s][info][verification] Verification for Tricky has exception
> pending java.security.AccessControlException
> [1,589s][info][verification] End class verification for: Tricky
> Error: A JNI error has occurred, please check your installation and try again
> Exception in thread "main" java.security.AccessControlException:
> access denied ("java.lang.RuntimePermission"
> "accessClassInPackage.com.sun.crypto.provider")
> ...
>
> And indeed, switching off class verification will fix the problem:
>
> $ jdk9/java -Djava.security.manager -Xlog:verification -noverify Tricky ""
> class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@1b9e1916)
>
> But switching off class verification is not something we usually want to do!
>
> So we can try to use '--add-exports
> java.base/com.sun.crypto.provider=ALL-UNNAMED' (although this wasn't
> needed without security manager):
>
> $ jdk9/java -Djava.security.manager --add-exports
> java.base/com.sun.crypto.provider=ALL-UNNAMED Tricky ""
> Error: A JNI error has occurred, please check your installation and try again
> Exception in thread "main" java.security.AccessControlException:
> access denied ("java.lang.RuntimePermission"
> "accessClassInPackage.com.sun.crypto.provider")
> ...
>
> but the result is still the same. If you feel that '--add-exports
> java.base/com.sun.crypto.provider=ALL-UNNAMED' should have fixed the
> problem, you're not alone. That issue is tracked under JDK-8174766
> [1]. It was discussed in the review [2] for JDK-8055206 [3] which
> introduced the problem.
>
> Currently, the only way to fix the problem is to add additional
> permissions through a custom security policy:
>
> my.policy
> ---------
> grant {
>  permission java.lang.RuntimePermission
> "accessClassInPackage.com.sun.crypto.provider";
> };
>
> $ jdk9/java -Djava.security.manager -Djava.security.policy=my.policy Tricky ""
> class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@1b9e1916)
>
> But the real question remains: why does class verification fails with
> a security manager and succeeds without?
>
> With the right Xlog parameters we can verify, that even without
> security manager, the 'SunJCE' class is successfully loaded by the
> verifier for the purpose of verification, and the execution of the
> program succeeds if we don't reference 'SunJCE' during execution:
>
> $ jdk9/java -Xlog:verification -Xlog:class+load Tricky ""
> ...
> [1,193s][info][verification] locals: { '[Ljava/lang/String;',
> 'java/lang/Exception', 'com/sun/crypto/provider/SunJCE' }
> [1,193s][info][verification] stack: { 'java/io/PrintStream',
> 'java/lang/StringBuilder', 'com/sun/crypto/provider/SunJCE' }
> [1,193s][info][verification] offset = 77,  opcode = invokevirtual
> [1,196s][info][class,load  ] java.security.Provider source: jrt:/java.base
> [1,197s][info][class,load  ] com.sun.crypto.provider.SunJCE source:
> jrt:/java.base
> ...
> [1,198s][info][verification] End class verification for: Tricky
> ...
> class Tricky (jdk.internal.loader.ClassLoaders$AppClassLoader@ba8a1dc)
>
> So how does the presence of a security manager changes the class
> verification process?
>
> With a security manager, there are two additional
> 'accessClassInPackage' checks during class loading. If we recall the
> verifier log:
>
> [1,193s][info][verification] locals: { '[Ljava/lang/String;',
> 'java/lang/Exception', 'com/sun/crypto/provider/SunJCE' }
> [1,193s][info][verification] stack: { 'java/io/PrintStream',
> 'java/lang/StringBuilder', 'com/sun/crypto/provider/SunJCE' }
> [1,193s][info][verification] offset = 77,  opcode = invokevirtual
> [1,196s][info][class,load  ] java.security.Provider source: jrt:/java.base
> [1,197s][info][class,load  ] com.sun.crypto.provider.SunJCE source:
> jrt:/java.base
>
> we see that the verifier has to prove that
> 'com/sun/crypto/provider/SunJCE' (which is on top of the stack) can be
> assigned (i.e. is assign-compatible) to 'java.security.Provider' and
> it is therefor safe to call 'Provider::toString()' on it. Therefor, in
> order to load the class 'com.sun.crypto.provider.SunJCE', the verifier
> calls 'SystemDictionary::resolve_or_fail()' which in the end calls
> 'ClassLoader.loadClass(_class_name)' on the class loader of the class
> under verification. If that class is a user class, this will call
> 'jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass()' in Java
> 9 because that's the actual application class loader (aka system class
> loader) and ClassLoaders$AppClassLoader.loadClass() will perform the
> following check if the VM runs with a security manager:
>
> @Override
> protected Class loadClass(String cn, boolean resolve) throws
> ClassNotFoundException
> {
>     SecurityManager sm = System.getSecurityManager();
>     if (sm != null) {
>         int i = cn.lastIndexOf('.');
>         if (i != -1) {
>             sm.checkPackageAccess(cn.substring(0, i));
>         }
>     }
>     return super.loadClass(cn, resolve);
> }
>
> Here's the corresponding VM stack trace:
>
> j  java.lang.SecurityManager.checkPackageAccess(Ljava/lang/String;)V+46
> java.base@9-internal
> j
> jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Ljava/lang/String;Z)Ljava/lang/Class;+8
> java.base@9-internal
> j  java.lang.ClassLoader.loadClass(Ljava/lang/String;)Ljava/lang/Class;+3
> java.base@9-internal
> v  ~StubRoutines::call_stub
> V  [libjvm.so+0xc6c75d]  JavaCalls::call_helper(JavaValue*,
> methodHandle const&, JavaCallArguments*, Thread*)+0x6a5
> V  [libjvm.so+0x1055bab]  os::os_exception_wrapper(void
> (*)(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*),
> JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x41
> V  [libjvm.so+0xc6c0a2]  JavaCalls::call(JavaValue*, methodHandle
> const&, JavaCallArguments*, Thread*)+0xaa
> V  [libjvm.so+0xc6b1b3]  JavaCalls::call_virtual(JavaValue*,
> KlassHandle, Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x1f1
> V  [libjvm.so+0xc6b3f0]  JavaCalls::call_virtual(JavaValue*, Handle,
> KlassHandle, Symbol*, Symbol*, Handle, Thread*)+0xd2
> V  [libjvm.so+0x1201c56]
> SystemDictionary::load_instance_class(Symbol*, Handle, Thread*)+0x93c
> V  [libjvm.so+0x11feda0]
> SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle,
> Handle, Thread*)+0x8c0
> V  [libjvm.so+0x11fd298]  SystemDictionary::resolve_or_null(Symbol*,
> Handle, Handle, Thread*)+0x262
> V  [libjvm.so+0x11fcca3]  SystemDictionary::resolve_or_fail(Symbol*,
> Handle, Handle, bool, Thread*)+0x45
> V  [libjvm.so+0x1286586]
> VerificationType::resolve_and_check_assignability(instanceKlassHandle,
> Symbol*, Symbol*, bool, bool, bool, Thread*)+0x258
> V  [libjvm.so+0x128682e]
> VerificationType::is_reference_assignable_from(VerificationType
> const&, ClassVerifier*, bool, Thread*) const+0x212
> V  [libjvm.so+0x118fa55]
> VerificationType::is_assignable_from(VerificationType const&,
> ClassVerifier*, bool, Thread*) const+0x191
> V  [libjvm.so+0x129a98f]  StackMapFrame::pop_stack(VerificationType,
> Thread*)+0x81
> V  [libjvm.so+0x12978f3]
> ClassVerifier::verify_invoke_instructions(RawBytecodeStream*, unsigned
> int, StackMapFrame*, bool, bool*, VerificationType, constantPoolHandle
> const&, StackMapTable*, Thread*)+0x1249
> V  [libjvm.so+0x129096b]  ClassVerifier::verify_method(methodHandle
> const&, Thread*)+0x6b71
> V  [libjvm.so+0x1289d1a]  ClassVerifier::verify_class(Thread*)+0x12a
> V  [libjvm.so+0x1287c9e]  Verifier::verify(instanceKlassHandle,
> Verifier::Mode, bool, Thread*)+0x2c0
> V  [libjvm.so+0xc2b9b0]
> InstanceKlass::verify_code(instanceKlassHandle, bool, Thread*)+0x7c
> V  [libjvm.so+0xc2c228]
> InstanceKlass::link_class_impl(instanceKlassHandle, bool,
> Thread*)+0x55e
> V  [libjvm.so+0xc2bb3d]  InstanceKlass::link_class(Thread*)+0xdf
> V  [libjvm.so+0xcf5f80]  get_class_declared_methods_helper(JNIEnv_*,
> _jclass*, unsigned char, bool, Klass*, Thread*)+0x155
> V  [libjvm.so+0xcf6570]  JVM_GetClassDeclaredMethods+0x1d9
> j  java.lang.Class.getDeclaredMethods0(Z)[Ljava/lang/reflect/Method;+0
> java.base@9-internal
> j  java.lang.Class.privateGetDeclaredMethods(Z)[Ljava/lang/reflect/Method;+34
> java.base@9-internal
> j
> java.lang.Class.getMethodsRecursive(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/PublicMethods$MethodList;+2
> java.base@9-internal
> j
> java.lang.Class.getMethod0(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+14
> java.base@9-internal
> j
> java.lang.Class.getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+26
> java.base@9-internal
> j  sun.launcher.LauncherHelper.validateMainClass(Ljava/lang/Class;)V+12
> java.base@9-internal
> j
> sun.launcher.LauncherHelper.checkAndLoadMain(ZILjava/lang/String;)Ljava/lang/Class;+54
> java.base@9-internal
>
> This check succeeds, because class-loading is triggered from
> sun.launcher.LauncherHelper which is in the 'java.base' package, just
> like the 'com.sun.crypto.provider.SunJCE' package.
>
> But when running with a security manager, there's also a second check,
> which is performed in the VM by
> 'SystemDictionary::resolve_instance_class_or_null()', right before it
> returns the loaded class, by calling
> 'SystemDictionary::validate_protection_domain()'. This method in turn
> calls 'java.lang.ClassLoader.checkPackageAccess()' to validate the
> package access:
>
>    // Invoked by the VM after loading class with this loader.
>    private void checkPackageAccess(Class cls, ProtectionDomain pd) {
>        final SecurityManager sm = System.getSecurityManager();
>        if (sm != null) {
>            ...
>            final String name = cls.getName();
>            final int i = name.lastIndexOf('.');
>            if (i != -1) {
>                AccessController.doPrivileged(new PrivilegedAction<>() {
>                    public Void run() {
>                        sm.checkPackageAccess(name.substring(0, i));
>                        return null;
>                    }
>                }, new AccessControlContext(new ProtectionDomain[] {pd}));
>            }
>        }
>    }
>
> This check is done with the protection domain of the initial class
> under verification ('Tricky' in our case) which is from the unnamed
> module and doesn't have the rights to access the unexported classes
> from java.base. Therefor the check fails the second time. Here's the
> corresponding VM stack trace:
>
> j
> java.lang.ClassLoader.checkPackageAccess(Ljava/lang/Class;Ljava/security/ProtectionDomain;)V+106
> java.base@9-internal
> v  ~StubRoutines::call_stub
> V  [libjvm.so+0xc6c75d]  JavaCalls::call_helper(JavaValue*,
> methodHandle const&, JavaCallArguments*, Thread*)+0x6a5
> V  [libjvm.so+0x1055bab]  os::os_exception_wrapper(void
> (*)(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*),
> JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x41
> V  [libjvm.so+0xc6c0a2]  JavaCalls::call(JavaValue*, methodHandle
> const&, JavaCallArguments*, Thread*)+0xaa
> V  [libjvm.so+0xc6b6a9]  JavaCalls::call_special(JavaValue*,
> KlassHandle, Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x173
> V  [libjvm.so+0xc6ba04]  JavaCalls::call_special(JavaValue*, Handle,
> KlassHandle, Symbol*, Symbol*, Handle, Handle, Thread*)+0xf6
> V  [libjvm.so+0x11fdcb0]
> SystemDictionary::validate_protection_domain(instanceKlassHandle,
> Handle, Handle, Thread*)+0x22e
> V  [libjvm.so+0x11ff40a]
> SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle,
> Handle, Thread*)+0xf2a
> V  [libjvm.so+0x11fd298]  SystemDictionary::resolve_or_null(Symbol*,
> Handle, Handle, Thread*)+0x262
> V  [libjvm.so+0x11fcca3]  SystemDictionary::resolve_or_fail(Symbol*,
> Handle, Handle, bool, Thread*)+0x45
> V  [libjvm.so+0x1286586]
> VerificationType::resolve_and_check_assignability(instanceKlassHandle,
> Symbol*, Symbol*, bool, bool, bool, Thread*)+0x258
> V  [libjvm.so+0x128682e]
> VerificationType::is_reference_assignable_from(VerificationType
> const&, ClassVerifier*, bool, Thread*) const+0x212
> V  [libjvm.so+0x118fa55]
> VerificationType::is_assignable_from(VerificationType const&,
> ClassVerifier*, bool, Thread*) const+0x191
> V  [libjvm.so+0x129a98f]  StackMapFrame::pop_stack(VerificationType,
> Thread*)+0x81
> V  [libjvm.so+0x12978f3]
> ClassVerifier::verify_invoke_instructions(RawBytecodeStream*, unsigned
> int, StackMapFrame*, bool, bool*, VerificationType, constantPoolHandle
> const&, StackMapTable*, Thread*)+0x1249
> V  [libjvm.so+0x129096b]  ClassVerifier::verify_method(methodHandle
> const&, Thread*)+0x6b71
> V  [libjvm.so+0x1289d1a]  ClassVerifier::verify_class(Thread*)+0x12a
> V  [libjvm.so+0x1287c9e]  Verifier::verify(instanceKlassHandle,
> Verifier::Mode, bool, Thread*)+0x2c0
> V  [libjvm.so+0xc2b9b0]
> InstanceKlass::verify_code(instanceKlassHandle, bool, Thread*)+0x7c
> V  [libjvm.so+0xc2c228]
> InstanceKlass::link_class_impl(instanceKlassHandle, bool,
> Thread*)+0x55e
> V  [libjvm.so+0xc2bb3d]  InstanceKlass::link_class(Thread*)+0xdf
> V  [libjvm.so+0xcf5f80]  get_class_declared_methods_helper(JNIEnv_*,
> _jclass*, unsigned char, bool, Klass*, Thread*)+0x155
> V  [libjvm.so+0xcf6570]  JVM_GetClassDeclaredMethods+0x1d9
> j  java.lang.Class.getDeclaredMethods0(Z)[Ljava/lang/reflect/Method;+0
> java.base@9-internal
> j  java.lang.Class.privateGetDeclaredMethods(Z)[Ljava/lang/reflect/Method;+34
> java.base@9-internal
> j
> java.lang.Class.getMethodsRecursive(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/PublicMethods$MethodList;+2
> java.base@9-internal
> j
> java.lang.Class.getMethod0(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+14
> java.base@9-internal
> j
> java.lang.Class.getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+26
> java.base@9-internal
> j  sun.launcher.LauncherHelper.validateMainClass(Ljava/lang/Class;)V+12
> java.base@9-internal
> j
> sun.launcher.LauncherHelper.checkAndLoadMain(ZILjava/lang/String;)Ljava/lang/Class;+54
> java.base@9-internal
>
> It is clear that the verifier sometimes has to load classes in order
> to accomplish its duty, even if these classes won't be used later on,
> at run-time.
>
> But the question remains if it is OK for an application to fail just
> because the verifier is unable to load classes required for
> verification because of security manager restrictions or if the
> verifier should run with higher privileges which allow such accesses?
>
> If the answer to this question is "Yes" (i.e. it's OK to fail), the
> consequence of running with a security manager will be that
> '--add-exports/--add-opens/--illegal-access=permit' may be not sharp
> enough knifes to achieve Java 8 backward compatibility. We may also
> have to grant some additional security permissions to our application
> classes.
>
> If the answer will be "No" (i.e. it's not OK to fail in the verifier)
> we may have to fix the VM to elevate the permissions used to load
> classes from within the verifier.
>
> Any comments?
>
> Thank you and best regards,
> Volker
>
> [1] https://bugs.openjdk.java.net/browse/JDK-8174766
> [2]
> http://mail.openjdk.java.net/pipermail/security-dev/2017-January/thread.html#15416
> [3] https://bugs.openjdk.java.net/browse/JDK-8055206
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Alan Bateman
In reply to this post by Volker Simonis
Volker - one suggestion for your experiments is to change your JDK 8
security properties file (java.security) to add
"com.sun.crypto.provider." to the value of the "package.access"
property. That should mean you will get the same AccessControlException
with JDK 8 as you do with JDK 9. It may help to reason about the JDK 8
behavior, including what is triggered by verification, before looking at 9.

On the "Error: A JNI error has occurred, please check your installation
and try again" message. It's not core to your questions of course but I
agree it is confusing. Ramanand Patil had a number of attempts on
core-libs-dev to improve this and it may be that more is needed there.

-Alan

Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

David Holmes
On 23/05/2017 6:20 PM, Alan Bateman wrote:

> Volker - one suggestion for your experiments is to change your JDK 8
> security properties file (java.security) to add
> "com.sun.crypto.provider." to the value of the "package.access"
> property. That should mean you will get the same AccessControlException
> with JDK 8 as you do with JDK 9. It may help to reason about the JDK 8
> behavior, including what is triggered by verification, before looking at 9.
>
> On the "Error: A JNI error has occurred, please check your installation
> and try again" message. It's not core to your questions of course but I
> agree it is confusing. Ramanand Patil had a number of attempts on
> core-libs-dev to improve this and it may be that more is needed there.

Yes I was sure we had agreed to remove that and I don't see it in Oracle
JDK so perhaps it is only in OpenJDK launcher?

David


> -Alan
>
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Volker Simonis
On Tue, May 23, 2017 at 10:51 AM, David Holmes <[hidden email]> wrote:

> On 23/05/2017 6:20 PM, Alan Bateman wrote:
>>
>> Volker - one suggestion for your experiments is to change your JDK 8
>> security properties file (java.security) to add "com.sun.crypto.provider."
>> to the value of the "package.access" property. That should mean you will get
>> the same AccessControlException with JDK 8 as you do with JDK 9. It may help
>> to reason about the JDK 8 behavior, including what is triggered by
>> verification, before looking at 9.
>>
>> On the "Error: A JNI error has occurred, please check your installation
>> and try again" message. It's not core to your questions of course but I
>> agree it is confusing. Ramanand Patil had a number of attempts on
>> core-libs-dev to improve this and it may be that more is needed there.
>
>
> Yes I was sure we had agreed to remove that and I don't see it in Oracle JDK
> so perhaps it is only in OpenJDK launcher?
>

Hi David,

not sure what you mean? Does the Oracle JDK come with another launcher
compared to the OpenJDK?

I've just tried with the latest EA build 170 (which I assume is an
Oracle JDK) and I see exactly the same error:

java -Djava.security.manager -showversion Tricky ""
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+170)
Java HotSpot(TM) Server VM (build 9-ea+170, mixed mode)
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.security.AccessControlException:
access denied ("java.lang.RuntimePermission"
"accessClassInPackage.com.sun.crypto.provider")
    at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:471)
    at java.base/java.security.AccessController.checkPermission(AccessController.java:894)
    at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:561)
    at java.base/java.lang.SecurityManager.checkPackageAccess(SecurityManager.java:1534)
    at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:671)
    at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:669)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:669)
    at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3134)
    at java.base/java.lang.Class.getMethodsRecursive(Class.java:3275)
    at java.base/java.lang.Class.getMethod0(Class.java:3261)
    at java.base/java.lang.Class.getMethod(Class.java:2062)
    at java.base/sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:712)
    at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:570)

So what do you mean when saying that you don't see it in the Oracle JDK?

Regards,
Volker

> David
>
>
>> -Alan
>>
>
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Volker Simonis
In reply to this post by Alan Bateman
On Tue, May 23, 2017 at 10:20 AM, Alan Bateman <[hidden email]> wrote:
> Volker - one suggestion for your experiments is to change your JDK 8
> security properties file (java.security) to add "com.sun.crypto.provider."
> to the value of the "package.access" property. That should mean you will get
> the same AccessControlException with JDK 8 as you do with JDK 9. It may help
> to reason about the JDK 8 behavior, including what is triggered by
> verification, before looking at 9.
>

You're right. JDK 8 produces the same error if we add
"com.sun.crypto.provider." to the value of the "package.access"
property. I didn't wanted to blame jdk9 here - I just saw this
difference because "com.sun.crypto.provider." isn't contained in the
"package.access" property by default.

So maybe I rephrase my question a little more generally:

Is it required for the verifier to do security and/or access checks
during the verification phase or could/should these checks be
postponed to runtime? The issue with verification errors due to
missing classes from Remi's previous answer is probably a corner case
of this question.

The other question is if '--add-exports' should implicitly grant the
corresponding security permissions? I understand that package/class
visibility and security checks are somewhat orthogonal concepts so I
tend to agree with the current functionality (and a part of this
problem is now tracked by JDK-8174766). Nevertheless it is probably
wise to document these differences so people won't get surprised.

> On the "Error: A JNI error has occurred, please check your installation and
> try again" message. It's not core to your questions of course but I agree it
> is confusing. Ramanand Patil had a number of attempts on core-libs-dev to
> improve this and it may be that more is needed there.
>

Yes, a better error message would be nice but thats a different question.

Thanks,
Volker

> -Alan
>
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Sean Mullan
On 5/23/17 10:44 AM, Volker Simonis wrote:
> The other question is if '--add-exports' should implicitly grant the
> corresponding security permissions? I understand that package/class
> visibility and security checks are somewhat orthogonal concepts so I
> tend to agree with the current functionality (and a part of this
> problem is now tracked by JDK-8174766). Nevertheless it is probably
> wise to document these differences so people won't get surprised.

We considered it but the SecurityManager checks all callers (and not
just the target module) on the stack for the appropriate
accessClassInPackage RuntimePermission. Also,
SecurityManager.checkPackageAccess does not know what the target module
is, all it has is the package name. It is possible however that we could
respect the module access rights when loading classes in the internal
ClassLoader.checkPackageAccess method. I will be looking more into this
in the JDK 10 timeframe.

The current javadoc of SecurityManager.checkPackageAccess [1] does
document this behavior:

"This implementation also restricts all non-exported packages of modules
loaded by the platform class loader or its ancestors. A "non-exported
package" refers to a package that is not exported to all modules.
Specifically, it refers to a package that either is not exported at all
by its containing module or is exported in a qualified fashion by its
containing module."

--Sean

[1]
http://download.java.net/java/jdk9/docs/api/java/lang/SecurityManager.html#checkPackageAccess-java.lang.String-

Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Alex Buckley
In reply to this post by Volker Simonis
On 5/23/2017 7:44 AM, Volker Simonis wrote:
> So maybe I rephrase my question a little more generally:
>
> Is it required for the verifier to do security and/or access checks
> during the verification phase or could/should these checks be
> postponed to runtime? The issue with verification errors due to
> missing classes from Remi's previous answer is probably a corner case
> of this question.

Verification must perform class loading in order to check subtyping, but
verification does not check access to the loaded classes. To be precise,
verification in JVMS 4.10.1 does not appeal to class resolution (JVMS
5.4.3.1) nor to access control (JVMS 5.4.4). Nor does verification in
JVMS 4.10.1 know what the package.access file is.

What you are seeing when the Security Manager is enabled is that class
loading fails (due to a package.access check in Hotspot) and so
verification fails. The verifier is not performing the package.access
check per se.

Alex
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

David Holmes
In reply to this post by Volker Simonis
On 23/05/2017 10:45 PM, Volker Simonis wrote:

> On Tue, May 23, 2017 at 10:51 AM, David Holmes <[hidden email]> wrote:
>> On 23/05/2017 6:20 PM, Alan Bateman wrote:
>>>
>>> Volker - one suggestion for your experiments is to change your JDK 8
>>> security properties file (java.security) to add "com.sun.crypto.provider."
>>> to the value of the "package.access" property. That should mean you will get
>>> the same AccessControlException with JDK 8 as you do with JDK 9. It may help
>>> to reason about the JDK 8 behavior, including what is triggered by
>>> verification, before looking at 9.
>>>
>>> On the "Error: A JNI error has occurred, please check your installation
>>> and try again" message. It's not core to your questions of course but I
>>> agree it is confusing. Ramanand Patil had a number of attempts on
>>> core-libs-dev to improve this and it may be that more is needed there.
>>
>>
>> Yes I was sure we had agreed to remove that and I don't see it in Oracle JDK
>> so perhaps it is only in OpenJDK launcher?
>>
>
> Hi David,
>
> not sure what you mean? Does the Oracle JDK come with another launcher
> compared to the OpenJDK?

No - I was confusing myself.

> I've just tried with the latest EA build 170 (which I assume is an
> Oracle JDK) and I see exactly the same error:

Ok. I see now that we still have the confusing error message depending
on exactly when the exception is thrown.

David
-----


> java -Djava.security.manager -showversion Tricky ""
> java version "9-ea"
> Java(TM) SE Runtime Environment (build 9-ea+170)
> Java HotSpot(TM) Server VM (build 9-ea+170, mixed mode)
> Error: A JNI error has occurred, please check your installation and try again
> Exception in thread "main" java.security.AccessControlException:
> access denied ("java.lang.RuntimePermission"
> "accessClassInPackage.com.sun.crypto.provider")
>      at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:471)
>      at java.base/java.security.AccessController.checkPermission(AccessController.java:894)
>      at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:561)
>      at java.base/java.lang.SecurityManager.checkPackageAccess(SecurityManager.java:1534)
>      at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:671)
>      at java.base/java.lang.ClassLoader$1.run(ClassLoader.java:669)
>      at java.base/java.security.AccessController.doPrivileged(Native Method)
>      at java.base/java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:669)
>      at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
>      at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3134)
>      at java.base/java.lang.Class.getMethodsRecursive(Class.java:3275)
>      at java.base/java.lang.Class.getMethod0(Class.java:3261)
>      at java.base/java.lang.Class.getMethod(Class.java:2062)
>      at java.base/sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:712)
>      at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:570)
>
> So what do you mean when saying that you don't see it in the Oracle JDK?
>
> Regards,
> Volker
>
>> David
>>
>>
>>> -Alan
>>>
>>
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Volker Simonis
In reply to this post by Alex Buckley
On Tue, May 23, 2017 at 10:01 PM, Alex Buckley <[hidden email]> wrote:

> On 5/23/2017 7:44 AM, Volker Simonis wrote:
>>
>> So maybe I rephrase my question a little more generally:
>>
>> Is it required for the verifier to do security and/or access checks
>> during the verification phase or could/should these checks be
>> postponed to runtime? The issue with verification errors due to
>> missing classes from Remi's previous answer is probably a corner case
>> of this question.
>
>
> Verification must perform class loading in order to check subtyping, but
> verification does not check access to the loaded classes. To be precise,
> verification in JVMS 4.10.1 does not appeal to class resolution (JVMS
> 5.4.3.1) nor to access control (JVMS 5.4.4). Nor does verification in JVMS
> 4.10.1 know what the package.access file is.
>
> What you are seeing when the Security Manager is enabled is that class
> loading fails (due to a package.access check in Hotspot) and so verification
> fails. The verifier is not performing the package.access check per se.
>

OK, so from what you say I understand that the verification errors I
see with the Security Manager enabled are an implementation detail of
HotSpot (because verification uses the same class loading mechanism
like the runtime) which is not required but still acceptable under the
JVMS. Is that correct?

Thanks,
Volker

> Alex
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Alex Buckley
On 5/24/2017 12:13 AM, Volker Simonis wrote:
> OK, so from what you say I understand that the verification errors I
> see with the Security Manager enabled are an implementation detail of
> HotSpot (because verification uses the same class loading mechanism
> like the runtime) which is not required but still acceptable under the
> JVMS. Is that correct?

The JVMS is precise about which exceptions are allowed to be thrown by a
JVM implementation during verification, and AccessControlException is
not one of them. However, the JVMS is only one part of the Java SE
Platform Specification. It is quite proper if another part specifies an
AccessControlException when a class in a restricted package is
referenced by a class without permission.

I'm thinking in particular of the API specification for
SecurityManager::checkPackageAccess. It states, "This method is called
by the loadClass method of class loaders." Plainly, the intention is
that a class (Tricky) which initiates the loading of another class
(com.sun.crypto.provider.SunJCE) can do so only if it has permission to
reference the other class. Unfortunately, the statement as written is
only guaranteed to be true for the built-in class loaders of the Java SE
Platform and not for user-defined class loaders. Accordingly, we will
update the API specification to clarify how a JVM implementation may
support the Security Manager in checking permissions when classes are
loaded and resolved. But to answer your original question, an
application CAN fail because the verifier can't load classes due to
Security Manager restrictions; you may have to grant additional
permissions if application classes wish to reference certain JDK 9 packages.

Alex
Reply | Threaded
Open this post in threaded view
|

Re: JPMS Access Checks, Verification and the Security Manager

Volker Simonis
Thanks Alex,

that makes sense.

Regards,
Volker


On Fri, Jun 2, 2017 at 2:13 AM, Alex Buckley <[hidden email]> wrote:

> On 5/24/2017 12:13 AM, Volker Simonis wrote:
>>
>> OK, so from what you say I understand that the verification errors I
>> see with the Security Manager enabled are an implementation detail of
>> HotSpot (because verification uses the same class loading mechanism
>> like the runtime) which is not required but still acceptable under the
>> JVMS. Is that correct?
>
>
> The JVMS is precise about which exceptions are allowed to be thrown by a JVM
> implementation during verification, and AccessControlException is not one of
> them. However, the JVMS is only one part of the Java SE Platform
> Specification. It is quite proper if another part specifies an
> AccessControlException when a class in a restricted package is referenced by
> a class without permission.
>
> I'm thinking in particular of the API specification for
> SecurityManager::checkPackageAccess. It states, "This method is called by
> the loadClass method of class loaders." Plainly, the intention is that a
> class (Tricky) which initiates the loading of another class
> (com.sun.crypto.provider.SunJCE) can do so only if it has permission to
> reference the other class. Unfortunately, the statement as written is only
> guaranteed to be true for the built-in class loaders of the Java SE Platform
> and not for user-defined class loaders. Accordingly, we will update the API
> specification to clarify how a JVM implementation may support the Security
> Manager in checking permissions when classes are loaded and resolved. But to
> answer your original question, an application CAN fail because the verifier
> can't load classes due to Security Manager restrictions; you may have to
> grant additional permissions if application classes wish to reference
> certain JDK 9 packages.
>
> Alex