mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-19 10:34:38 +02:00
8247444: Trust final fields in records
Co-authored-by: Christoph Dreis <christoph.dreis@freenet.de> Reviewed-by: jrose, dholmes, forax, coleenp, vlivanov
This commit is contained in:
parent
983e012c9f
commit
f2b191a6e9
26 changed files with 227 additions and 49 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2020, 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
|
||||
|
@ -480,7 +480,8 @@ final class MemberName implements Member, Cloneable {
|
|||
IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor
|
||||
IS_FIELD = MN_IS_FIELD, // field
|
||||
IS_TYPE = MN_IS_TYPE, // nested type
|
||||
CALLER_SENSITIVE = MN_CALLER_SENSITIVE; // @CallerSensitive annotation detected
|
||||
CALLER_SENSITIVE = MN_CALLER_SENSITIVE, // @CallerSensitive annotation detected
|
||||
TRUSTED_FINAL = MN_TRUSTED_FINAL; // trusted final field
|
||||
|
||||
static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED;
|
||||
static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE;
|
||||
|
@ -520,6 +521,8 @@ final class MemberName implements Member, Cloneable {
|
|||
public boolean isCallerSensitive() {
|
||||
return testAllFlags(CALLER_SENSITIVE);
|
||||
}
|
||||
/** Query whether this member is a trusted final field. */
|
||||
public boolean isTrustedFinalField() { return testAllFlags(TRUSTED_FINAL|IS_FIELD); }
|
||||
|
||||
/** Utility method to query whether this member is accessible from a given lookup class. */
|
||||
public boolean isAccessibleFrom(Class<?> lookupClass) {
|
||||
|
|
|
@ -118,6 +118,7 @@ class MethodHandleNatives {
|
|||
MN_IS_FIELD = 0x00040000, // field
|
||||
MN_IS_TYPE = 0x00080000, // nested type
|
||||
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
||||
MN_TRUSTED_FINAL = 0x00200000, // trusted final field
|
||||
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
||||
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
|
||||
// The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
|
||||
|
|
|
@ -3273,10 +3273,10 @@ return mh1;
|
|||
private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
|
||||
MemberName field = new MemberName(f, isSetter);
|
||||
if (isSetter && field.isFinal()) {
|
||||
if (field.isStatic()) {
|
||||
throw field.makeAccessException("static final field has no write access", this);
|
||||
} else if (field.getDeclaringClass().isHidden()){
|
||||
throw field.makeAccessException("final field in a hidden class has no write access", this);
|
||||
if (field.isTrustedFinalField()) {
|
||||
String msg = field.isStatic() ? "static final field has no write access"
|
||||
: "final field has no write access";
|
||||
throw field.makeAccessException(msg, this);
|
||||
}
|
||||
}
|
||||
assert(isSetter
|
||||
|
@ -3839,7 +3839,7 @@ return mh1;
|
|||
refc = lookupClass();
|
||||
}
|
||||
return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(),
|
||||
this.allowedModes == TRUSTED && !getField.getDeclaringClass().isHidden());
|
||||
this.allowedModes == TRUSTED && !getField.isTrustedFinalField());
|
||||
}
|
||||
/** Check access and get the requested constructor. */
|
||||
private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
|
||||
|
|
|
@ -177,10 +177,16 @@ public class AccessibleObject implements AnnotatedElement {
|
|||
* to the caller's module. </p>
|
||||
*
|
||||
* <p> This method cannot be used to enable {@linkplain Field#set <em>write</em>}
|
||||
* access to a final field declared in a {@linkplain Class#isHidden() hidden class},
|
||||
* since such fields are not modifiable. The {@code accessible} flag when
|
||||
* {@code true} suppresses Java language access control checks to only
|
||||
* enable {@linkplain Field#get <em>read</em>} access to such fields.
|
||||
* access to a <em>non-modifiable</em> final field. The following fields
|
||||
* are non-modifiable:
|
||||
* <ul>
|
||||
* <li>static final fields declared in any class or interface</li>
|
||||
* <li>final fields declared in a {@linkplain Class#isHidden() hidden class}</li>
|
||||
* <li>final fields declared in a {@linkplain Class#isRecord() record}</li>
|
||||
* </ul>
|
||||
* <p> The {@code accessible} flag when {@code true} suppresses Java language access
|
||||
* control checks to only enable {@linkplain Field#get <em>read</em>} access to
|
||||
* these non-modifiable final fields.
|
||||
*
|
||||
* <p> If there is a security manager, its
|
||||
* {@code checkPermission} method is first called with a
|
||||
|
|
|
@ -72,6 +72,7 @@ class Field extends AccessibleObject implements Member {
|
|||
private String name;
|
||||
private Class<?> type;
|
||||
private int modifiers;
|
||||
private boolean trustedFinal;
|
||||
// Generics and annotations support
|
||||
private transient String signature;
|
||||
// generic info repository; lazily initialized
|
||||
|
@ -119,6 +120,7 @@ class Field extends AccessibleObject implements Member {
|
|||
String name,
|
||||
Class<?> type,
|
||||
int modifiers,
|
||||
boolean trustedFinal,
|
||||
int slot,
|
||||
String signature,
|
||||
byte[] annotations)
|
||||
|
@ -127,6 +129,7 @@ class Field extends AccessibleObject implements Member {
|
|||
this.name = name;
|
||||
this.type = type;
|
||||
this.modifiers = modifiers;
|
||||
this.trustedFinal = trustedFinal;
|
||||
this.slot = slot;
|
||||
this.signature = signature;
|
||||
this.annotations = annotations;
|
||||
|
@ -148,7 +151,7 @@ class Field extends AccessibleObject implements Member {
|
|||
if (this.root != null)
|
||||
throw new IllegalArgumentException("Can not copy a non-root Field");
|
||||
|
||||
Field res = new Field(clazz, name, type, modifiers, slot, signature, annotations);
|
||||
Field res = new Field(clazz, name, type, modifiers, trustedFinal, slot, signature, annotations);
|
||||
res.root = this;
|
||||
// Might as well eagerly propagate this if already present
|
||||
res.fieldAccessor = fieldAccessor;
|
||||
|
@ -728,7 +731,9 @@ class Field extends AccessibleObject implements Member {
|
|||
* this {@code Field} object;</li>
|
||||
* <li>the field is non-static; and</li>
|
||||
* <li>the field's declaring class is not a {@linkplain Class#isHidden()
|
||||
* hidden class}.</li>
|
||||
* hidden class}; and</li>
|
||||
* <li>the field's declaring class is not a {@linkplain Class#isRecord()
|
||||
* record class}.</li>
|
||||
* </ul>
|
||||
* If any of the above checks is not met, this method throws an
|
||||
* {@code IllegalAccessException}.
|
||||
|
@ -1145,10 +1150,14 @@ class Field extends AccessibleObject implements Member {
|
|||
}
|
||||
|
||||
@Override
|
||||
Field getRoot() {
|
||||
/* package-private */ Field getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/* package-private */ boolean isTrustedFinal() {
|
||||
return trustedFinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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
|
||||
|
@ -118,6 +118,10 @@ class ReflectAccess implements jdk.internal.access.JavaLangReflectAccess {
|
|||
return (T) obj.getRoot();
|
||||
}
|
||||
|
||||
public boolean isTrustedFinalField(Field f) {
|
||||
return f.isTrustedFinal();
|
||||
}
|
||||
|
||||
public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
|
||||
throws IllegalAccessException, InstantiationException, InvocationTargetException
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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
|
||||
|
@ -96,6 +96,9 @@ public interface JavaLangReflectAccess {
|
|||
/** Gets the root of the given AccessibleObject object; null if arg is the root */
|
||||
public <T extends AccessibleObject> T getRoot(T obj);
|
||||
|
||||
/** Tests if this is a trusted final field */
|
||||
public boolean isTrustedFinalField(Field f);
|
||||
|
||||
/** Returns a new instance created by the given constructor with access check */
|
||||
public <T> T newInstance(Constructor<T> ctor, Object[] args, Class<?> caller)
|
||||
throws IllegalAccessException, InstantiationException, InvocationTargetException;
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
/** Common utility routines used by both java.lang and
|
||||
|
@ -336,6 +337,14 @@ public class Reflection {
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests if the given Field is a trusted final field and it cannot be
|
||||
* modified reflectively regardless of the value of its accessible flag.
|
||||
*/
|
||||
public static boolean isTrustedFinalField(Field field) {
|
||||
return SharedSecrets.getJavaLangReflectAccess().isTrustedFinalField(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an IllegalAccessException with an exception message based on
|
||||
* the access that is denied.
|
||||
|
|
|
@ -181,7 +181,9 @@ public class ReflectionFactory {
|
|||
field = root;
|
||||
}
|
||||
}
|
||||
return UnsafeFieldAccessorFactory.newFieldAccessor(field, override);
|
||||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
||||
boolean isReadOnly = isFinal && (!override || langReflectAccess.isTrustedFinalField(field));
|
||||
return UnsafeFieldAccessorFactory.newFieldAccessor(field, isReadOnly);
|
||||
}
|
||||
|
||||
public MethodAccessor newMethodAccessor(Method method) {
|
||||
|
|
|
@ -29,13 +29,12 @@ import java.lang.reflect.Field;
|
|||
import java.lang.reflect.Modifier;
|
||||
|
||||
class UnsafeFieldAccessorFactory {
|
||||
static FieldAccessor newFieldAccessor(Field field, boolean override) {
|
||||
static FieldAccessor newFieldAccessor(Field field, boolean isReadOnly) {
|
||||
Class<?> type = field.getType();
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
||||
boolean isVolatile = Modifier.isVolatile(field.getModifiers());
|
||||
boolean isQualified = isFinal || isVolatile;
|
||||
boolean isReadOnly = isFinal && (isStatic || !override || field.getDeclaringClass().isHidden());
|
||||
if (isStatic) {
|
||||
// This code path does not guarantee that the field's
|
||||
// declaring class has been initialized, but it must be
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue