Skip to content

Commit

Permalink
Prevent illegal access exceptions, when retrieving options for privat…
Browse files Browse the repository at this point in the history
…e library interfaces with an instance field

 Since direct mapped classes may retrieve options for the first time, only when a callback is passed or returned, this could happen unexpectedly.
  • Loading branch information
fkistner committed Dec 3, 2020
1 parent 331ddc8 commit d47af88
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 2 deletions.
4 changes: 2 additions & 2 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1256,7 +1256,7 @@ cd ..
</and>
</condition>
<property name="tests.platform" value=""/>
<property name="tests.include" value="com/sun/jna/*Test.java"/>
<property name="tests.include" value="com/sun/jna/*Test.java com/sun/jna/different_package/*Test.java"/>
<property name="tests.exclude" value=""/>
<property name="tests.exclude-patterns" value=""/>
<condition property="java.awt.headless" value="true">
Expand All @@ -1282,7 +1282,7 @@ cd ..
<formatter type="xml"/>
<batchtest todir="${results.junit}">
<fileset dir="${test.src}" excludes="${tests.exclude-patterns}">
<include name="${tests.include}"/>
<patternset includes="${tests.include}"/>
<include name="${tests.platform}"/>
<exclude name="${tests.exclude}"/>
</fileset>
Expand Down
1 change: 1 addition & 0 deletions src/com/sun/jna/Native.java
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ private static void loadLibraryInstance(Class<?> cls) {
if (field.getType() == cls
&& Modifier.isStatic(field.getModifiers())) {
// Ensure the field gets initialized by reading it
field.setAccessible(true); // interface might be private
libraries.put(cls, new WeakReference<Object>(field.get(null)));
break;
}
Expand Down
82 changes: 82 additions & 0 deletions test/com/sun/jna/different_package/PrivateDirectCallbacksTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.sun.jna.different_package;

import com.sun.jna.*;
import junit.framework.TestCase;

public class PrivateDirectCallbacksTest extends TestCase {
private interface PrivateWithCallbackArgumentTestLibrary extends Library {
PrivateWithCallbackArgumentTestLibrary INSTANCE = new DirectPrivateWithCallbackArgumentTestLibrary();

interface VoidCallback extends Callback {
void callback();
}

void callVoidCallback(VoidCallback c);
}

private static class DirectPrivateWithCallbackArgumentTestLibrary implements PrivateWithCallbackArgumentTestLibrary {
@Override
public native void callVoidCallback(VoidCallback c);

static {
Native.register("testlib");
}
}

public void testCallVoidCallback() {
final boolean[] called = {false};
PrivateWithCallbackArgumentTestLibrary.VoidCallback cb = new PrivateWithCallbackArgumentTestLibrary.VoidCallback() {
@Override
public void callback() {
called[0] = true;
}
};
PrivateWithCallbackArgumentTestLibrary.INSTANCE.callVoidCallback(cb);
assertTrue("Callback not called", called[0]);
}

private interface PrivateWithCallbackReturnTestLibrary extends Library {
PrivateWithCallbackReturnTestLibrary INSTANCE = new DirectPrivateWithCallbackReturnTestLibrary();

interface Int32CallbackX extends Callback {
int callback(int arg);
}

Int32CallbackX returnCallback();

Int32CallbackX returnCallbackArgument(Int32CallbackX cb);
}

private static class DirectPrivateWithCallbackReturnTestLibrary implements PrivateWithCallbackReturnTestLibrary {
@Override
public native Int32CallbackX returnCallback();

@Override
public native Int32CallbackX returnCallbackArgument(Int32CallbackX cb);

static {
Native.register("testlib");
}
}

public void testInvokeCallback() {
PrivateWithCallbackReturnTestLibrary.Int32CallbackX cb = PrivateWithCallbackReturnTestLibrary.INSTANCE.returnCallback();
assertNotNull("Callback should not be null", cb);
assertEquals("Callback should be callable", 1, cb.callback(1));

PrivateWithCallbackReturnTestLibrary.Int32CallbackX cb2 = new PrivateWithCallbackReturnTestLibrary.Int32CallbackX() {
@Override
public int callback(int arg) {
return 0;
}
};
assertSame("Java callback should be looked up",
cb2, PrivateWithCallbackReturnTestLibrary.INSTANCE.returnCallbackArgument(cb2));
assertSame("Existing native function wrapper should be reused",
cb, PrivateWithCallbackReturnTestLibrary.INSTANCE.returnCallbackArgument(cb));
}

public static void main(java.lang.String[] argList) {
junit.textui.TestRunner.run(PrivateDirectCallbacksTest.class);
}
}
31 changes: 31 additions & 0 deletions test/com/sun/jna/different_package/PrivateLibraryInfoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.sun.jna.different_package;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import junit.framework.TestCase;

public class PrivateLibraryInfoTest extends TestCase {
private interface TestLibrary extends Library {
@SuppressWarnings("unused")
TestLibrary INSTANCE = new TestLibrary() {
};

interface VoidCallback extends Callback {
void callback();
}
}

public void testLibraryInfo() {
assertTrue(Native.getLibraryOptions(TestLibrary.class).containsKey(Library.OPTION_TYPE_MAPPER));
}

public void testCallbackLibraryInfo() {
TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {
@Override
public void callback() {
}
};
assertTrue(Native.getLibraryOptions(cb.getClass()).containsKey(Library.OPTION_TYPE_MAPPER));
}
}

0 comments on commit d47af88

Please sign in to comment.