未验证 提交 ae882917 编写于 作者: T Tim Jacomb 提交者: GitHub

[JENKINS-64423, JENKINS-46618] Resolve all core caused startup Illegal...

[JENKINS-64423, JENKINS-46618] Resolve all core caused startup Illegal reflective access warnings (#5110)
Co-authored-by: NJesse Glick <jglick@cloudbees.com>
上级 03c8d862
......@@ -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;
}
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();
}
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;
} catch (InvocationTargetException|IllegalAccessException e) {
throw new Error(e);
}
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);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册