diff --git a/prj/coherence-core/src/main/java/com/oracle/coherence/common/schema/ClassFileSchemaSource.java b/prj/coherence-core/src/main/java/com/oracle/coherence/common/schema/ClassFileSchemaSource.java index ed500f8e16cd3..3b2e6fdff294b 100644 --- a/prj/coherence-core/src/main/java/com/oracle/coherence/common/schema/ClassFileSchemaSource.java +++ b/prj/coherence-core/src/main/java/com/oracle/coherence/common/schema/ClassFileSchemaSource.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.common.schema; @@ -13,6 +13,8 @@ import com.oracle.coherence.common.schema.util.NameTransformer; import com.oracle.coherence.common.schema.util.NameTransformerChain; +import com.tangosol.internal.asm.ClassReaderInternal; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -29,7 +31,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; @@ -458,8 +459,8 @@ else if (isPass(1)) protected void populateSchema(Schema schema, InputStream in) throws IOException { - ClassReader reader = new ClassReader(in); - ClassNode source = new ClassNode(); + ClassReaderInternal reader = new ClassReaderInternal(in); + ClassNode source = new ClassNode(); reader.accept(source, 0); if (m_typeFilter.test(source)) diff --git a/prj/coherence-core/src/main/java/com/tangosol/internal/asm/ClassReaderInternal.java b/prj/coherence-core/src/main/java/com/tangosol/internal/asm/ClassReaderInternal.java new file mode 100644 index 0000000000000..9c4b18a23dc89 --- /dev/null +++ b/prj/coherence-core/src/main/java/com/tangosol/internal/asm/ClassReaderInternal.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.tangosol.internal.asm; + +import com.tangosol.util.asm.BaseClassReaderInternal; + +import java.io.IOException; +import java.io.InputStream; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; + +/** + * This class wraps ASM's ClassReader allowing Coherence to bypass the class + * version checks performed by ASM when reading a class. + * + * @since 15.1.1.0 + */ +/* + * Internal NOTE: This class is also duplicated in coherence-json and + * coherence-rest. This is done because each module shades + * ASM within a unique package into the produced JAR and + * thus having to create copes to deal with those package + * differences. + */ +public class ClassReaderInternal + extends BaseClassReaderInternal + { + // ----- constructors --------------------------------------------------- + + /** + * @see BaseClassReaderInternal#BaseClassReaderInternal(InputStream) + */ + public ClassReaderInternal(InputStream streamIn) throws IOException + { + super(streamIn); + } + + /** + * @see BaseClassReaderInternal#BaseClassReaderInternal(byte[]) + */ + public ClassReaderInternal(byte[] abBytes) + { + super(abBytes); + } + + // ----- BaseClassReaderInternal methods -------------------------------- + + @Override + protected ClassReader createReader(byte[] abBytes) + { + return new ClassReader(abBytes); + } + + @Override + protected void accept(ClassReader classReader, ClassVisitor classVisitor, int nParsingOptions) + { + classReader.accept(classVisitor, nParsingOptions); + } + } diff --git a/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/RemotableClassGenerator.java b/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/RemotableClassGenerator.java index 04dd730a9a17e..f4dfc06df2c93 100644 --- a/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/RemotableClassGenerator.java +++ b/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/RemotableClassGenerator.java @@ -1,16 +1,17 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.tangosol.internal.util.invoke; import com.tangosol.dev.assembler.Field; +import com.tangosol.internal.asm.ClassReaderInternal; + import java.lang.invoke.MethodHandleInfo; -import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -55,7 +56,7 @@ public static byte[] createRemoteClass(String sClassNameOld, String sClassNameNe ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassRemapper remapper = new ClassRemapper(writer, new SimpleRemapper(sClassNameOld, sClassNameNew)); - new ClassReader(abClass).accept(remapper, ClassReader.EXPAND_FRAMES); + new ClassReaderInternal(abClass).accept(remapper, org.objectweb.asm.ClassReader.EXPAND_FRAMES); return writer.toByteArray(); } diff --git a/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/lambda/RemotableLambdaGenerator.java b/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/lambda/RemotableLambdaGenerator.java index fcf0355be9e64..cd597d3db0cb3 100644 --- a/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/lambda/RemotableLambdaGenerator.java +++ b/prj/coherence-core/src/main/java/com/tangosol/internal/util/invoke/lambda/RemotableLambdaGenerator.java @@ -1,18 +1,20 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.tangosol.internal.util.invoke.lambda; +import com.tangosol.internal.asm.ClassReaderInternal; + import com.tangosol.internal.util.invoke.ClassIdentity; + import com.tangosol.internal.util.invoke.Lambdas; import com.tangosol.internal.util.invoke.RemotableClassGenerator; import com.tangosol.internal.util.invoke.Remotable; -import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -319,8 +321,8 @@ private static String copyLambdaMethod(ClassWriter cw, SerializedLambda lambdaMe try (InputStream is = loader.getResourceAsStream(sImplClassName + ".class")) { - ClassReader reader = new ClassReader(is); - ClassNode implClass = new ClassNode(); + ClassReaderInternal reader = new ClassReaderInternal(is); + ClassNode implClass = new ClassNode(); reader.accept(implClass, 0); diff --git a/prj/coherence-core/src/main/java/com/tangosol/util/asm/BaseClassReaderInternal.java b/prj/coherence-core/src/main/java/com/tangosol/util/asm/BaseClassReaderInternal.java new file mode 100644 index 0000000000000..36f7aa0efcf3c --- /dev/null +++ b/prj/coherence-core/src/main/java/com/tangosol/util/asm/BaseClassReaderInternal.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.tangosol.util.asm; + +import com.oracle.coherence.common.base.Logger; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Base class for internal wrappers of ASM's ClassReader. + * + * @param the ClassReader type + * @param the ClassVisitor type + * + * @since 15.1.1.0 + */ +public abstract class BaseClassReaderInternal + { + // ----- constructors --------------------------------------------------- + + /** + * @see org.objectweb.asm.ClassReader#ClassReader(InputStream) + */ + public BaseClassReaderInternal(InputStream streamIn) + throws IOException + { + this(streamIn.readAllBytes()); + } + + /** + * @see org.objectweb.asm.ClassReader#ClassReader(byte[]) + */ + public BaseClassReaderInternal(byte[] abBytes) + { + m_abBytes = abBytes; + } + + // ----- abstract methods ----------------------------------------------- + + /** + * Create the module-specific ClassReader. + * + * @param abBytes the class bytes + * + * @return the module-specific ClassReader + */ + protected abstract RT createReader(byte[] abBytes); + + /** + * Perform the accept operation on the module-specific ClassReader + * + * @param classReader the module-specific ClassReader + * @param classVisitor the module-specific ClassVisitor + * @param nParsingOptions the parsing options + */ + protected abstract void accept(RT classReader, CVT classVisitor, int nParsingOptions); + + // ----- api methods ---------------------------------------------------- + + /** + * Makes the given visitor visit the Java Virtual Machine's class file + * structure passed to the constructor of this {@code ClassReader}. + * + * @param classVisitor the visitor that must visit this class + * @param nParsingOptions the options to use to parse this class + * + * @see org.objectweb.asm.ClassReader#accept(org.objectweb.asm.ClassVisitor, int) + */ + @SuppressWarnings("DuplicatedCode") + public void accept(CVT classVisitor, int nParsingOptions) + { + byte[] abBytes = m_abBytes; + int nOriginalVersion = getMajorVersion(abBytes); + boolean fRevertVersion = false; + + if (nOriginalVersion > MAX_MAJOR_VERSION) + { + // temporarily downgrade version to bypass check in ASM + setMajorVersion(MAX_MAJOR_VERSION, abBytes); + + fRevertVersion = true; + + Logger.warn(() -> String.format("Unsupported class file major version " + nOriginalVersion)); + } + + RT classReader = createReader(abBytes); + + if (fRevertVersion) + { + // set version back + setMajorVersion(nOriginalVersion, abBytes); + } + + accept(classReader, classVisitor, nParsingOptions); + } + + // ----- helper methods ------------------------------------------------- + + /** + * Sets the major version number in given class bytes. + * + * @param nMajorVersion major version of bytecode to set + * @param abBytes class bytes + * + * @see #getMajorVersion(byte[]) + */ + protected static void setMajorVersion(final int nMajorVersion, final byte[] abBytes) + { + abBytes[6] = (byte) (nMajorVersion >>> 8); + abBytes[7] = (byte) nMajorVersion; + } + + /** + * Gets major version number from the given class bytes. + * + * @param abBytes class bytes + * + * @return the major version of bytecode + */ + protected static int getMajorVersion(final byte[] abBytes) + { + return ((abBytes[6] & 0xFF) << 8) | (abBytes[7] & 0xFF); + } + + // ----- constants ------------------------------------------------------ + + /** + * The max major version supported by the shaded ASM. + */ + /* + * Implementation Note: This doesn't reference the constant to avoid + * strange issues with moditect + */ + private static final int MAX_MAJOR_VERSION = 66; // Opcodes.V22 + + // ----- data members --------------------------------------------------- + + /** + * The class bytes. + */ + protected byte[] m_abBytes; + } diff --git a/prj/coherence-dependencies/pom.xml b/prj/coherence-dependencies/pom.xml index 00f2a9bcb3bc1..d50249842259c 100644 --- a/prj/coherence-dependencies/pom.xml +++ b/prj/coherence-dependencies/pom.xml @@ -175,7 +175,16 @@ 2.0.2 + + 9.6 + 6.2.31 6.4.0 3.1.1 diff --git a/prj/coherence-json/src/main/java/com/oracle/coherence/io/json/genson/reflect/ASMCreatorParameterNameResolver.java b/prj/coherence-json/src/main/java/com/oracle/coherence/io/json/genson/reflect/ASMCreatorParameterNameResolver.java index 2be1fd37c26a0..c0e278c9d093d 100644 --- a/prj/coherence-json/src/main/java/com/oracle/coherence/io/json/genson/reflect/ASMCreatorParameterNameResolver.java +++ b/prj/coherence-json/src/main/java/com/oracle/coherence/io/json/genson/reflect/ASMCreatorParameterNameResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * * Copyright 2011-2014 Genson - Cepoi Eugen * @@ -15,6 +15,7 @@ package com.oracle.coherence.io.json.genson.reflect; +import com.tangosol.util.asm.BaseClassReaderInternal; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Array; @@ -73,10 +74,10 @@ protected void read(Class ofClass) { is = ClassLoader.getSystemClassLoader().getResourceAsStream(ofClassName); else is = ofClass.getClassLoader().getResourceAsStream(ofClassName); - ClassReader cr; + ClassReaderInternal cr; ClassConstructorsVisitor visitor = new ClassConstructorsVisitor(ofClass, constructorParameterNames, methodParameterNames); try { - cr = new ClassReader(is); + cr = new ClassReaderInternal(is); cr.accept(visitor, 0); } catch (IOException e) { // C'est ok pas grave, cette technique n'est pas cense marcher dans tous les cas @@ -331,4 +332,53 @@ public String signature() { return sb.toString(); } } + + /** + * This class wraps ASM's ClassReader allowing Coherence to bypass the class + * version checks performed by ASM when reading a class. + * + * @since 15.1.1.0 + */ + /* + * Internal NOTE: This class is also duplicated in coherence-core and + * coherence-rest. This is done because each module shades + * ASM within a unique package into the produced JAR and + * thus having to create copes to deal with those package + * differences. + */ + protected static final class ClassReaderInternal + extends BaseClassReaderInternal + { + // ----- constructors --------------------------------------------------- + + /** + * @see BaseClassReaderInternal#BaseClassReaderInternal(InputStream) + */ + public ClassReaderInternal(InputStream streamIn) throws IOException + { + super(streamIn); + } + + /** + * @see BaseClassReaderInternal#BaseClassReaderInternal(byte[]) + */ + public ClassReaderInternal(byte[] abBytes) + { + super(abBytes); + } + + // ----- BaseClassReaderInternal methods -------------------------------- + + @Override + protected ClassReader createReader(byte[] abBytes) + { + return new ClassReader(abBytes); + } + + @Override + protected void accept(ClassReader classReader, ClassVisitor classVisitor, int nParsingOptions) + { + classReader.accept(classVisitor, nParsingOptions); + } + } } diff --git a/prj/coherence-rest/src/main/java/com/tangosol/coherence/rest/util/PartialObject.java b/prj/coherence-rest/src/main/java/com/tangosol/coherence/rest/util/PartialObject.java index 9d05c976d8b2e..c677f308529a6 100644 --- a/prj/coherence-rest/src/main/java/com/tangosol/coherence/rest/util/PartialObject.java +++ b/prj/coherence-rest/src/main/java/com/tangosol/coherence/rest/util/PartialObject.java @@ -1,11 +1,13 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.tangosol.coherence.rest.util; +import com.tangosol.util.asm.BaseClassReaderInternal; + import com.tangosol.util.Base; import com.tangosol.util.CopyOnWriteMap; import com.tangosol.util.UID; @@ -23,6 +25,7 @@ import java.util.concurrent.ConcurrentMap; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Type; @@ -511,7 +514,7 @@ protected static ClassNode getClassNode(Class clz) classStream = getPartialClassLoader().getResourceAsStream( clz.getName().replace('.', '/') + ".class"); - ClassReader cr = new ClassReader(classStream); + ClassReaderInternal cr = new ClassReaderInternal(classStream); cr.accept(cn, 0); return cn; } @@ -694,6 +697,57 @@ protected Package[] getPackages() private Package m_package; } + // ----- inner class: ClassReaderInternal ------------------------------- + + /** + * This class wraps ASM's ClassReader allowing Coherence to bypass the class + * version checks performed by ASM when reading a class. + * + * @since 15.1.1.0 + */ + /* + * Internal NOTE: This class is also duplicated in coherence-core and + * coherence-rest. This is done because each module shades + * ASM within a unique package into the produced JAR and + * thus having to create copes to deal with those package + * differences. + */ + protected static final class ClassReaderInternal + extends BaseClassReaderInternal + { + // ----- constructors --------------------------------------------------- + + /** + * @see BaseClassReaderInternal#BaseClassReaderInternal(InputStream) + */ + public ClassReaderInternal(InputStream streamIn) throws IOException + { + super(streamIn); + } + + /** + * @see BaseClassReaderInternal#BaseClassReaderInternal(byte[]) + */ + public ClassReaderInternal(byte[] abBytes) + { + super(abBytes); + } + + // ----- BaseClassReaderInternal methods -------------------------------- + + @Override + protected ClassReader createReader(byte[] abBytes) + { + return new ClassReader(abBytes); + } + + @Override + protected void accept(ClassReader classReader, ClassVisitor classVisitor, int nParsingOptions) + { + classReader.accept(classVisitor, nParsingOptions); + } + } + // ----- data members --------------------------------------------------- /**