8022509: Various Dynalink security enhancements

Reviewed-by: jlaskey, hannesw
This commit is contained in:
Attila Szegedi 2013-08-07 16:38:44 +02:00
parent 24adb234a8
commit 65cc3fdc8a
10 changed files with 74 additions and 163 deletions

View File

@ -85,12 +85,12 @@ package jdk.internal.dynalink;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.support.AbstractRelinkableCallSite;
import jdk.internal.dynalink.support.Lookup;
/**
* A relinkable call site that maintains a chain of linked method handles. In the default implementation, up to 8 method
@ -103,6 +103,9 @@ import jdk.internal.dynalink.support.AbstractRelinkableCallSite;
* handle is always at the start of the chain.
*/
public class ChainedCallSite extends AbstractRelinkableCallSite {
private static final MethodHandle PRUNE = Lookup.findOwnSpecial(MethodHandles.lookup(), "prune", MethodHandle.class,
MethodHandle.class);
private final AtomicReference<LinkedList<GuardedInvocation>> invocations = new AtomicReference<>();
/**
@ -194,18 +197,4 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
private MethodHandle prune(MethodHandle relink) {
return relinkInternal(null, relink, false);
}
private static final MethodHandle PRUNE;
static {
try {
PRUNE = MethodHandles.lookup().findSpecial(ChainedCallSite.class, "prune", MethodType.methodType(
MethodHandle.class, MethodHandle.class), ChainedCallSite.class);
// NOTE: using two catch blocks so we don't introduce a reference to 1.7 ReflectiveOperationException, allowing
// Dynalink to be used on 1.6 JVMs with Remi's backport library.
} catch(IllegalAccessException e) {
throw new AssertionError(e.getMessage(), e); // Can not happen
} catch(NoSuchMethodException e) {
throw new AssertionError(e.getMessage(), e); // Can not happen
}
}
}

View File

@ -84,6 +84,8 @@
package jdk.internal.dynalink;
import java.lang.invoke.MutableCallSite;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -117,7 +119,9 @@ public class DynamicLinkerFactory {
*/
public static final int DEFAULT_UNSTABLE_RELINK_THRESHOLD = 8;
private ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
private boolean classLoaderExplicitlySet = false;
private ClassLoader classLoader;
private List<? extends GuardingDynamicLinker> prioritizedLinkers;
private List<? extends GuardingDynamicLinker> fallbackLinkers;
private int runtimeContextArgCount = 0;
@ -126,12 +130,13 @@ public class DynamicLinkerFactory {
/**
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
* context class loader at the time of the constructor invocation will be used.
* context class loader at the time of {@link #createLinker()} invocation will be used.
*
* @param classLoader the class loader used for the autodiscovery of available linkers.
*/
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
classLoaderExplicitlySet = true;
}
/**
@ -260,7 +265,8 @@ public class DynamicLinkerFactory {
addClasses(knownLinkerClasses, prioritizedLinkers);
addClasses(knownLinkerClasses, fallbackLinkers);
final List<GuardingDynamicLinker> discovered = AutoDiscovery.loadLinkers(classLoader);
final ClassLoader effectiveClassLoader = classLoaderExplicitlySet ? classLoader : getThreadContextClassLoader();
final List<GuardingDynamicLinker> discovered = AutoDiscovery.loadLinkers(effectiveClassLoader);
// Now, concatenate ...
final List<GuardingDynamicLinker> linkers =
new ArrayList<>(prioritizedLinkers.size() + discovered.size()
@ -303,6 +309,15 @@ public class DynamicLinkerFactory {
runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
}
private static ClassLoader getThreadContextClassLoader() {
return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
@Override
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
});
}
private static void addClasses(Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses,
List<? extends GuardingDynamicLinker> linkers) {
for(GuardingDynamicLinker linker: linkers) {

View File

@ -112,10 +112,6 @@ final class ClassString {
this(type.parameterArray());
}
Class<?>[] getClasses() {
return classes;
}
@Override
public boolean equals(Object other) {
if(!(other instanceof ClassString)) {

View File

@ -189,15 +189,17 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
return type == StaticClass.class;
}
/*private*/ static final MethodHandle GET_CLASS = new Lookup(MethodHandles.lookup()).findVirtual(StaticClass.class,
"getRepresentedClass", MethodType.methodType(Class.class));
/*private*/ static final MethodHandle IS_CLASS = new Lookup(MethodHandles.lookup()).findStatic(StaticClassLinker.class,
"isClass", MethodType.methodType(Boolean.TYPE, Class.class, Object.class));
/*private*/ static final MethodHandle GET_CLASS;
/*private*/ static final MethodHandle IS_CLASS;
/*private*/ static final MethodHandle ARRAY_CTOR = Lookup.PUBLIC.findStatic(Array.class, "newInstance",
MethodType.methodType(Object.class, Class.class, int.class));
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
GET_CLASS = lookup.findVirtual(StaticClass.class, "getRepresentedClass", MethodType.methodType(Class.class));
IS_CLASS = lookup.findOwnStatic("isClass", Boolean.TYPE, Class.class, Object.class);
}
@SuppressWarnings("unused")
private static boolean isClass(Class<?> clazz, Object obj) {
return obj instanceof StaticClass && ((StaticClass)obj).getRepresentedClass() == clazz;

View File

@ -1,99 +0,0 @@
/*
* Copyright (c) 2010, 2013, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file, and Oracle licenses the original version of this file under the BSD
* license:
*/
/*
Copyright 2009-2013 Attila Szegedi
Licensed under both the Apache License, Version 2.0 (the "Apache License")
and the BSD License (the "BSD License"), with licensee being free to
choose either of the two at their discretion.
You may not use this file except in compliance with either the Apache
License or the BSD License.
If you choose to use this file in compliance with the Apache License, the
following notice applies to you:
You may obtain a copy of the Apache License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
If you choose to use this file in compliance with the BSD License, the
following notice applies to you:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jdk.internal.dynalink.support;
import java.lang.invoke.MethodHandles;
/**
* @author Attila Szegedi
*/
public class Backport {
/**
* True if Remi's JSR-292 backport agent is active; false if we're using native OpenJDK JSR-292 support.
*/
public static final boolean inUse = MethodHandles.class.getName().startsWith("jsr292");
private Backport() {
}
}

View File

@ -85,6 +85,8 @@ package jdk.internal.dynalink.support;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
@ -121,22 +123,13 @@ public abstract class ClassMap<T> {
*/
protected abstract T computeValue(Class<?> clazz);
/**
* Returns the class loader that governs the strong referenceability of this class map.
*
* @return the class loader that governs the strong referenceability of this class map.
*/
public ClassLoader getClassLoader() {
return classLoader;
}
/**
* Returns the value associated with the class
*
* @param clazz the class
* @return the value associated with the class
*/
public T get(Class<?> clazz) {
public T get(final Class<?> clazz) {
// Check in fastest first - objects we're allowed to strongly reference
final T v = map.get(clazz);
if(v != null) {
@ -156,8 +149,16 @@ public abstract class ClassMap<T> {
// Not found in either place; create a new value
final T newV = computeValue(clazz);
assert newV != null;
final ClassLoader clazzLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
@Override
public ClassLoader run() {
return clazz.getClassLoader();
}
});
// If allowed to strongly reference, put it in the fast map
if(Guards.canReferenceDirectly(classLoader, clazz.getClassLoader())) {
if(Guards.canReferenceDirectly(classLoader, clazzLoader)) {
final T oldV = map.putIfAbsent(clazz, newV);
return oldV != null ? oldV : newV;
}

View File

@ -258,23 +258,24 @@ public class Guards {
type.changeReturnType(Boolean.TYPE), new int[] { pos });
}
private static final MethodHandle IS_OF_CLASS = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
"isOfClass", MethodType.methodType(Boolean.TYPE, Class.class, Object.class));
private static final MethodHandle IS_INSTANCE = Lookup.PUBLIC.findVirtual(Class.class, "isInstance",
MethodType.methodType(Boolean.TYPE, Object.class));
private static final MethodHandle IS_ARRAY = new Lookup(MethodHandles.lookup()).findStatic(Guards.class, "isArray",
MethodType.methodType(Boolean.TYPE, Object.class));
private static final MethodHandle IS_OF_CLASS;
private static final MethodHandle IS_ARRAY;
private static final MethodHandle IS_IDENTICAL;
private static final MethodHandle IS_NULL;
private static final MethodHandle IS_NOT_NULL;
private static final MethodHandle IS_IDENTICAL = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
"isIdentical", MethodType.methodType(Boolean.TYPE, Object.class, Object.class));
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
private static final MethodHandle IS_NULL = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
"isNull", MethodType.methodType(Boolean.TYPE, Object.class));
private static final MethodHandle IS_NOT_NULL = new Lookup(MethodHandles.lookup()).findStatic(Guards.class,
"isNotNull", MethodType.methodType(Boolean.TYPE, Object.class));
IS_OF_CLASS = lookup.findOwnStatic("isOfClass", Boolean.TYPE, Class.class, Object.class);
IS_ARRAY = lookup.findOwnStatic("isArray", Boolean.TYPE, Object.class);
IS_IDENTICAL = lookup.findOwnStatic("isIdentical", Boolean.TYPE, Object.class, Object.class);
IS_NULL = lookup.findOwnStatic("isNull", Boolean.TYPE, Object.class);
IS_NOT_NULL = lookup.findOwnStatic("isNotNull", Boolean.TYPE, Object.class);
}
/**
* Creates a guard method that tests its only argument for being of an exact particular class.

View File

@ -89,7 +89,6 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* A wrapper around MethodHandles.Lookup that masks checked exceptions in those cases when you're looking up methods
@ -235,9 +234,8 @@ public class Lookup {
}
/**
* Performs a findSpecial on the underlying lookup, except for the backport where it rather uses unreflect. Converts
* any encountered {@link IllegalAccessException} into an {@link IllegalAccessError} and a
* {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
* Performs a findSpecial on the underlying lookup. Converts any encountered {@link IllegalAccessException} into an
* {@link IllegalAccessError} and a {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
*
* @param declaringClass class declaring the method
* @param name the name of the method
@ -248,13 +246,6 @@ public class Lookup {
*/
public MethodHandle findSpecial(Class<?> declaringClass, String name, MethodType type) {
try {
if(Backport.inUse) {
final Method m = declaringClass.getDeclaredMethod(name, type.parameterArray());
if(!Modifier.isPublic(declaringClass.getModifiers()) || !Modifier.isPublic(m.getModifiers())) {
m.setAccessible(true);
}
return unreflect(m);
}
return lookup.findSpecial(declaringClass, name, type, declaringClass);
} catch(IllegalAccessException e) {
final IllegalAccessError ee = new IllegalAccessError("Failed to access special method " + methodDescription(

View File

@ -87,6 +87,8 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedList;
import java.util.List;
import jdk.internal.dynalink.linker.ConversionComparator;
@ -110,7 +112,7 @@ public class TypeConverterFactory {
private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
@Override
protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
return new ClassMap<MethodHandle>(sourceType.getClassLoader()) {
return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
@Override
protected MethodHandle computeValue(Class<?> targetType) {
try {
@ -128,7 +130,7 @@ public class TypeConverterFactory {
private final ClassValue<ClassMap<MethodHandle>> converterIdentityMap = new ClassValue<ClassMap<MethodHandle>>() {
@Override
protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
return new ClassMap<MethodHandle>(sourceType.getClassLoader()) {
return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
@Override
protected MethodHandle computeValue(Class<?> targetType) {
if(!canAutoConvert(sourceType, targetType)) {
@ -143,6 +145,15 @@ public class TypeConverterFactory {
}
};
private static final ClassLoader getClassLoader(final Class<?> clazz) {
return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
@Override
public ClassLoader run() {
return clazz.getClassLoader();
}
});
}
/**
* Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
*

View File

@ -68,6 +68,10 @@ public final class Bootstrap {
if (relinkThreshold > -1) {
factory.setUnstableRelinkThreshold(relinkThreshold);
}
// Linkers for any additional language runtimes deployed alongside Nashorn will be picked up by the factory.
factory.setClassLoader(Bootstrap.class.getClassLoader());
dynamicLinker = factory.createLinker();
}