Skip to content

Commit 69955bd

Browse files
Replace usage of SecurityManager and AccessController#doPrivileged
The SecurityManager class is deprecated for removal since JDK 17. Its getClassContext method is used to optain the calling class when Native#register and Native#unregister are used without the target class being passed in. With JDK 9 the StalkWalker was introduced to make that information available. This change makes both methods available using method handles and prefers the StalkWalker codepath as that is the path expected to be invoked in most cases (on JDK 9+). The AccessController#doProvileged call is replaced by a method handle, assuming, that in an environment where WebStart is available, AccessController is also available. Closes: #1636
1 parent 9e1e377 commit 69955bd

File tree

3 files changed

+149
-17
lines changed

3 files changed

+149
-17
lines changed

CHANGES.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Next Release (5.16.0)
77

88
Features
99
--------
10-
* [#1626](https://github.com/java-native-access/jna/pull/1626): Add caching of field list and field validation in `Structure` along with more efficient reentrant read-write locking instead of synchronized() blocks - [@BrettWooldridge](https://github.com/brettwooldridge)
10+
* [#1626](https://github.com/java-native-access/jna/pull/1626): Add caching of field list and field validation in `Structure` along with more efficient reentrant read-write locking instead of synchronized() blocks - [@BrettWooldridge](https://github.com/brettwooldridge).
11+
* [#1636](https://github.com/java-native-access/jna/issues/1636): Drop hard dependency on java.lang.SecurityManager/java.security.AccessController - [@matthiasblaesing](https://github.com/matthiasblaesing).
1112

1213
Bug Fixes
1314
---------

src/com/sun/jna/Native.java

+109-16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
*/
2424
package com.sun.jna;
2525

26+
import com.sun.jna.Callback.UncaughtExceptionHandler;
27+
import com.sun.jna.Structure.FFIType;
2628
import java.awt.Component;
2729
import java.awt.GraphicsEnvironment;
2830
import java.awt.HeadlessException;
@@ -33,6 +35,10 @@
3335
import java.io.IOException;
3436
import java.io.InputStream;
3537
import java.io.UnsupportedEncodingException;
38+
import java.lang.invoke.MethodHandle;
39+
import java.lang.invoke.MethodHandles;
40+
import java.lang.invoke.MethodHandles.Lookup;
41+
import java.lang.invoke.MethodType;
3642
import java.lang.ref.Reference;
3743
import java.lang.ref.WeakReference;
3844
import java.lang.reflect.Array;
@@ -50,7 +56,6 @@
5056
import java.nio.charset.Charset;
5157
import java.nio.charset.IllegalCharsetNameException;
5258
import java.nio.charset.UnsupportedCharsetException;
53-
import java.security.AccessController;
5459
import java.security.PrivilegedAction;
5560
import java.util.ArrayList;
5661
import java.util.Arrays;
@@ -60,11 +65,9 @@
6065
import java.util.Map;
6166
import java.util.StringTokenizer;
6267
import java.util.WeakHashMap;
63-
64-
import com.sun.jna.Callback.UncaughtExceptionHandler;
65-
import com.sun.jna.Structure.FFIType;
6668
import java.util.logging.Level;
6769
import java.util.logging.Logger;
70+
import java.util.stream.Stream;
6871

6972
/** Provides generation of invocation plumbing for a defined native
7073
* library interface. Also provides various utilities for native operations.
@@ -181,6 +184,16 @@ public void uncaughtException(Callback c, Throwable e) {
181184
private static final int TYPE_BOOL = 4;
182185
private static final int TYPE_LONG_DOUBLE = 5;
183186

187+
private static final MethodHandle stackWalkerGetInstance;
188+
private static final Enum stackWalkerRetainClassReference;
189+
private static final MethodHandle stackWalkerWalk;
190+
private static final Object stackWalkerFilter;
191+
192+
private static final MethodHandle securityManagerExposerConstructor;
193+
private static final MethodHandle securityManagerGetClassContext;
194+
195+
private static final MethodHandle accessControllerDoPrivileged;
196+
184197
static final int MAX_ALIGNMENT;
185198
static final int MAX_PADDING;
186199

@@ -255,6 +268,70 @@ static boolean isCompatibleVersion(String expectedVersion, String nativeVersion)
255268
|| (Platform.isAndroid() && !Platform.isIntel())
256269
? 8 : LONG_SIZE;
257270
MAX_PADDING = (Platform.isMac() && Platform.isPPC()) ? 8 : MAX_ALIGNMENT;
271+
272+
Enum stackWalkerRetainClassReferenceBuilder;
273+
MethodHandle stackWalkerGetInstanceBuilder;
274+
MethodHandle stackWalkerWalkBuilder;
275+
Object stackWalkerFilterBuilder;
276+
try {
277+
Lookup lookup = MethodHandles.lookup();
278+
Class<?> stackWalkerClass = Class.forName("java.lang.StackWalker");
279+
Class<? extends Enum> stackWalkerOptionClass = (Class<? extends Enum>) Class.forName("java.lang.StackWalker$Option");
280+
stackWalkerRetainClassReferenceBuilder = Enum.valueOf(stackWalkerOptionClass, "RETAIN_CLASS_REFERENCE");
281+
stackWalkerGetInstanceBuilder = lookup.findStatic(stackWalkerClass, "getInstance", MethodType.methodType(stackWalkerClass, stackWalkerOptionClass));
282+
stackWalkerWalkBuilder = lookup.findVirtual(stackWalkerClass, "walk", MethodType.methodType(Object.class, java.util.function.Function.class));
283+
Class<?> stackframe = Class.forName("java.lang.StackWalker$StackFrame");
284+
MethodHandle stackFrameGetDeclaringClass = lookup.findVirtual(stackframe, "getDeclaringClass", MethodType.methodType(Class.class));
285+
stackWalkerFilterBuilder = new java.util.function.Function<Stream<Object>, Class<?>>() {
286+
@Override
287+
public Class<?> apply(Stream<Object> t) {
288+
Object stackFrame = t.skip(2).findFirst().get();
289+
try {
290+
return (Class<?>) stackFrameGetDeclaringClass.invoke(stackFrame);
291+
} catch (Throwable ex) {
292+
return null;
293+
}
294+
}
295+
};
296+
297+
} catch (Throwable ex) {
298+
LOG.log(Level.FINE, "Failed to initialize stack accessor method StackWalker", ex);
299+
stackWalkerRetainClassReferenceBuilder = null;
300+
stackWalkerGetInstanceBuilder = null;
301+
stackWalkerWalkBuilder = null;
302+
stackWalkerFilterBuilder = null;
303+
}
304+
stackWalkerRetainClassReference = stackWalkerRetainClassReferenceBuilder;
305+
stackWalkerGetInstance = stackWalkerGetInstanceBuilder;
306+
stackWalkerWalk = stackWalkerWalkBuilder;
307+
stackWalkerFilter = stackWalkerFilterBuilder;
308+
309+
MethodHandle securityManagerExposerConstructorBuilder;
310+
MethodHandle securityManagerGetClassContextBuilder;
311+
try {
312+
Lookup lookup = MethodHandles.lookup();
313+
Class<?> securityManagerExposerClass = Class.forName("com.sun.jna.SecurityManagerExposer");
314+
securityManagerExposerConstructorBuilder = lookup.findConstructor(securityManagerExposerClass, MethodType.methodType(void.class));
315+
securityManagerGetClassContextBuilder = lookup.findVirtual(securityManagerExposerClass, "getClassContext", MethodType.methodType(Class[].class));
316+
} catch (Throwable ex) {
317+
LOG.log(Level.FINE, "Failed to initialize stack accessor method SecurityManager", ex);
318+
securityManagerExposerConstructorBuilder = null;
319+
securityManagerGetClassContextBuilder = null;
320+
}
321+
securityManagerExposerConstructor = securityManagerExposerConstructorBuilder;
322+
securityManagerGetClassContext = securityManagerGetClassContextBuilder;
323+
324+
MethodHandle accessControllerDoPrivilegedBuilder = null;
325+
try {
326+
Lookup lookup = MethodHandles.lookup();
327+
Class<?> accessControllerClass = Class.forName("java.security.AccessController");
328+
accessControllerDoPrivilegedBuilder = lookup.findStatic(accessControllerClass, "doPrivileged", MethodType.methodType(Object.class, PrivilegedAction.class));
329+
} catch (Throwable ex) {
330+
LOG.log(Level.FINE, "Failed to initialize AccessController#doPrivileged", ex);
331+
accessControllerDoPrivilegedBuilder = null;
332+
}
333+
accessControllerDoPrivileged = accessControllerDoPrivilegedBuilder;
334+
258335
System.setProperty("jna.loaded", "true");
259336
}
260337

@@ -1275,7 +1352,7 @@ public static String getWebStartLibraryPath(final String libName) {
12751352
try {
12761353

12771354
final ClassLoader cl = Native.class.getClassLoader();
1278-
Method m = AccessController.doPrivileged(new PrivilegedAction<Method>() {
1355+
Method m = (Method) accessControllerDoPrivileged.invoke(new PrivilegedAction<Method>() {
12791356
@Override
12801357
public Method run() {
12811358
try {
@@ -1294,7 +1371,7 @@ public Method run() {
12941371
}
12951372
return null;
12961373
}
1297-
catch (Exception e) {
1374+
catch (Throwable e) {
12981375
return null;
12991376
}
13001377
}
@@ -1523,19 +1600,35 @@ static Class<?> findDirectMappedClass(Class<?> cls) {
15231600
was made.
15241601
*/
15251602
static Class<?> getCallingClass() {
1526-
Class<?>[] context = new SecurityManager() {
1527-
@Override
1528-
public Class<?>[] getClassContext() {
1529-
return super.getClassContext();
1603+
if (stackWalkerGetInstance != null) {
1604+
try {
1605+
Object walker = stackWalkerGetInstance.invoke(stackWalkerRetainClassReference);
1606+
Class<?> caller = (Class<?>) stackWalkerWalk.invoke(walker, stackWalkerFilter);
1607+
return caller;
1608+
} catch (Throwable ex) {
1609+
LOG.log(Level.WARNING, "Failed to invoke StackWalker#getInstance or StackWalker#walk", ex);
15301610
}
1531-
}.getClassContext();
1532-
if (context == null) {
1533-
throw new IllegalStateException("The SecurityManager implementation on this platform is broken; you must explicitly provide the class to register");
15341611
}
1535-
if (context.length < 4) {
1536-
throw new IllegalStateException("This method must be called from the static initializer of a class");
1612+
1613+
if (securityManagerExposerConstructor != null) {
1614+
Class<?>[] context = null;
1615+
try {
1616+
Object securityManagerExposer = securityManagerExposerConstructor.invoke();
1617+
context = (Class<?>[]) securityManagerGetClassContext.invoke(securityManagerExposer);
1618+
} catch (Throwable ex) {
1619+
LOG.log(Level.WARNING, "Failed to invoke SecurityManagerExposer#<init> or SecurityManagerExposer#getClassContext", ex);
1620+
}
1621+
1622+
if (context == null) {
1623+
throw new IllegalStateException("The SecurityManager implementation on this platform is broken; you must explicitly provide the class to register");
1624+
}
1625+
if (context.length < 4) {
1626+
throw new IllegalStateException("This method must be called from the static initializer of a class");
1627+
}
1628+
return context[3];
15371629
}
1538-
return context[3];
1630+
1631+
throw new IllegalStateException("Neither the StackWalker, nor the SecurityManager based getCallingClass implementation are useable; you must explicitly provide the class to register");
15391632
}
15401633

15411634
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* Copyright (c) 2024 Matthias Bläsing, All Rights Reserved
2+
*
3+
* The contents of this file is dual-licensed under 2
4+
* alternative Open Source/Free licenses: LGPL 2.1 or later and
5+
* Apache License 2.0. (starting with JNA version 4.0.0).
6+
*
7+
* You can freely decide which license you want to apply to
8+
* the project.
9+
*
10+
* You may obtain a copy of the LGPL License at:
11+
*
12+
* http://www.gnu.org/licenses/licenses.html
13+
*
14+
* A copy is also included in the downloadable source code package
15+
* containing JNA, in file "LGPL2.1".
16+
*
17+
* You may obtain a copy of the Apache License at:
18+
*
19+
* http://www.apache.org/licenses/
20+
*
21+
* A copy is also included in the downloadable source code package
22+
* containing JNA, in file "AL2.0".
23+
*/
24+
package com.sun.jna;
25+
26+
/**
27+
* Helper class to make {@link SecurityManager#getClassContext()} accessible
28+
* from {@link Native}. It is used as a fallback, if {@link StackWalker} is not
29+
* availble.
30+
*/
31+
class SecurityManagerExposer extends SecurityManager {
32+
33+
@Override
34+
protected Class<?>[] getClassContext() {
35+
return super.getClassContext();
36+
}
37+
38+
}

0 commit comments

Comments
 (0)