1 /* 2 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @summary Verifier should verify ClassFileLoadHook bytes even if on bootclasspath 27 * @bug 8351654 28 * @requires vm.jvmti 29 * @library /test/lib 30 * @modules java.base/jdk.internal.misc 31 * @modules java.compiler 32 * java.instrument 33 * jdk.jartool/sun.tools.jar 34 * @compile TestChecker.java 35 * @run driver jdk.test.lib.helpers.ClassFileInstaller checker/TestChecker 36 * @run main TestVerify buildAgent 37 * @run main/othervm --patch-module=java.base=. -Dagent.retransform=false -javaagent:redefineagent.jar TestVerify 38 * @run main/othervm --patch-module=java.base=. -Dagent.retransform=true -javaagent:redefineagent.jar TestVerify 39 */ 40 41 import java.lang.invoke.MethodHandles; 42 import java.lang.classfile.ClassFile; 43 import java.lang.classfile.ClassTransform; 44 import java.lang.classfile.MethodTransform; 45 import java.lang.classfile.constantpool.InterfaceMethodRefEntry; 46 import java.lang.classfile.instruction.ReturnInstruction; 47 import java.lang.constant.ClassDesc; 48 import java.lang.constant.ConstantDescs; 49 import java.lang.constant.MethodTypeDesc; 50 import java.lang.instrument.ClassFileTransformer; 51 import java.lang.instrument.IllegalClassFormatException; 52 import java.lang.instrument.Instrumentation; 53 import java.nio.file.Files; 54 import java.nio.file.Path; 55 import java.security.ProtectionDomain; 56 57 import java.io.PrintWriter; 58 import jdk.test.lib.helpers.ClassFileInstaller; 59 import java.io.FileNotFoundException; 60 61 public class TestVerify { 62 63 private static final String CLASS_TO_BREAK = "java.util.Date"; 64 private static final String INTERNAL_CLASS_TO_BREAK = CLASS_TO_BREAK.replace('.', '/'); 65 private static final boolean DEBUG = false; 66 67 private static class BadTransformer implements ClassFileTransformer { 68 69 @Override 70 public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 71 72 if (className.equals(INTERNAL_CLASS_TO_BREAK)) { 73 System.out.println("Instrumenting modular class " + INTERNAL_CLASS_TO_BREAK); 74 75 var methodTransform = MethodTransform.transformingCode((builder, element) -> { 76 if (element instanceof ReturnInstruction) { 77 System.out.println("Injecting bug"); 78 // THE BUG! insert broken function call 79 80 var checkerDesc = ClassDesc.of("checker", "TestChecker"); 81 builder.invokestatic(checkerDesc, "instance", MethodTypeDesc.of(checkerDesc), true); 82 83 // dup the instance ref, this is just to get a bad argument to the next method call 84 builder.dup(); 85 86 // then call a check method that doesn't take that type, but we have the wrong desc 87 builder.invokeinterface(checkerDesc, "check", MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Integer)); 88 89 System.out.println("Done injecting bug"); 90 } 91 builder.with(element); 92 }); 93 var classTransform = ClassTransform.transformingMethods(mm -> mm.methodName().stringValue().equals("parse"), methodTransform); 94 95 byte[] bytes; 96 try { 97 var cf = ClassFile.of(); 98 var existingClass = cf.parse(classfileBuffer); 99 bytes = cf.transformClass(existingClass, classTransform); 100 101 if (DEBUG) Files.write(Path.of("bad.class"), bytes); 102 } catch (Throwable e) { 103 throw new AssertionError(e); 104 } 105 return bytes; 106 } 107 return null; 108 } 109 } 110 111 static Instrumentation inst = null; 112 113 public static void premain(String args, Instrumentation instrumentation) throws Exception { 114 System.out.println("Premain"); 115 inst = instrumentation; 116 } 117 118 private static void buildAgent() { 119 try { 120 ClassFileInstaller.main("TestVerify"); 121 } catch (Exception e) { 122 throw new RuntimeException("Could not write agent classfile", e); 123 } 124 125 try { 126 PrintWriter pw = new PrintWriter("MANIFEST.MF"); 127 pw.println("Premain-Class: TestVerify"); 128 pw.println("Agent-Class: TestVerify"); 129 pw.println("Can-Retransform-Classes: true"); 130 pw.println("Can-Redefine-Classes: true"); 131 pw.close(); 132 } catch (FileNotFoundException e) { 133 throw new RuntimeException("Could not write manifest file for the agent", e); 134 } 135 136 sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); 137 if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "TestVerify.class" })) { 138 throw new RuntimeException("Could not write the agent jar file"); 139 } 140 } 141 142 public static void main(String argv[]) throws Exception { 143 if (argv.length == 1 && argv[0].equals("buildAgent")) { 144 buildAgent(); 145 return; 146 } 147 148 // double check our class hasn't been loaded yet 149 for (Class clazz : inst.getAllLoadedClasses()) { 150 if (clazz.getName().equals(CLASS_TO_BREAK)) { 151 throw new AssertionError("Oops! Class " + CLASS_TO_BREAK + " is already loaded, the test can't work"); 152 } 153 } 154 155 boolean retransform = Boolean.getBoolean("agent.retransform"); 156 157 try { 158 if (retransform) { 159 // Retransform the class for the VerifyError. 160 var clazz = Class.forName(CLASS_TO_BREAK); 161 inst.addTransformer(new BadTransformer(), true); 162 inst.retransformClasses(clazz); 163 } else { 164 // Load the class instrumented with CFLH for the VerifyError. 165 inst.addTransformer(new BadTransformer()); 166 Class<?> cls = Class.forName(CLASS_TO_BREAK); 167 System.out.println("class loaded" + cls); 168 } 169 throw new RuntimeException("Failed: Did not throw VerifyError"); 170 } catch (VerifyError e) { 171 System.out.println("Passed: VerifyError " + e.getMessage()); 172 } 173 } 174 }