HHH-15809 Secondary super-type cache pollution mitigations for HibernateBasicProxy

This commit is contained in:
Sanne Grinovero 2022-12-03 18:56:41 +00:00 committed by Sanne Grinovero
parent ded4c433ac
commit 8ebf3c8507
6 changed files with 54 additions and 49 deletions

View File

@ -6,14 +6,12 @@
*/
package org.hibernate.bytecode.internal.bytebuddy;
import java.util.Collections;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.spi.BasicProxyFactory;
import org.hibernate.engine.spi.PrimeAmongSecondarySupertypes;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.proxy.ProxyConfiguration;
@ -44,15 +42,19 @@ public class BasicProxyFactoryImpl implements BasicProxyFactory {
final Class<?> superClassOrMainInterface = superClass != null ? superClass : interfaceClass;
final TypeCache.SimpleKey cacheKey = new TypeCache.SimpleKey( superClassOrMainInterface );
this.proxyClass = byteBuddyState.loadBasicProxy( superClassOrMainInterface, cacheKey, byteBuddy -> byteBuddy
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( superClassOrMainInterface.getName() ) ) )
.subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR )
.implement( interfaceClass == null ? NO_INTERFACES : new Class[]{ interfaceClass } )
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
.method( byteBuddyState.getProxyDefinitionHelpers().getVirtualNotFinalizerFilter() )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getDelegateToInterceptorDispatcherMethodDelegation() )
.implement( ProxyConfiguration.class )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getInterceptorFieldAccessor() )
ByteBuddyState.ProxyDefinitionHelpers helpers = byteBuddyState.getProxyDefinitionHelpers();
this.proxyClass = byteBuddyState.loadBasicProxy( superClassOrMainInterface, cacheKey, byteBuddy ->
helpers.appendIgnoreAlsoAtEnd( byteBuddy
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( superClassOrMainInterface.getName() ) ) )
.subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR )
.implement( interfaceClass == null ? NO_INTERFACES : new Class[]{ interfaceClass } )
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
.method( byteBuddyState.getProxyDefinitionHelpers().getVirtualNotFinalizerFilter() )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getDelegateToInterceptorDispatcherMethodDelegation() )
.implement( ProxyConfiguration.class )
.intercept( byteBuddyState.getProxyDefinitionHelpers().getInterceptorFieldAccessor() )
)
);
this.interceptor = new PassThroughInterceptor( proxyClass.getName() );
try {
@ -65,29 +67,23 @@ public class BasicProxyFactoryImpl implements BasicProxyFactory {
@Override
public Object getProxy() {
final PrimeAmongSecondarySupertypes instance;
try {
final ProxyConfiguration proxy = (ProxyConfiguration) proxyClassConstructor.newInstance();
proxy.$$_hibernate_set_interceptor( this.interceptor );
return proxy;
instance = (PrimeAmongSecondarySupertypes) proxyClassConstructor.newInstance();
}
catch (Throwable t) {
throw new HibernateException( "Unable to instantiate proxy instance", t );
}
final ProxyConfiguration proxyConfiguration = instance.asProxyConfiguration();
if ( proxyConfiguration == null ) {
throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" );
}
proxyConfiguration.$$_hibernate_set_interceptor( this.interceptor );
return instance;
}
public boolean isInstance(Object object) {
return proxyClass.isInstance( object );
}
private TypeCache.SimpleKey getCacheKey(Class<?> superClass, Class<?>[] interfaces) {
Set<Class<?>> key = new HashSet<>();
if ( superClass != null ) {
key.add( superClass );
}
if ( interfaces != null ) {
Collections.addAll( key, interfaces );
}
return new TypeCache.SimpleKey( key );
}
}

View File

@ -304,7 +304,7 @@ public final class ByteBuddyState {
for ( Method m : PrimeAmongSecondarySupertypes.class.getMethods() ) {
//We need to ignore both the match of each default method on PrimeAmongSecondarySupertypes
toFullyIgnore.add( isDeclaredBy( PrimeAmongSecondarySupertypes.class ).and( named( m.getName() ) ).and( takesNoArguments() ) );
//And the override in the interface it belong to - which we happen to have in the return type
//And the override in the interface it belongs to - which we happen to have in the return type
toFullyIgnore.add( isDeclaredBy( m.getReturnType() ).and( named( m.getName() ) ).and( takesNoArguments() ) );
}

View File

@ -8,6 +8,7 @@ package org.hibernate.engine.spi;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.ProxyConfiguration;
/**
* For a full explanation of the purpose of this interface
@ -58,4 +59,8 @@ public interface PrimeAmongSecondarySupertypes {
return null;
}
default ProxyConfiguration asProxyConfiguration() {
return null;
}
}

View File

@ -8,6 +8,8 @@ package org.hibernate.proxy;
import java.lang.reflect.Method;
import org.hibernate.engine.spi.PrimeAmongSecondarySupertypes;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.FieldValue;
import net.bytebuddy.implementation.bind.annotation.Origin;
@ -23,7 +25,7 @@ import net.bytebuddy.implementation.bind.annotation.This;
* suppressed by the runtime if they are not available on a class loader. This allows using this interceptor
* and configuration with for example OSGi without any export of Byte Buddy when using Hibernate.
*/
public interface ProxyConfiguration {
public interface ProxyConfiguration extends PrimeAmongSecondarySupertypes {
/**
* The canonical field name for an interceptor object stored in a proxied object.
@ -37,6 +39,11 @@ public interface ProxyConfiguration {
*/
void $$_hibernate_set_interceptor(Interceptor interceptor);
@Override
default ProxyConfiguration asProxyConfiguration() {
return this;
}
/**
* An interceptor object that is responsible for invoking a proxy's method.
*/

View File

@ -89,11 +89,13 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
overridesEquals
);
final HibernateProxy proxy = getHibernateProxy();
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
return proxy;
final HibernateProxy instance = getHibernateProxy();
final ProxyConfiguration proxyConfiguration = instance.asProxyConfiguration();
if ( proxyConfiguration == null ) {
throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" );
}
proxyConfiguration.$$_hibernate_set_interceptor( interceptor );
return instance;
}
private HibernateProxy getHibernateProxy() {

View File

@ -6,10 +6,6 @@
*/
package org.hibernate.proxy.pojo.bytebuddy;
import static net.bytebuddy.matcher.ElementMatchers.anyOf;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.hibernate.engine.internal.ManagedTypeHelper.asHibernateProxy;
import static org.hibernate.internal.CoreLogging.messageLogger;
import java.io.Serializable;
@ -23,15 +19,7 @@ import java.util.function.Function;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.ManagedComposite;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.ManagedMappedSuperclass;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PrimeAmongSecondarySupertypes;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.proxy.HibernateProxy;
@ -46,7 +34,6 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.DefaultMethodCall;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.pool.TypePool;
@ -129,9 +116,17 @@ public class ByteBuddyProxyHelper implements Serializable {
serializableProxy.getPersistentClass(),
serializableProxy.getInterfaces()
);
final HibernateProxy proxy = asHibernateProxy( proxyClass.newInstance() );
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
return proxy;
PrimeAmongSecondarySupertypes instance = (PrimeAmongSecondarySupertypes) proxyClass.getDeclaredConstructor().newInstance();
final ProxyConfiguration proxyConfiguration = instance.asProxyConfiguration();
if ( proxyConfiguration == null ) {
throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" );
}
proxyConfiguration.$$_hibernate_set_interceptor( interceptor );
final HibernateProxy hibernateProxy = instance.asHibernateProxy();
if ( hibernateProxy == null ) {
throw new HibernateException( "Produced proxy does not correctly implement HibernateProxy" );
}
return hibernateProxy;
}
catch (Throwable t) {
final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() );