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.time.Duration; 43 import java.lang.classfile.ClassFile; 44 import java.lang.classfile.ClassTransform; 45 import java.lang.classfile.MethodTransform; 46 import java.lang.classfile.constantpool.InterfaceMethodRefEntry; 47 import java.lang.classfile.instruction.ReturnInstruction; 48 import java.lang.constant.ClassDesc; 49 import java.lang.constant.ConstantDescs; 50 import java.lang.constant.MethodTypeDesc; 51 import java.lang.instrument.ClassFileTransformer; 52 import java.lang.instrument.IllegalClassFormatException; 53 import java.lang.instrument.Instrumentation; 54 import java.nio.file.Files; 55 import java.nio.file.Path; 56 import java.security.ProtectionDomain; 57 58 import java.io.PrintWriter; 59 import jdk.test.lib.helpers.ClassFileInstaller; 60 import java.io.FileNotFoundException; 61 62 public class TestVerify { 63 64 private static final String CLASS_TO_BREAK = "java.time.Duration"; 65 private static final String INTERNAL_CLASS_TO_BREAK = CLASS_TO_BREAK.replace('.', '/'); 66 private static final boolean DEBUG = false; 67 68 private static class BadTransformer implements ClassFileTransformer { 69 70 @Override 71 public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 72 73 if (className.equals(INTERNAL_CLASS_TO_BREAK)) { 74 System.out.println("Instrumenting modular class " + INTERNAL_CLASS_TO_BREAK); 75 76 var methodTransform = MethodTransform.transformingCode((builder, element) -> { 77 if (element instanceof ReturnInstruction) { 78 System.out.println("Injecting bug"); 79 // THE BUG! insert broken function call 80 81 var checkerDesc = ClassDesc.of("checker", "TestChecker"); 82 builder.invokestatic(checkerDesc, "instance", MethodTypeDesc.of(checkerDesc), true); 83 84 // dup the instance ref, this is just to get a bad argument to the next method call 85 builder.dup(); 86 87 // then call a check method that doesn't take that type, but we have the wrong desc 88 builder.invokeinterface(checkerDesc, "check", MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Integer)); 89 90 System.out.println("Done injecting bug"); 91 } 92 builder.with(element); 93 }); 94 var classTransform = ClassTransform.transformingMethods(mm -> mm.methodName().stringValue().equals("getSeconds"), methodTransform); 95 96 byte[] bytes; 97 try { 98 var cf = ClassFile.of(); 99 var existingClass = cf.parse(classfileBuffer); 100 bytes = cf.transformClass(existingClass, classTransform); 101 102 if (DEBUG) Files.write(Path.of("bad.class"), bytes); 103 } catch (Throwable e) { 104 throw new AssertionError(e); 105 } 106 return bytes; 107 } 108 return null; 109 } 110 } 111 112 static Instrumentation inst = null; 113 114 public static void premain(String args, Instrumentation instrumentation) throws Exception { 115 System.out.println("Premain"); 116 inst = instrumentation; 117 } 118 119 private static void buildAgent() { 120 try { 121 ClassFileInstaller.main("TestVerify"); 122 } catch (Exception e) { 123 throw new RuntimeException("Could not write agent classfile", e); 124 } 125 126 try { 127 PrintWriter pw = new PrintWriter("MANIFEST.MF"); 128 pw.println("Premain-Class: TestVerify"); 129 pw.println("Agent-Class: TestVerify"); 130 pw.println("Can-Retransform-Classes: true"); 131 pw.println("Can-Redefine-Classes: true"); 132 pw.close(); 133 } catch (FileNotFoundException e) { 134 throw new RuntimeException("Could not write manifest file for the agent", e); 135 } 136 137 sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); 138 if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "TestVerify.class" })) { 139 throw new RuntimeException("Could not write the agent jar file"); 140 } 141 } 142 143 public static void main(String argv[]) throws Exception { 144 if (argv.length == 1 && argv[0].equals("buildAgent")) { 145 buildAgent(); 146 return; 147 } 148 149 // double check our class hasn't been loaded yet 150 for (Class clazz : inst.getAllLoadedClasses()) { 151 if (clazz.getName().equals(CLASS_TO_BREAK)) { 152 throw new AssertionError("Oops! Class " + CLASS_TO_BREAK + " is already loaded, the test can't work"); 153 } 154 } 155 156 boolean retransform = Boolean.getBoolean("agent.retransform"); 157 158 try { 159 if (retransform) { 160 // Retransform the class for the VerifyError. 161 var clazz = Class.forName(CLASS_TO_BREAK); 162 inst.addTransformer(new BadTransformer(), true); 163 inst.retransformClasses(clazz); 164 } else { 165 // Load the class instrumented with CFLH for the VerifyError. 166 inst.addTransformer(new BadTransformer()); 167 System.out.println("1 hour is " + Duration.ofHours(1).getSeconds() + " seconds"); 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 }