mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-09 10:06:13 +00:00
[JENKINS-64423, JENKINS-46618] Resolve all core caused startup Illegal reflective access warnings (#5110)
Co-authored-by: Jesse Glick <jglick@cloudbees.com>
This commit is contained in:
1 parent
03c8d8628e
commit
ae88291731
6 files changed
+158
-78
No files matched your search
@@ -486,12 +486,6 @@ public abstract class ExtensionFinder implements ExtensionPoint {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ClassLoader ecl = c.getClassLoader();
|
||||
if (ecl != null) { // Not bootstrap classloader
|
||||
Method m = ClassLoader.class.getDeclaredMethod("resolveClass", Class.class);
|
||||
m.setAccessible(true);
|
||||
m.invoke(ecl, c);
|
||||
}
|
||||
for (Class<?> cc = c; cc != Object.class && cc != null; cc = cc.getSuperclass()) {
|
||||
/*
|
||||
* See {@link com.google.inject.spi.InjectionPoint#getInjectionPoints(TypeLiteral, boolean, Errors)}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class PluginFirstClassLoader
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Enumeration findResources( String name )
|
||||
public Enumeration findResources( String name )
|
||||
throws IOException
|
||||
{
|
||||
return super.findResources( name );
|
||||
|
||||
@@ -4,41 +4,29 @@ import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Enumeration;
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import jenkins.util.AntClassLoader;
|
||||
import jenkins.util.AntWithFindResourceClassLoader;
|
||||
|
||||
/**
|
||||
* Reflective access to various {@link ClassLoader} methods which are otherwise {@code protected}.
|
||||
* <p>
|
||||
* Initially tries to access methods using known classloaders in use that expose the methods
|
||||
* to prevent illegal reflective access errors on Java 11+
|
||||
* Then falls back to accessing the {@link ClassLoader} methods.
|
||||
* <p>
|
||||
* All reflection method initialisation is delayed until first use so that we don't access the methods if we don't need to.
|
||||
* <p>
|
||||
* Note: Currently there is no known production use-case for the fallback case of accessing these methods via reflection:
|
||||
* the {@code JenkinsRule} tests use a different classloader,
|
||||
* but once that is made consistent with production Jenkins we can re-evaluate the fallback code.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class ClassLoaderReflectionToolkit {
|
||||
|
||||
private static final Method FIND_CLASS, FIND_LOADED_CLASS, FIND_RESOURCE, FIND_RESOURCES, GET_CLASS_LOADING_LOCK;
|
||||
|
||||
static {
|
||||
try {
|
||||
FIND_CLASS = ClassLoader.class.getDeclaredMethod("findClass",String.class);
|
||||
FIND_CLASS.setAccessible(true);
|
||||
FIND_LOADED_CLASS = ClassLoader.class.getDeclaredMethod("findLoadedClass",String.class);
|
||||
FIND_LOADED_CLASS.setAccessible(true);
|
||||
FIND_RESOURCE = ClassLoader.class.getDeclaredMethod("findResource",String.class);
|
||||
FIND_RESOURCE.setAccessible(true);
|
||||
FIND_RESOURCES = ClassLoader.class.getDeclaredMethod("findResources",String.class);
|
||||
FIND_RESOURCES.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
Method gCLL;
|
||||
try {
|
||||
gCLL = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
|
||||
gCLL.setAccessible(true);
|
||||
} catch (NoSuchMethodException x) {
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
GET_CLASS_LOADING_LOCK = gCLL;
|
||||
}
|
||||
|
||||
private static <T extends Exception> Object invoke(Method method, Class<T> exception, Object obj, Object... args) throws T {
|
||||
try {
|
||||
return method.invoke(obj, args);
|
||||
@@ -59,7 +47,25 @@ public class ClassLoaderReflectionToolkit {
|
||||
}
|
||||
|
||||
private static Object getClassLoadingLock(ClassLoader cl, String name) {
|
||||
return invoke(GET_CLASS_LOADING_LOCK, RuntimeException.class, cl, name);
|
||||
if (cl instanceof AntWithFindResourceClassLoader) {
|
||||
return ((AntWithFindResourceClassLoader) cl).getClassLoadingLock(name);
|
||||
}
|
||||
return invoke(GetClassLoadingLock.GET_CLASS_LOADING_LOCK, RuntimeException.class, cl, name);
|
||||
}
|
||||
|
||||
private static class GetClassLoadingLock {
|
||||
private static final Method GET_CLASS_LOADING_LOCK;
|
||||
|
||||
static {
|
||||
Method gCLL;
|
||||
try {
|
||||
gCLL = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
|
||||
gCLL.setAccessible(true);
|
||||
} catch (NoSuchMethodException x) {
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
GET_CLASS_LOADING_LOCK = gCLL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +74,27 @@ public class ClassLoaderReflectionToolkit {
|
||||
*/
|
||||
public static @CheckForNull Class<?> _findLoadedClass(ClassLoader cl, String name) {
|
||||
synchronized (getClassLoadingLock(cl, name)) {
|
||||
return (Class) invoke(FIND_LOADED_CLASS, RuntimeException.class, cl, name);
|
||||
Class<?> c;
|
||||
if (cl instanceof AntWithFindResourceClassLoader) {
|
||||
c = ((AntWithFindResourceClassLoader) cl).findLoadedClass2(name);
|
||||
} else {
|
||||
c = (Class) invoke(FindLoadedClass.FIND_LOADED_CLASS, RuntimeException.class, cl, name);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FindLoadedClass {
|
||||
private static final Method FIND_LOADED_CLASS;
|
||||
|
||||
static {
|
||||
try {
|
||||
FIND_LOADED_CLASS = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
FIND_LOADED_CLASS.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,17 +103,57 @@ public class ClassLoaderReflectionToolkit {
|
||||
* @since 1.553
|
||||
*/
|
||||
public static @NonNull Class<?> _findClass(ClassLoader cl, String name) throws ClassNotFoundException {
|
||||
if (cl instanceof AntClassLoader) {
|
||||
return ((AntClassLoader) cl).findClass(name);
|
||||
}
|
||||
|
||||
synchronized (getClassLoadingLock(cl, name)) {
|
||||
return (Class) invoke(FIND_CLASS, ClassNotFoundException.class, cl, name);
|
||||
return (Class) invoke(FindClass.FIND_CLASS, ClassNotFoundException.class, cl, name);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FindClass {
|
||||
private static final Method FIND_CLASS;
|
||||
|
||||
static {
|
||||
try {
|
||||
FIND_CLASS = ClassLoader.class.getDeclaredMethod("findClass",String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
FIND_CLASS.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls {@link ClassLoader#findResource}.
|
||||
* @since 1.553
|
||||
*/
|
||||
public static @CheckForNull URL _findResource(ClassLoader cl, String name) {
|
||||
return (URL) invoke(FIND_RESOURCE, RuntimeException.class, cl, name);
|
||||
URL url;
|
||||
if (cl instanceof AntWithFindResourceClassLoader) {
|
||||
url = ((AntWithFindResourceClassLoader) cl).findResource(name);
|
||||
} else if (cl instanceof URLClassLoader) {
|
||||
url = ((URLClassLoader) cl).findResource(name);
|
||||
} else {
|
||||
url = (URL) invoke(FindResource.FIND_RESOURCE, RuntimeException.class, cl, name);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
private static class FindResource {
|
||||
private static final Method FIND_RESOURCE;
|
||||
|
||||
static {
|
||||
try {
|
||||
FIND_RESOURCE = ClassLoader.class.getDeclaredMethod("findResource", String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
FIND_RESOURCE.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +161,27 @@ public class ClassLoaderReflectionToolkit {
|
||||
* @since 1.553
|
||||
*/
|
||||
public static @NonNull Enumeration<URL> _findResources(ClassLoader cl, String name) throws IOException {
|
||||
return (Enumeration<URL>) invoke(FIND_RESOURCES, IOException.class, cl, name);
|
||||
Enumeration<URL> urls;
|
||||
if (cl instanceof AntWithFindResourceClassLoader) {
|
||||
urls = ((AntWithFindResourceClassLoader) cl).findResources(name);
|
||||
} else {
|
||||
urls = (Enumeration<URL>) invoke(FindResources.FIND_RESOURCES, IOException.class, cl, name);
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
private static class FindResources {
|
||||
private static final Method FIND_RESOURCES;
|
||||
|
||||
static {
|
||||
try {
|
||||
FIND_RESOURCES = ClassLoader.class.getDeclaredMethod("findResources", String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
FIND_RESOURCES.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated unsafe */
|
||||
@@ -105,7 +191,7 @@ public class ClassLoaderReflectionToolkit {
|
||||
@Deprecated
|
||||
public Class findLoadedClass(ClassLoader cl, String name) throws InvocationTargetException {
|
||||
try {
|
||||
return (Class)FIND_LOADED_CLASS.invoke(cl,name);
|
||||
return (Class)FindLoadedClass.FIND_LOADED_CLASS.invoke(cl,name);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@@ -115,7 +201,7 @@ public class ClassLoaderReflectionToolkit {
|
||||
@Deprecated
|
||||
public Class findClass(ClassLoader cl, String name) throws InvocationTargetException {
|
||||
try {
|
||||
return (Class)FIND_CLASS.invoke(cl,name);
|
||||
return (Class)FindClass.FIND_CLASS.invoke(cl,name);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@@ -125,7 +211,7 @@ public class ClassLoaderReflectionToolkit {
|
||||
@Deprecated
|
||||
public URL findResource(ClassLoader cl, String name) throws InvocationTargetException {
|
||||
try {
|
||||
return (URL)FIND_RESOURCE.invoke(cl,name);
|
||||
return (URL)FindResource.FIND_RESOURCE.invoke(cl,name);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
@@ -135,7 +221,7 @@ public class ClassLoaderReflectionToolkit {
|
||||
@Deprecated
|
||||
public Enumeration<URL> findResources(ClassLoader cl, String name) throws InvocationTargetException {
|
||||
try {
|
||||
return (Enumeration<URL>)FIND_RESOURCES.invoke(cl,name);
|
||||
return (Enumeration<URL>)FindResources.FIND_RESOURCES.invoke(cl,name);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package jenkins.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.UnprotectedRootAction;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import jenkins.ClassLoaderReflectionToolkit;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
@@ -14,8 +16,6 @@ import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
@@ -83,41 +83,26 @@ public class AssetManager implements UnprotectedRootAction {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (path.contains("..")) // crude avoidance of directory traversal attack
|
||||
throw new IllegalArgumentException(path);
|
||||
if (path.contains("..")) // crude avoidance of directory traversal attack
|
||||
throw new IllegalArgumentException(path);
|
||||
|
||||
String name;
|
||||
if (path.charAt(0) == '/') {
|
||||
name = "assets" + path;
|
||||
} else {
|
||||
name = "assets/" + path;
|
||||
}
|
||||
|
||||
ClassLoader cl = Jenkins.class.getClassLoader();
|
||||
URL url = (URL) $findResource.invoke(cl, name);
|
||||
if (url==null) {
|
||||
// pick the last one, which is the one closest to the leaf of the classloader tree.
|
||||
Enumeration<URL> e = cl.getResources(name);
|
||||
while (e.hasMoreElements()) {
|
||||
url = e.nextElement();
|
||||
}
|
||||
}
|
||||
return url;
|
||||
} catch (InvocationTargetException|IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
String name;
|
||||
if (path.charAt(0) == '/') {
|
||||
name = "assets" + path;
|
||||
} else {
|
||||
name = "assets/" + path;
|
||||
}
|
||||
|
||||
ClassLoader cl = Jenkins.class.getClassLoader();
|
||||
URL url = ClassLoaderReflectionToolkit._findResource(cl, name);
|
||||
if (url == null) {
|
||||
// pick the last one, which is the one closest to the leaf of the classloader tree.
|
||||
Enumeration<URL> e = cl.getResources(name);
|
||||
while (e.hasMoreElements()) {
|
||||
url = e.nextElement();
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private static final Method $findResource = init();
|
||||
|
||||
private static Method init() {
|
||||
try {
|
||||
Method m = ClassLoader.class.getDeclaredMethod("findResource", String.class);
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw (Error)new NoSuchMethodError().initCause(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,7 +960,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
|
||||
* @return an enumeration of URLs for the resources
|
||||
* @exception IOException if I/O errors occurs (can't happen)
|
||||
*/
|
||||
protected Enumeration/*<URL>*/ findResources(String name) throws IOException {
|
||||
public Enumeration/*<URL>*/ findResources(String name) throws IOException {
|
||||
return findResources(name, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,25 @@ public class AntWithFindResourceClassLoader extends AntClassLoader implements Cl
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL findResource(String name) {
|
||||
public URL findResource(String name) {
|
||||
// try and load from this loader if the parent either didn't find
|
||||
// it or wasn't consulted.
|
||||
return getUrl(pathComponents, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public version of {@link ClassLoader#findLoadedClass(String)}
|
||||
*/
|
||||
public Class<?> findLoadedClass2(String name) {
|
||||
return findLoadedClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public version of {@link ClassLoader#getClassLoadingLock(String)}
|
||||
*/
|
||||
@Override
|
||||
public Object getClassLoadingLock(String className) {
|
||||
return super.getClassLoadingLock(className);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user