JDK-8191053 : Provide a mechanism to make system's security manager immutable
  • Type: Enhancement
  • Component: security-libs
  • Sub-Component: java.security
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2017-11-09
  • Updated: 2018-10-17
  • Resolved: 2018-10-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 12
12 b16Fixed
Related Reports
CSR :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Sub Tasks
JDK-8212047 :  
Description
Most applications run without a security manager.  When a security manager is installed, typically it's set via the -Djava.security.manager system property at startup and should not be changed during runtime.   

System::setSecurityManager allows a security manager to be set programmatically. That can incur unnecessary runtime overhead, even for applications that do not install a SecurityManager. These applications ideally should not have to incur the cost of supporting a SecurityManager if it is not used.

If the system's security manager can be guaranteed to be immutable, then various startup and runtime performance improvements can be applied.  Here are a few of the costs due to a mutable security manager:

1. There are lots of "if (sm != null)" checks in the runtime.  An immutable security manager can benefit from JIT constant fold that can impact performance of many security-sensitive methods (see JDK-8130289).

2. FilePermission and various Permission objects are constructed even if a security manager is disabled and no permission check is done at runtime.  e.g. FilePermission initialization causes NIO to initialize.

3. AccessControlContexts are captured in various places at runtime but never used.

4. AccessController::doPrivileged may create an additional anonymous class to prepare the privileged action invocation.   To avoid such startup overhead, there are many places in java.base with code like this: "if (sm != null) doPrivileged(.....) else action;"

This gives some examples.

Proposal for JDK 12:

Add new tokens ("allow" and "disallow") to the "java.security.manager" system property to allow or disallow the setting of the security manager at runtime, ex: -Djava.security.manager=disallow

Change System::setSecurityManager to throw an UnsupportedOperationException when the "java.security.manager" system property is set to "disallow".

See the CSR for exact details.

Future potential changes beyond JDK 12:

1. If this system property is set and no security manager is enabled, improve the startup performance to create an empty permission object, stop capturing ACC, and possibly have doPrivileged to invoke the action directly as a fast path.

2. Change to disallow setting security manager at runtime by default in a future release (TBD). 

Tests are one typical case that sets the security manager at runtime.  A test framework like jtreg supports running multiple tests in same VM and so it would have to capture the current security manager, if present, set to the one the test desires (e.g. configure the test security policy) and then restore to the captured one when the test completes.   Some existing applications may set security manager programmatically while it isn't clear why it can't be installed during startup.
Comments
Initial review thread at security-dev: http://mail.openjdk.java.net/pipermail/security-dev/2018-September/018192.html
17-09-2018

Performance results on latest test build from Claes: One microbenchmark I've looked at in the past is simply getting the class loader for a class, eg., calling into this code in java.lang.Class: @CallerSensitive @ForceInline // to ensure Reflection.getCallerClass optimization public ClassLoader getClassLoader() { ClassLoader cl = getClassLoader0(); if (cl == null) return null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass()); } return cl; } The baseline and test builds generate the exact thing OOTB or with -Djdk.allowSecurityManager=true: c2, level 4, org.openjdk.Clazz::getClassLoader, version 467 (73 bytes) # {method} {0x00007f2137febc48} 'getClassLoader' '()Ljava/lang/ClassLoader;' in 'org/openjdk/Clazz' # [sp+0x30] (sp of caller) 0x00007f215c553320: mov 0x8(%rsi),%r10d 0x00007f215c553324: mov $0x800000000,%r12 0x00007f215c55332e: add %r12,%r10 0x00007f215c553331: xor %r12,%r12 0x00007f215c553334: cmp %r10,%rax 0x00007f215c553337: jne 0x00007f2154a83a80 ; {runtime_call ic_miss_stub} 0x00007f215c55333d: xchg %ax,%ax [Verified Entry Point] 2.80% 2.54% 0x00007f215c553340: mov %eax,-0x14000(%rsp) 4.26% 4.38% 0x00007f215c553347: push %rbp 0.88% 0.96% 0x00007f215c553348: sub $0x20,%rsp ;*synchronization entry ; - org.openjdk.Clazz::getClassLoader@-1 (line 56) 2.77% 3.46% 0x00007f215c55334c: mov $0x71669b9e8,%r10 ; {oop(a 'java/lang/Class'{0x000000071669b9e8} = 'org/openjdk/Clazz')} 3.37% 3.74% 0x00007f215c553356: mov 0x70(%r10),%r11d ;*getstatic c1 {reexecute=0 rethrow=0 return_oop=0} ; - org.openjdk.Clazz::getClassLoader@0 (line 56) 0.90% 0.96% 0x00007f215c55335a: mov 0x1c(%r12,%r11,8),%r11d ;*getfield classLoader {reexecute=0 rethrow=0 return_oop=0} ; - java.lang.Class::getClassLoader0@1 (line 813) ; - java.lang.Class::getClassLoader@1 (line 802) ; - org.openjdk.Clazz::getClassLoader@3 (line 56) ; implicit exception: dispatches to 0x00007f215c55339e 2.20% 2.42% 0x00007f215c55335f: test %r11d,%r11d ��� 0x00007f215c553362: je 0x00007f215c55338a ;*ifnonnull {reexecute=0 rethrow=0 return_oop=0} ��� ; - java.lang.Class::getClassLoader@6 (line 803) ��� ; - org.openjdk.Clazz::getClassLoader@3 (line 56) 1.29% 0.85% ��� 0x00007f215c553364: mov $0x716b00d60,%r10 ; {oop(a 'java/lang/Class'{0x0000000716b00d60} = 'java/lang/System')} 2.80% 3.50% ��� 0x00007f215c55336e: mov 0x7c(%r10),%ebp ;*getstatic security {reexecute=0 rethrow=0 return_oop=0} // SecurityManager sm = System.getSecurityManager() is folded into a load of the System.security field ��� ; - java.lang.System::getSecurityManager@0 (line 369) ��� ; - java.lang.Class::getClassLoader@11 (line 805) ��� ; - org.openjdk.Clazz::getClassLoader@3 (line 56) 1.00% 0.67% ��� 0x00007f215c553372: test %ebp,%ebp ������ 0x00007f215c553374: jne 0x00007f215c55338e ;*ifnull {reexecute=0 rethrow=0 return_oop=0} // if (sm != null) { ... ������ ; - java.lang.Class::getClassLoader@16 (line 806) ������ ; - org.openjdk.Clazz::getClassLoader@3 (line 56) 2.22% 2.02% ������ 0x00007f215c553376: lea (%r12,%r11,8),%rax ;*invokevirtual getClassLoader {reexecute=0 rethrow=0 return_oop=0} ������ ; - org.openjdk.Clazz::getClassLoader@3 (line 56) 0.78% 0.86% ��������� 0x00007f215c55337a: add $0x20,%rsp 3.08% 3.18% ��������� 0x00007f215c55337e: pop %rbp 1.73% 1.69% ��������� 0x00007f215c55337f: mov 0xe8(%r15),%r10 1.94% 2.59% ��������� 0x00007f215c553386: test %eax,(%r10) ; {poll_return} 1.31% 1.16% ��������� 0x00007f215c553389: retq ��������� 0x00007f215c55338a: xor %eax,%eax ������ 0x00007f215c55338c: jmp 0x00007f215c55337a ��� 0x00007f215c55338e: mov $0xffffff55,%esi 0x00007f215c553393: mov %r11d,(%rsp) 0x00007f215c553397: callq 0x00007f2154a85700 ; ImmutableOopMap{rbp=NarrowOop [0]=NarrowOop } ;*ifnull {reexecute=1 rethrow=0 return_oop=0} ; - java.lang.Class::getClassLoader@16 (line 806) ; - org.openjdk.Clazz::getClassLoader@3 (line 56) ; {runtime_call UncommonTrapBlob} 0x00007f215c55339c: ud2a ;*ifnull {reexecute=0 rethrow=0 return_oop=0} With -Djdk.allowSecurityManager=false, the code loading and checking the security field is gone: c2, level 4, org.openjdk.Clazz::getClassLoader, version 451 (55 bytes) # {method} {0x00007fad554cbc48} 'getClassLoader' '()Ljava/lang/ClassLoader;' in 'org/openjdk/Clazz' # [sp+0x20] (sp of caller) 0x00007fad5fd10ba0: mov 0x8(%rsi),%r10d 0x00007fad5fd10ba4: mov $0x800000000,%r12 0x00007fad5fd10bae: add %r12,%r10 0x00007fad5fd10bb1: xor %r12,%r12 0x00007fad5fd10bb4: cmp %r10,%rax 0x00007fad5fd10bb7: jne 0x00007fad58242a80 ; {runtime_call ic_miss_stub} 0x00007fad5fd10bbd: xchg %ax,%ax [Verified Entry Point] 2.73% 2.85% 0x00007fad5fd10bc0: mov %eax,-0x14000(%rsp) 4.17% 4.96% 0x00007fad5fd10bc7: push %rbp 1.45% 1.17% 0x00007fad5fd10bc8: sub $0x10,%rsp ;*synchronization entry ; - org.openjdk.Clazz::getClassLoader@-1 (line 56) 2.46% 2.89% 0x00007fad5fd10bcc: mov $0x71669b9e8,%r10 ; {oop(a 'java/lang/Class'{0x000000071669b9e8} = 'org/openjdk/Clazz')} 3.28% 3.91% 0x00007fad5fd10bd6: mov 0x70(%r10),%r11d ;*getstatic c1 {reexecute=0 rethrow=0 return_oop=0} ; - org.openjdk.Clazz::getClassLoader@0 (line 56) 1.67% 1.54% 0x00007fad5fd10bda: mov 0x1c(%r12,%r11,8),%r10d ;*getfield classLoader {reexecute=0 rethrow=0 return_oop=0} ; - java.lang.Class::getClassLoader0@1 (line 813) ; - java.lang.Class::getClassLoader@1 (line 802) ; - org.openjdk.Clazz::getClassLoader@3 (line 56) ; implicit exception: dispatches to 0x00007fad5fd10bfc 2.46% 2.15% 0x00007fad5fd10bdf: test %r10d,%r10d ��� 0x00007fad5fd10be2: je 0x00007fad5fd10bf8 ;*ifnonnull {reexecute=0 rethrow=0 return_oop=0} ��� ; - java.lang.Class::getClassLoader@6 (line 803) ��� ; - org.openjdk.Clazz::getClassLoader@3 (line 56) 1.55% 2.05% ��� 0x00007fad5fd10be4: lea (%r12,%r10,8),%rax 2.97% 3.40% ������ 0x00007fad5fd10be8: add $0x10,%rsp 1.07% 1.86% ������ 0x00007fad5fd10bec: pop %rbp 3.80% 3.14% ������ 0x00007fad5fd10bed: mov 0xe8(%r15),%r10 1.34% 1.29% ������ 0x00007fad5fd10bf4: test %eax,(%r10) ; {poll_return} 2.46% 2.44% ������ 0x00007fad5fd10bf7: retq ������ 0x00007fad5fd10bf8: xor %eax,%eax ��� 0x00007fad5fd10bfa: jmp 0x00007fad5fd10be8 0x00007fad5fd10bfc: mov $0xfffffff6,%esi 0x00007fad5fd10c01: xchg %ax,%ax 0x00007fad5fd10c03: callq 0x00007fad58244700 ; ImmutableOopMap{} ;*invokevirtual getClassLoader {reexecute=0 rethrow=0 return_oop=0} ; - org.openjdk.Clazz::getClassLoader@3 (line 56) ; {runtime_call UncommonTrapBlob} 0x00007fad5fd10c08: ud2a ;*invokevirtual getClassLoader {reexecute=0 rethrow=0 return_oop=0} ; - org.openjdk.Clazz::getClassLoader@3 (line 56) The actual performance numbers in this particular microbenchmark isn't too interesting, but there's actually a small but measurable speedup (1.08x) when saturating the system on my dual-socket workstation: -Djdk.allowSecurityManager=true Benchmark Mode Cnt Score Error Units Clazz.getClassLoader avgt 25 41.431 �� 0.110 ns/op -Djdk.allowSecurityManager=false Benchmark Mode Cnt Score Error Units Clazz.getClassLoader avgt 25 38.320 �� 0.160 ns/op
11-06-2018