# HG changeset patch # User Doug Simon # Date 1491086319 -7200 # Node ID 6508ce8d068f27e7738c218bb7cf0229a255f77b # Parent df038ccc5943254ce8f48c9f34ebbf33da026907 check bounds of java.lang.Class reads (JDK-8177673) diff -r df038ccc5943 -r 6508ce8d068f jvmci/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java --- a/jvmci/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java Sat Apr 01 15:45:34 2017 +0200 +++ b/jvmci/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java Sun Apr 02 00:38:39 2017 +0200 @@ -28,6 +28,7 @@ import java.lang.reflect.Array; +import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -56,25 +57,37 @@ * @return {@code null} if {@code base} does not box an object otherwise the object boxed in * {@code base} */ - private static Object asObject(HotSpotJVMCIRuntimeProvider runtime, Constant base, JavaKind kind, long displacement) { + private Object asObject(Constant base, JavaKind kind, long displacement) { if (base instanceof HotSpotObjectConstantImpl) { HotSpotObjectConstantImpl constant = (HotSpotObjectConstantImpl) base; HotSpotResolvedObjectType type = constant.getType(); Object object = constant.object(); - if (object instanceof Class && kind != JavaKind.Object) { - // Cannot check bounds when reading primitives from java.lang.Class as - // we don't have the bounds for the part of a java.lang.Class object - // containing the static fields of the represented class. - } else { - checkRead(runtime, kind, displacement, type, object); - } + checkRead(kind, displacement, type, object); return object; } return null; - } - private static void checkRead(HotSpotJVMCIRuntimeProvider runtime, JavaKind kind, long displacement, HotSpotResolvedObjectType type, Object object) { + /** + * Offset of injected {@code java.lang.Class::oop_size} field. No need to make {@code volatile} + * as initialization is idempotent. + */ + private long oopSizeOffset; + + private static int computeOopSizeOffset(HotSpotJVMCIRuntimeProvider runtime) { + MetaAccessProvider metaAccess = runtime.getHostJVMCIBackend().getMetaAccess(); + ResolvedJavaType staticType = metaAccess.lookupJavaType(Class.class); + for (ResolvedJavaField f : staticType.getInstanceFields(false)) { + if (f.getName().equals("oop_size")) { + int offset = ((HotSpotResolvedJavaField) f).offset(); + assert offset != 0 : "not expecting offset of java.lang.Class::oop_size to be 0"; + return offset; + } + } + throw new JVMCIError("Could not find injected java.lang.Class::oop_size field"); + } + + private boolean checkRead(JavaKind kind, long displacement, HotSpotResolvedObjectType type, Object object) { if (type.isArray()) { ResolvedJavaType componentType = type.getComponentType(); JavaKind componentKind = componentType.getJavaKind(); @@ -90,9 +103,16 @@ type.toJavaName() + " object of length " + length); } } else if (kind != JavaKind.Object) { - // In non-product builds, unsafe.cpp asserts that an access based on a non-null - // object is within bounds so do a similar check here to prevent a VM halt. - long size = Math.abs(type.instanceSize()); + long size; + if (object instanceof Class) { + if (oopSizeOffset == 0) { + oopSizeOffset = computeOopSizeOffset(runtime); + } + int wordSize = runtime.getHostJVMCIBackend().getCodeCache().getTarget().wordSize; + size = UNSAFE.getInt(object, oopSizeOffset) * wordSize; + } else { + size = Math.abs(type.instanceSize()); + } int bytesToRead = kind.getByteCount(); if (displacement + bytesToRead > size || displacement < 0) { throw new IllegalArgumentException("Unsafe access: reading " + bytesToRead + " bytes at offset " + displacement + " in " + @@ -112,10 +132,10 @@ } if (field.getJavaKind() != JavaKind.Object) { throw new IllegalArgumentException("Unsafe object access: field " + field.format("%H.%n:%T") + " not of expected kind Object" + - " at offset " + displacement + " in " + - type.toJavaName() + " object"); + " at offset " + displacement + " in " + type.toJavaName() + " object"); } } + return true; } private boolean isValidObjectFieldDisplacement(Constant base, long displacement) { @@ -149,8 +169,8 @@ throw new IllegalArgumentException(String.valueOf(base)); } - private static long readRawValue(HotSpotJVMCIRuntimeProvider runtime, Constant baseConstant, long displacement, JavaKind kind, int bits) { - Object base = asObject(runtime, baseConstant, kind, displacement); + private long readRawValue(Constant baseConstant, long displacement, JavaKind kind, int bits) { + Object base = asObject(baseConstant, kind, displacement); if (base != null) { switch (bits) { case Byte.SIZE: @@ -196,7 +216,7 @@ private Object readRawObject(Constant baseConstant, long initialDisplacement, boolean compressed) { long displacement = initialDisplacement; Object ret; - Object base = asObject(runtime, baseConstant, JavaKind.Object, displacement); + Object base = asObject(baseConstant, JavaKind.Object, displacement); if (base == null) { assert !compressed; displacement += asRawPointer(baseConstant); @@ -213,6 +233,7 @@ assert obj != null; assert !field.isStatic() || obj instanceof Class; long displacement = field.offset(); + assert checkRead(field.getJavaKind(), displacement, (HotSpotResolvedObjectType) runtime.getHostJVMCIBackend().getMetaAccess().lookupJavaType(obj.getClass()), obj); if (field.getJavaKind() == JavaKind.Object) { Object o = UNSAFE.getObject(obj, displacement); return HotSpotObjectConstantImpl.forObject(o); @@ -244,7 +265,7 @@ @Override public JavaConstant readPrimitiveConstant(JavaKind kind, Constant baseConstant, long initialDisplacement, int bits) { try { - long rawValue = readRawValue(runtime, baseConstant, initialDisplacement, kind, bits); + long rawValue = readRawValue(baseConstant, initialDisplacement, kind, bits); switch (kind) { case Boolean: return JavaConstant.forBoolean(rawValue != 0);