8221642: AccessibleObject::setAccessible throws NPE when invoked by JNI code with no java frame on stack

Reviewed-by: alanb
This commit is contained in:
Mandy Chung 2022-02-01 00:09:35 +00:00
parent 4dbebb62aa
commit 9c0104b9c9
3 changed files with 174 additions and 27 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -168,6 +168,15 @@ public class AccessibleObject implements AnnotatedElement {
* open module. </li>
* </ul>
*
* <p> This method may be used by <a href="{@docRoot}/../specs/jni/index.html">JNI code</a>
* with no caller class on the stack to enable access to a {@link Member member}
* of {@link Member#getDeclaringClass() declaring class} {@code D} if and only if:
* <ul>
* <li> The member is {@code public} and {@code D} is {@code public} in
* a package that the module containing {@code D} {@link
* Module#isExported(String,Module) exports} unconditionally. </li>
* </ul>
*
* <p> This method cannot be used to enable access to private members,
* members with default (package) access, protected instance members, or
* protected constructors when the declaring class is in a different module
@ -246,6 +255,11 @@ public class AccessibleObject implements AnnotatedElement {
* }
* }</pre>
*
* <p> If this method is invoked by <a href="{@docRoot}/../specs/jni/index.html">JNI code</a>
* with no caller class on the stack, the {@code accessible} flag can
* only be set if the member and the declaring class are public, and
* the class is in a package that is exported unconditionally. </p>
*
* <p> If there is a security manager, its {@code checkPermission} method
* is first called with a {@code ReflectPermission("suppressAccessChecks")}
* permission. </p>
@ -304,6 +318,16 @@ public class AccessibleObject implements AnnotatedElement {
throw new IllegalCallerException(); // should not happen
}
if (caller == null) {
// No caller frame when a native thread attaches to the VM
// only allow access to a public accessible member
boolean canAccess = Reflection.verifyPublicMemberAccess(declaringClass, declaringClass.getModifiers());
if (!canAccess && throwExceptionIfDenied) {
throwInaccessibleObjectException(caller, declaringClass);
}
return canAccess;
}
Module callerModule = caller.getModule();
Module declaringModule = declaringClass.getModule();
@ -312,12 +336,7 @@ public class AccessibleObject implements AnnotatedElement {
if (!declaringModule.isNamed()) return true;
String pn = declaringClass.getPackageName();
int modifiers;
if (this instanceof Executable) {
modifiers = ((Executable) this).getModifiers();
} else {
modifiers = ((Field) this).getModifiers();
}
int modifiers = ((Member)this).getModifiers();
// class is public and package is exported to caller
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
@ -341,25 +360,37 @@ public class AccessibleObject implements AnnotatedElement {
}
if (throwExceptionIfDenied) {
// not accessible
String msg = "Unable to make ";
if (this instanceof Field)
msg += "field ";
msg += this + " accessible: " + declaringModule + " does not \"";
if (isClassPublic && Modifier.isPublic(modifiers))
msg += "exports";
else
msg += "opens";
msg += " " + pn + "\" to " + callerModule;
InaccessibleObjectException e = new InaccessibleObjectException(msg);
if (printStackTraceWhenAccessFails()) {
e.printStackTrace(System.err);
}
throw e;
throwInaccessibleObjectException(caller, declaringClass);
}
return false;
}
private void throwInaccessibleObjectException(Class<?> caller, Class<?> declaringClass) {
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
String pn = declaringClass.getPackageName();
int modifiers = ((Member)this).getModifiers();
// not accessible
String msg = "Unable to make ";
if (this instanceof Field)
msg += "field ";
msg += this + " accessible";
msg += caller == null ? " by JNI attached native thread with no caller frame: " : ": ";
msg += declaringClass.getModule() + " does not \"";
if (isClassPublic && Modifier.isPublic(modifiers))
msg += "exports";
else
msg += "opens";
msg += " " + pn + "\"" ;
if (caller != null)
msg += " to " + caller.getModule();
InaccessibleObjectException e = new InaccessibleObjectException(msg);
if (printStackTraceWhenAccessFails()) {
e.printStackTrace(System.err);
}
throw e;
}
private boolean isSubclassOf(Class<?> queryClass, Class<?> ofClass) {
while (queryClass != null) {
if (queryClass == ofClass) {
@ -409,7 +440,11 @@ public class AccessibleObject implements AnnotatedElement {
* is set to {@code true}, i.e. the checks for Java language access control
* are suppressed, or if the caller can access the member as
* specified in <cite>The Java Language Specification</cite>,
* with the variation noted in the class description. </p>
* with the variation noted in the class description.
* If this method is invoked by <a href="{@docRoot}/../specs/jni/index.html">JNI code</a>
* with no caller class on the stack, this method returns {@code true}
* if the member and the declaring class are public, and the class is in
* a package that is exported unconditionally. </p>
*
* @param obj an instance object of the declaring class of this reflected
* object if it is an instance method or field