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().equalsString("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 }
--- EOF ---