Skip to content

Commit c59d194

Browse files
Merge pull request #1638 from matthiasblaesing/replace_security_manager
Replace usage of SecurityManager and AccessController#doPrivileged
2 parents 08f9e8d + f5f17d5 commit c59d194

3 files changed

Lines changed: 171 additions & 17 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Bug Fixes
1818
* [#1715](https://github.com/java-native-access/jna/pull/1715): Fix `UdevDevice.getSysname()` calling `udev_device_get_syspath` instead of `udev_device_get_sysname` - [@dbwiddis](https://github.com/dbwiddis).
1919
* [#1721](https://github.com/java-native-access/jna/pull/1721): Fix switched `serverName`/`domainName` arguments in `Netapi32Util#getDCName` - [@dbwiddis](https://github.com/dbwiddis).
2020
* [#1722](https://github.com/java-native-access/jna/pull/1722): Fix `Advapi32#RegisterServiceCtrlHandler` using wrong `Handler` type - [@dbwiddis](https://github.com/dbwiddis).
21+
* [#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).
2122

2223
Release 5.18.1
2324
==============
@@ -58,7 +59,7 @@ Release 5.16.0
5859

5960
Features
6061
--------
61-
* [#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)
62+
* [#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).
6263

6364
Bug Fixes
6465
---------

src/com/sun/jna/Native.java

Lines changed: 131 additions & 16 deletions
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,57 @@ 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+
// This method makes the assumption that it is only called from register
1604+
// and unregister methods of this class. It is further assumed, that
1605+
// these two methods are called from a static initialized block or from
1606+
// a method of the class to be registered.
1607+
//
1608+
// There are two approaches taken to find the right stack frame:
1609+
// - on modern VMs the StackWalker API is used and the first two entries
1610+
// are skipped. The stack at the time of calling is:
1611+
// 0. Stackframe: #getCallingClass
1612+
// 1. Stackframe: #register or #unregister
1613+
// 2. Stackframe: method of outer caller
1614+
// the last element is the right one
1615+
//
1616+
// - on VMs not supporting the StackWalker, the SecurityManager is used
1617+
// to fetch the classes of the stack. The SecurityManager has
1618+
// getClassContext which returns the classes of the callers. It is
1619+
// protected though and needs to be exposed first. For this the stack
1620+
// trace is:
1621+
// 0. Stackframe: SecurityManagerExposer#getClassContext
1622+
// 1. Stackframe: #getCallingClass
1623+
// 2. Stackframe: #register or #unregister
1624+
// 3. Stackframe: method of outer caller
1625+
if (stackWalkerGetInstance != null) {
1626+
try {
1627+
Object walker = stackWalkerGetInstance.invoke(stackWalkerRetainClassReference);
1628+
Class<?> caller = (Class<?>) stackWalkerWalk.invoke(walker, stackWalkerFilter);
1629+
return caller;
1630+
} catch (Throwable ex) {
1631+
LOG.log(Level.WARNING, "Failed to invoke StackWalker#getInstance or StackWalker#walk", ex);
15301632
}
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");
15341633
}
1535-
if (context.length < 4) {
1536-
throw new IllegalStateException("This method must be called from the static initializer of a class");
1634+
1635+
if (securityManagerExposerConstructor != null) {
1636+
Class<?>[] context = null;
1637+
try {
1638+
Object securityManagerExposer = securityManagerExposerConstructor.invoke();
1639+
context = (Class<?>[]) securityManagerGetClassContext.invoke(securityManagerExposer);
1640+
} catch (Throwable ex) {
1641+
LOG.log(Level.WARNING, "Failed to invoke SecurityManagerExposer#<init> or SecurityManagerExposer#getClassContext", ex);
1642+
}
1643+
1644+
if (context == null) {
1645+
throw new IllegalStateException("The SecurityManager implementation on this platform is broken; you must explicitly provide the class to register");
1646+
}
1647+
if (context.length < 4) {
1648+
throw new IllegalStateException("This method must be called from the static initializer of a class");
1649+
}
1650+
return context[3];
15371651
}
1538-
return context[3];
1652+
1653+
throw new IllegalStateException("Neither the StackWalker, nor the SecurityManager based getCallingClass implementation are useable; you must explicitly provide the class to register");
15391654
}
15401655

15411656
/**
Lines changed: 38 additions & 0 deletions
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)