From 8ebf3c8507bce339bc63cdd76e6869c2fbd975a7 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Sat, 3 Dec 2022 18:56:41 +0000 Subject: [PATCH] HHH-15809 Secondary super-type cache pollution mitigations for HibernateBasicProxy --- .../bytebuddy/BasicProxyFactoryImpl.java | 48 +++++++++---------- .../internal/bytebuddy/ByteBuddyState.java | 2 +- .../spi/PrimeAmongSecondarySupertypes.java | 5 ++ .../hibernate/proxy/ProxyConfiguration.java | 9 +++- .../pojo/bytebuddy/ByteBuddyProxyFactory.java | 12 +++-- .../pojo/bytebuddy/ByteBuddyProxyHelper.java | 27 +++++------ 6 files changed, 54 insertions(+), 49 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java index a645859467..ad8b931014 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BasicProxyFactoryImpl.java @@ -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> key = new HashSet<>(); - if ( superClass != null ) { - key.add( superClass ); - } - if ( interfaces != null ) { - Collections.addAll( key, interfaces ); - } - - return new TypeCache.SimpleKey( key ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java index fa1472a0dc..333f331593 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java @@ -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() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java index 79ac598ae5..1f97ef3288 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PrimeAmongSecondarySupertypes.java @@ -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; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/ProxyConfiguration.java b/hibernate-core/src/main/java/org/hibernate/proxy/ProxyConfiguration.java index 18c3ebcbdf..6bc387849b 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/ProxyConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/ProxyConfiguration.java @@ -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. */ diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java index 827860d4aa..54ee098ac0 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyFactory.java @@ -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() { diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java index 8378dc17f5..b61d273d7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/pojo/bytebuddy/ByteBuddyProxyHelper.java @@ -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() );