8200167: Validate more special case invocations

Co-authored-by: Vladimir Ivanov <vladimir.x.ivanov@oracle.com>
Co-authored-by: Tobias Hartmann <tobias.hartmann@oracle.com>
Reviewed-by: acorn, vlivanov, dholmes
This commit is contained in:
John Rose 2018-04-30 21:56:54 -04:00 committed by David Holmes
parent 08855df46a
commit d05ed512ac
8 changed files with 431 additions and 38 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2018, 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
@ -72,26 +72,30 @@ class DirectMethodHandle extends MethodHandle {
}
// Factory methods:
static DirectMethodHandle make(byte refKind, Class<?> receiver, MemberName member) {
static DirectMethodHandle make(byte refKind, Class<?> refc, MemberName member, Class<?> callerClass) {
MethodType mtype = member.getMethodOrFieldType();
if (!member.isStatic()) {
if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
if (!member.getDeclaringClass().isAssignableFrom(refc) || member.isConstructor())
throw new InternalError(member.toString());
mtype = mtype.insertParameterTypes(0, receiver);
mtype = mtype.insertParameterTypes(0, refc);
}
if (!member.isField()) {
switch (refKind) {
case REF_invokeSpecial: {
member = member.asSpecial();
LambdaForm lform = preparedLambdaForm(member);
return new Special(mtype, lform, member);
LambdaForm lform = preparedLambdaForm(member, callerClass);
Class<?> checkClass = refc; // Class to use for receiver type check
if (callerClass != null) {
checkClass = callerClass; // potentially strengthen to caller class
}
return new Special(mtype, lform, member, checkClass);
}
case REF_invokeInterface: {
LambdaForm lform = preparedLambdaForm(member);
return new Interface(mtype, lform, member, receiver);
LambdaForm lform = preparedLambdaForm(member, callerClass);
return new Interface(mtype, lform, member, refc);
}
default: {
LambdaForm lform = preparedLambdaForm(member);
LambdaForm lform = preparedLambdaForm(member, callerClass);
return new DirectMethodHandle(mtype, lform, member);
}
}
@ -108,11 +112,11 @@ class DirectMethodHandle extends MethodHandle {
}
}
}
static DirectMethodHandle make(Class<?> receiver, MemberName member) {
static DirectMethodHandle make(Class<?> refc, MemberName member) {
byte refKind = member.getReferenceKind();
if (refKind == REF_invokeSpecial)
refKind = REF_invokeVirtual;
return make(refKind, receiver, member);
return make(refKind, refc, member, null /* no callerClass context */);
}
static DirectMethodHandle make(MemberName member) {
if (member.isConstructor())
@ -161,7 +165,7 @@ class DirectMethodHandle extends MethodHandle {
* Cache and share this structure among all methods with
* the same basicType and refKind.
*/
private static LambdaForm preparedLambdaForm(MemberName m) {
private static LambdaForm preparedLambdaForm(MemberName m, Class<?> callerClass) {
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
MethodType mtype = m.getInvocationType().basicType();
assert(!m.isMethodHandleInvoke()) : m;
@ -179,6 +183,9 @@ class DirectMethodHandle extends MethodHandle {
preparedLambdaForm(mtype, which);
which = LF_INVSTATIC_INIT;
}
if (which == LF_INVSPECIAL && callerClass != null && callerClass.isInterface()) {
which = LF_INVSPECIAL_IFC;
}
LambdaForm lform = preparedLambdaForm(mtype, which);
maybeCompile(lform, m);
assert(lform.methodType().dropParameterTypes(0, 1)
@ -187,6 +194,10 @@ class DirectMethodHandle extends MethodHandle {
return lform;
}
private static LambdaForm preparedLambdaForm(MemberName m) {
return preparedLambdaForm(m, null);
}
private static LambdaForm preparedLambdaForm(MethodType mtype, int which) {
LambdaForm lform = mtype.form().cachedLambdaForm(which);
if (lform != null) return lform;
@ -197,13 +208,16 @@ class DirectMethodHandle extends MethodHandle {
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
boolean needsInit = (which == LF_INVSTATIC_INIT);
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
boolean needsReceiverCheck = (which == LF_INVINTERFACE);
boolean needsReceiverCheck = (which == LF_INVINTERFACE ||
which == LF_INVSPECIAL_IFC);
String linkerName;
LambdaForm.Kind kind;
switch (which) {
case LF_INVVIRTUAL: linkerName = "linkToVirtual"; kind = DIRECT_INVOKE_VIRTUAL; break;
case LF_INVSTATIC: linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC; break;
case LF_INVSTATIC_INIT:linkerName = "linkToStatic"; kind = DIRECT_INVOKE_STATIC_INIT; break;
case LF_INVSPECIAL_IFC:linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL_IFC; break;
case LF_INVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_INVOKE_SPECIAL; break;
case LF_INVINTERFACE: linkerName = "linkToInterface"; kind = DIRECT_INVOKE_INTERFACE; break;
case LF_NEWINVSPECIAL: linkerName = "linkToSpecial"; kind = DIRECT_NEW_INVOKE_SPECIAL; break;
@ -376,8 +390,10 @@ class DirectMethodHandle extends MethodHandle {
/** This subclass represents invokespecial instructions. */
static class Special extends DirectMethodHandle {
private Special(MethodType mtype, LambdaForm form, MemberName member) {
private final Class<?> caller;
private Special(MethodType mtype, LambdaForm form, MemberName member, Class<?> caller) {
super(mtype, form, member);
this.caller = caller;
}
@Override
boolean isInvokeSpecial() {
@ -385,7 +401,15 @@ class DirectMethodHandle extends MethodHandle {
}
@Override
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new Special(mt, lf, member);
return new Special(mt, lf, member, caller);
}
Object checkReceiver(Object recv) {
if (!caller.isInstance(recv)) {
String msg = String.format("Receiver class %s is not a subclass of caller class %s",
recv.getClass().getName(), caller.getName());
throw new IncompatibleClassChangeError(msg);
}
return recv;
}
}
@ -401,17 +425,23 @@ class DirectMethodHandle extends MethodHandle {
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
return new Interface(mt, lf, member, refc);
}
@Override
Object checkReceiver(Object recv) {
if (!refc.isInstance(recv)) {
String msg = String.format("Class %s does not implement the requested interface %s",
recv.getClass().getName(), refc.getName());
String msg = String.format("Receiver class %s does not implement the requested interface %s",
recv.getClass().getName(), refc.getName());
throw new IncompatibleClassChangeError(msg);
}
return recv;
}
}
/** Used for interface receiver type checks, by Interface and Special modes. */
Object checkReceiver(Object recv) {
throw new InternalError("Should only be invoked on a subclass");
}
/** This subclass handles constructor references. */
static class Constructor extends DirectMethodHandle {
final MemberName initMethod;
@ -823,10 +853,10 @@ class DirectMethodHandle extends MethodHandle {
MemberName.getFactory()
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
case NF_checkReceiver:
member = new MemberName(Interface.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
member = new MemberName(DirectMethodHandle.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
return new NamedFunction(
MemberName.getFactory()
.resolveOrFail(REF_invokeVirtual, member, Interface.class, NoSuchMethodException.class));
.resolveOrFail(REF_invokeVirtual, member, DirectMethodHandle.class, NoSuchMethodException.class));
default:
throw newInternalError("Unknown function: " + func);
}

View file

@ -674,6 +674,7 @@ class InvokerBytecodeGenerator {
case DIRECT_NEW_INVOKE_SPECIAL: // fall-through
case DIRECT_INVOKE_INTERFACE: // fall-through
case DIRECT_INVOKE_SPECIAL: // fall-through
case DIRECT_INVOKE_SPECIAL_IFC: // fall-through
case DIRECT_INVOKE_STATIC: // fall-through
case DIRECT_INVOKE_STATIC_INIT: // fall-through
case DIRECT_INVOKE_VIRTUAL: return resolveFrom(name, invokerType, DirectMethodHandle.Holder.class);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2018, 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
@ -291,6 +291,7 @@ class LambdaForm {
LINK_TO_CALL_SITE("linkToCallSite"),
DIRECT_INVOKE_VIRTUAL("DMH.invokeVirtual", "invokeVirtual"),
DIRECT_INVOKE_SPECIAL("DMH.invokeSpecial", "invokeSpecial"),
DIRECT_INVOKE_SPECIAL_IFC("DMH.invokeSpecialIFC", "invokeSpecialIFC"),
DIRECT_INVOKE_STATIC("DMH.invokeStatic", "invokeStatic"),
DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial", "newInvokeSpecial"),
DIRECT_INVOKE_INTERFACE("DMH.invokeInterface", "invokeInterface"),

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2018, 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
@ -2267,27 +2267,27 @@ return mh1;
}
/** Check access and get the requested method. */
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
final boolean doRestrict = true;
final boolean checkSecurity = true;
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, boundCallerClass);
}
/** Check access and get the requested method, for invokespecial with no restriction on the application of narrowing rules. */
private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
final boolean doRestrict = false;
final boolean checkSecurity = true;
return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, callerClass);
return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, boundCallerClass);
}
/** Check access and get the requested method, eliding security manager checks. */
private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
final boolean doRestrict = true;
final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, boundCallerClass);
}
/** Common code for all methods; do not call directly except from immediately above. */
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
boolean checkSecurity,
boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
boolean doRestrict, Class<?> boundCallerClass) throws IllegalAccessException {
checkMethod(refKind, refc, method);
// Optionally check with the security manager; this isn't needed for unreflect* calls.
if (checkSecurity)
@ -2325,25 +2325,25 @@ return mh1;
checkMethod(refKind, refc, method);
}
DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method);
DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method, lookupClass());
MethodHandle mh = dmh;
// Optionally narrow the receiver argument to refc using restrictReceiver.
// Optionally narrow the receiver argument to lookupClass using restrictReceiver.
if ((doRestrict && refKind == REF_invokeSpecial) ||
(MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(method))) {
mh = restrictReceiver(method, dmh, lookupClass());
}
mh = maybeBindCaller(method, mh, callerClass);
mh = maybeBindCaller(method, mh, boundCallerClass);
mh = mh.setVarargs(method);
return mh;
}
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
Class<?> callerClass)
Class<?> boundCallerClass)
throws IllegalAccessException {
if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
return mh;
Class<?> hostClass = lookupClass;
if (!hasPrivateAccess()) // caller must have private access
hostClass = callerClass; // callerClass came from a security manager style stack walk
hostClass = boundCallerClass; // boundCallerClass came from a security manager style stack walk
MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
// Note: caller will apply varargs after this step happens.
return cbmh;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2018, 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
@ -85,7 +85,8 @@ final class MethodTypeForm {
LF_GWT = 17, // guardWithTest
LF_TF = 18, // tryFinally
LF_LOOP = 19, // loop
LF_LIMIT = 20;
LF_INVSPECIAL_IFC = 20, // DMH invokeSpecial of (private) interface method
LF_LIMIT = 21;
/** Return the type corresponding uniquely (1-1) to this MT-form.
* It might have any primitive returns or arguments, but will have no references except Object.