1 /* |
|
2 * Copyright (c) 2014, 2019, 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 import java.io.File; |
|
26 import java.io.FileOutputStream; |
|
27 import jdk.test.lib.process.OutputAnalyzer; |
|
28 import java.nio.file.Files; |
|
29 |
|
30 import java.util.*; |
|
31 import jdk.internal.org.objectweb.asm.*; |
|
32 |
|
33 /** |
|
34 * The testsets contained in this class are executed by ./VerifierTest_*.java, so that |
|
35 * individual testsets can be executed in parallel to shorten the total time required. |
|
36 */ |
|
37 public class VerifierTest implements Opcodes { |
|
38 // Test verification settings for dumping & runtime |
|
39 static final String VFY_ALL = "-Xverify:all"; |
|
40 static final String VFY_REMOTE = "-Xverify:remote"; // default |
|
41 static final String VFY_NONE = "-XX:+UnlockDiagnosticVMOptions, -XX:-BytecodeVerificationRemote, -XX:-BytecodeVerificationLocal"; |
|
42 |
|
43 static final String ERR = |
|
44 "ERROR: class VerifierTestC was loaded unexpectedly"; |
|
45 static final String MAP_FAIL = |
|
46 "shared archive file was created with less restrictive verification setting"; |
|
47 static final String VFY_ERR = "java.lang.VerifyError"; |
|
48 static final String PASS_RESULT = "Hi, how are you?"; |
|
49 static final String VFY_INFO_MESSAGE = |
|
50 "All non-system classes will be verified (-Xverify:remote) during CDS dump time."; |
|
51 static final String CDS_LOGGING = "-Xlog:cds,cds+hashtables"; |
|
52 |
|
53 enum Testset1Part { |
|
54 A, B |
|
55 } |
|
56 |
|
57 public static void main(String[] args) throws Exception { |
|
58 String subCaseId = args[0]; |
|
59 String jarName_verifier_test_tmp = "verifier_test_tmp" + "_" + subCaseId; |
|
60 String jarName_verifier_test = "verifier_test" + "_" + subCaseId; |
|
61 String jarName_greet = "greet" + "_" + subCaseId; |
|
62 String jarName_hi = "hi" + "_" + subCaseId; |
|
63 |
|
64 |
|
65 JarBuilder.build(jarName_verifier_test_tmp, "VerifierTest0", "VerifierTestA", |
|
66 "VerifierTestB", "VerifierTestC", "VerifierTestD", "VerifierTestE", |
|
67 "UnverifiableBase", "UnverifiableIntf", "UnverifiableIntfSub"); |
|
68 JarBuilder.build(jarName_greet, "Greet"); |
|
69 JarBuilder.build(jarName_hi, "Hi", "Hi$MyClass"); |
|
70 |
|
71 File dir = new File(System.getProperty("test.classes", ".")); |
|
72 File jarSrcFile = new File(dir, jarName_verifier_test_tmp + ".jar"); |
|
73 File jarFile = new File(dir, jarName_verifier_test + ".jar"); |
|
74 String jar = jarFile.getPath(); |
|
75 |
|
76 if (!jarFile.exists() || jarFile.lastModified() < jarSrcFile.lastModified()) { |
|
77 createTestJarFile(jarSrcFile, jarFile); |
|
78 } else { |
|
79 System.out.println("Already up-to-date: " + jarFile); |
|
80 } |
|
81 |
|
82 String noAppClasses[] = TestCommon.list(""); |
|
83 String appClasses[] = TestCommon.list("UnverifiableBase", |
|
84 "UnverifiableIntf", |
|
85 "UnverifiableIntfSub", |
|
86 "VerifierTestA", |
|
87 "VerifierTestB", |
|
88 "VerifierTestC", |
|
89 "VerifierTestD", |
|
90 "VerifierTestE", |
|
91 "VerifierTest0"); |
|
92 |
|
93 |
|
94 switch (subCaseId) { |
|
95 case "0": testset_0(jar, noAppClasses, appClasses); return; |
|
96 case "1A": testset_1(jar, noAppClasses, appClasses, Testset1Part.A); return; |
|
97 case "1B": testset_1(jar, noAppClasses, appClasses, Testset1Part.B); return; |
|
98 case "2": testset_2(jarName_greet, jarName_hi); return; |
|
99 default: |
|
100 throw new RuntimeException("Unknown option: " + subCaseId); |
|
101 } |
|
102 } |
|
103 |
|
104 static void testset_0(String jar, String[] noAppClasses, String[] appClasses) throws Exception { |
|
105 // Unverifiable classes won't be included in the CDS archive. |
|
106 // Dumping should not fail. |
|
107 OutputAnalyzer output = TestCommon.dump(jar, appClasses); |
|
108 output.shouldHaveExitValue(0); |
|
109 if (output.getStdout().contains("Loading clases to share")) { |
|
110 // last entry in appClasses[] is a verifiable class |
|
111 for (int i = 0; i < (appClasses.length - 1); i++) { |
|
112 output.shouldContain("Verification failed for " + appClasses[i]); |
|
113 output.shouldContain("Removed error class: " + appClasses[i]); |
|
114 } |
|
115 } |
|
116 } |
|
117 |
|
118 static void checkRuntimeOutput(OutputAnalyzer output, String expected) throws Exception { |
|
119 output.shouldContain(expected); |
|
120 if (expected.equals(PASS_RESULT) || |
|
121 expected.equals(VFY_ERR)) { |
|
122 output.shouldHaveExitValue(0); |
|
123 } else { |
|
124 output.shouldNotHaveExitValue(0); |
|
125 } |
|
126 } |
|
127 |
|
128 static void testset_1(String jar, String[] noAppClasses, String[] appClasses, Testset1Part part) |
|
129 throws Exception |
|
130 { |
|
131 String config[][] = { |
|
132 // {dump_list, dumptime_verification_setting, |
|
133 // runtime_verification_setting, expected_output_str}, |
|
134 |
|
135 // Dump app/ext with -Xverify:remote |
|
136 {"app", VFY_REMOTE, VFY_REMOTE, VFY_ERR}, |
|
137 {"app", VFY_REMOTE, VFY_ALL, MAP_FAIL}, |
|
138 {"app", VFY_REMOTE, VFY_NONE, ERR }, |
|
139 // Dump app/ext with -Xverify:all |
|
140 {"app", VFY_ALL, VFY_REMOTE, VFY_ERR }, |
|
141 {"app", VFY_ALL, VFY_ALL, VFY_ERR }, |
|
142 {"app", VFY_ALL, VFY_NONE, ERR }, |
|
143 // Dump app/ext with verifier turned off |
|
144 {"app", VFY_NONE, VFY_REMOTE, VFY_ERR}, |
|
145 {"app", VFY_NONE, VFY_ALL, MAP_FAIL}, |
|
146 {"app", VFY_NONE, VFY_NONE, ERR }, |
|
147 // Dump sys only with -Xverify:remote |
|
148 {"noApp", VFY_REMOTE, VFY_REMOTE, VFY_ERR}, |
|
149 {"noApp", VFY_REMOTE, VFY_ALL, VFY_ERR}, |
|
150 {"noApp", VFY_REMOTE, VFY_NONE, ERR}, |
|
151 // Dump sys only with -Xverify:all |
|
152 {"noApp", VFY_ALL, VFY_REMOTE, VFY_ERR}, |
|
153 {"noApp", VFY_ALL, VFY_ALL, VFY_ERR}, |
|
154 {"noApp", VFY_ALL, VFY_NONE, ERR}, |
|
155 // Dump sys only with verifier turned off |
|
156 {"noApp", VFY_NONE, VFY_REMOTE, VFY_ERR}, |
|
157 {"noApp", VFY_NONE, VFY_ALL, VFY_ERR}, |
|
158 {"noApp", VFY_NONE, VFY_NONE, ERR}, |
|
159 }; |
|
160 |
|
161 int loop_start, loop_stop; |
|
162 |
|
163 // Further break down testset_1 into two parts (to be invoked from VerifierTest_1A.java |
|
164 // and VerifierTest_1B.java) to improve parallel test execution time. |
|
165 switch (part) { |
|
166 case A: |
|
167 loop_start = 0; |
|
168 loop_stop = 9; |
|
169 break; |
|
170 case B: |
|
171 default: |
|
172 assert part == Testset1Part.B; |
|
173 loop_start = 9; |
|
174 loop_stop = config.length; |
|
175 break; |
|
176 } |
|
177 |
|
178 String prev_dump_setting = ""; |
|
179 for (int i = loop_start; i < loop_stop; i ++) { |
|
180 String dump_list[] = config[i][0].equals("app") ? appClasses : |
|
181 noAppClasses; |
|
182 String dump_setting = config[i][1]; |
|
183 String runtime_setting = config[i][2]; |
|
184 String expected_output_str = config[i][3]; |
|
185 System.out.println("Test case [" + i + "]: dumping " + config[i][0] + |
|
186 " with " + dump_setting + |
|
187 ", run with " + runtime_setting); |
|
188 if (!dump_setting.equals(prev_dump_setting)) { |
|
189 String dump_arg1; |
|
190 String dump_arg2; |
|
191 String dump_arg3; |
|
192 // Need to break this into two separate arguments. |
|
193 if (dump_setting.equals(VFY_NONE)) { |
|
194 dump_arg1 = "-XX:+UnlockDiagnosticVMOptions"; |
|
195 dump_arg2 = "-XX:-BytecodeVerificationRemote"; |
|
196 dump_arg3 = "-XX:-BytecodeVerificationLocal"; |
|
197 } else { |
|
198 // Redundant args should be harmless. |
|
199 dump_arg1 = dump_arg2 = dump_arg3 = dump_setting; |
|
200 } |
|
201 |
|
202 OutputAnalyzer dumpOutput = TestCommon.dump( |
|
203 jar, dump_list, dump_arg1, dump_arg2, |
|
204 dump_arg3, CDS_LOGGING, |
|
205 // FIXME: the following options are for working around a GC |
|
206 // issue - assert failure when dumping archive with the -Xverify:all |
|
207 "-Xms256m", |
|
208 "-Xmx256m"); |
|
209 if (dump_setting.equals(VFY_NONE) && |
|
210 runtime_setting.equals(VFY_REMOTE)) { |
|
211 dumpOutput.shouldContain(VFY_INFO_MESSAGE); |
|
212 } |
|
213 } |
|
214 String runtime_arg1; |
|
215 String runtime_arg2; |
|
216 String runtime_arg3; |
|
217 if (runtime_setting.equals(VFY_NONE)) { |
|
218 runtime_arg1 = "-XX:+UnlockDiagnosticVMOptions"; |
|
219 runtime_arg2 = "-XX:-BytecodeVerificationRemote"; |
|
220 runtime_arg3 = "-XX:-BytecodeVerificationLocal"; |
|
221 } else { |
|
222 // Redundant args should be harmless. |
|
223 runtime_arg1 = runtime_arg2 = runtime_arg3 = runtime_setting; |
|
224 } |
|
225 TestCommon.run("-cp", jar, |
|
226 runtime_arg1, runtime_arg2, runtime_arg3, |
|
227 "VerifierTest0") |
|
228 .ifNoMappingFailure(output -> checkRuntimeOutput(output, expected_output_str)); |
|
229 prev_dump_setting = dump_setting; |
|
230 } |
|
231 } |
|
232 |
|
233 static void testset_2(String jarName_greet, String jarName_hi) throws Exception { |
|
234 String appClasses[]; |
|
235 String jar; |
|
236 |
|
237 // The following section is for testing the scenarios where |
|
238 // the classes are verifiable during dump time. |
|
239 appClasses = TestCommon.list("Hi", |
|
240 "Greet", |
|
241 "Hi$MyClass"); |
|
242 jar = TestCommon.getTestJar(jarName_hi + ".jar") + File.pathSeparator + |
|
243 TestCommon.getTestJar(jarName_greet + ".jar"); |
|
244 String config2[][] = { |
|
245 // {dump_list, dumptime_verification_setting, |
|
246 // runtime_verification_setting, expected_output_str}, |
|
247 |
|
248 // Dump app/ext with -Xverify:remote |
|
249 {"app", VFY_REMOTE, VFY_REMOTE, PASS_RESULT}, |
|
250 {"app", VFY_REMOTE, VFY_ALL, MAP_FAIL}, |
|
251 {"app", VFY_REMOTE, VFY_NONE, PASS_RESULT }, |
|
252 // Dump app/ext with -Xverify:all |
|
253 {"app", VFY_ALL, VFY_REMOTE, PASS_RESULT }, |
|
254 {"app", VFY_ALL, VFY_ALL, PASS_RESULT }, |
|
255 {"app", VFY_ALL, VFY_NONE, PASS_RESULT }, |
|
256 // Dump app/ext with verifier turned off |
|
257 {"app", VFY_NONE, VFY_REMOTE, PASS_RESULT}, |
|
258 {"app", VFY_NONE, VFY_ALL, MAP_FAIL}, |
|
259 {"app", VFY_NONE, VFY_NONE, PASS_RESULT }, |
|
260 }; |
|
261 String prev_dump_setting = ""; |
|
262 for (int i = 0; i < config2.length; i ++) { |
|
263 // config2[i][0] is always set to "app" in this test |
|
264 String dump_setting = config2[i][1]; |
|
265 String runtime_setting = config2[i][2]; |
|
266 String expected_output_str = config2[i][3]; |
|
267 System.out.println("Test case [" + i + "]: dumping " + config2[i][0] + |
|
268 " with " + dump_setting + |
|
269 ", run with " + runtime_setting); |
|
270 if (!dump_setting.equals(prev_dump_setting)) { |
|
271 String dump_arg1; |
|
272 String dump_arg2; |
|
273 String dump_arg3; |
|
274 if (dump_setting.equals(VFY_NONE)) { |
|
275 dump_arg1 = "-XX:+UnlockDiagnosticVMOptions"; |
|
276 dump_arg2 = "-XX:-BytecodeVerificationRemote"; |
|
277 dump_arg3 = "-XX:-BytecodeVerificationLocal"; |
|
278 } else { |
|
279 // Redundant args should be harmless. |
|
280 dump_arg1 = dump_arg2 = dump_arg3 = dump_setting; |
|
281 } |
|
282 OutputAnalyzer dumpOutput = TestCommon.dump( |
|
283 jar, appClasses, dump_arg1, dump_arg2, |
|
284 dump_arg3, CDS_LOGGING, |
|
285 // FIXME: the following options are for working around a GC |
|
286 // issue - assert failure when dumping archive with the -Xverify:all |
|
287 "-Xms256m", |
|
288 "-Xmx256m"); |
|
289 if (dump_setting.equals(VFY_NONE) && |
|
290 runtime_setting.equals(VFY_REMOTE)) { |
|
291 dumpOutput.shouldContain(VFY_INFO_MESSAGE); |
|
292 } |
|
293 } |
|
294 String runtime_arg1; |
|
295 String runtime_arg2; |
|
296 String runtime_arg3; |
|
297 if (runtime_setting.equals(VFY_NONE)) { |
|
298 runtime_arg1 = "-XX:+UnlockDiagnosticVMOptions"; |
|
299 runtime_arg2 = "-XX:-BytecodeVerificationRemote"; |
|
300 runtime_arg3 = "-XX:-BytecodeVerificationLocal"; |
|
301 } else { |
|
302 // Redundant args should be harmless. |
|
303 runtime_arg1 = runtime_arg2 = runtime_arg3 = runtime_setting; |
|
304 } |
|
305 TestCommon.run("-cp", jar, |
|
306 runtime_arg1, runtime_arg2, runtime_arg3, |
|
307 "Hi") |
|
308 .ifNoMappingFailure(output -> checkRuntimeOutput(output, expected_output_str)); |
|
309 prev_dump_setting = dump_setting; |
|
310 } |
|
311 } |
|
312 |
|
313 static void createTestJarFile(File jarSrcFile, File jarFile) throws Exception { |
|
314 jarFile.delete(); |
|
315 Files.copy(jarSrcFile.toPath(), jarFile.toPath()); |
|
316 |
|
317 File dir = new File(System.getProperty("test.classes", ".")); |
|
318 File outdir = new File(dir, "verifier_test_classes"); |
|
319 outdir.mkdir(); |
|
320 |
|
321 writeClassFile(new File(outdir, "UnverifiableBase.class"), makeUnverifiableBase()); |
|
322 writeClassFile(new File(outdir, "UnverifiableIntf.class"), makeUnverifiableIntf()); |
|
323 |
|
324 JarBuilder.update(jarFile.getPath(), outdir.getPath()); |
|
325 } |
|
326 |
|
327 static void writeClassFile(File file, byte bytecodes[]) throws Exception { |
|
328 try (FileOutputStream fos = new FileOutputStream(file)) { |
|
329 fos.write(bytecodes); |
|
330 } |
|
331 } |
|
332 |
|
333 // This was obtained using JDK8: java jdk.internal.org.objectweb.asm.util.ASMifier tmpclasses/UnverifiableBase.class |
|
334 static byte[] makeUnverifiableBase() throws Exception { |
|
335 ClassWriter cw = new ClassWriter(0); |
|
336 FieldVisitor fv; |
|
337 MethodVisitor mv; |
|
338 AnnotationVisitor av0; |
|
339 |
|
340 cw.visit(V1_8, ACC_SUPER, "UnverifiableBase", null, "java/lang/Object", null); |
|
341 { |
|
342 fv = cw.visitField(ACC_FINAL + ACC_STATIC, "x", "LVerifierTest;", null, null); |
|
343 fv.visitEnd(); |
|
344 } |
|
345 { |
|
346 mv = cw.visitMethod(0, "<init>", "()V", null, null); |
|
347 mv.visitCode(); |
|
348 mv.visitVarInsn(ALOAD, 0); |
|
349 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); |
|
350 mv.visitInsn(RETURN); |
|
351 mv.visitMaxs(1, 1); |
|
352 mv.visitEnd(); |
|
353 } |
|
354 { |
|
355 mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); |
|
356 mv.visitCode(); |
|
357 mv.visitTypeInsn(NEW, "VerifierTest0"); |
|
358 mv.visitInsn(DUP); |
|
359 mv.visitMethodInsn(INVOKESPECIAL, "VerifierTest0", "<init>", "()V", false); |
|
360 mv.visitFieldInsn(PUTSTATIC, "UnverifiableBase", "x", "LVerifierTest;"); |
|
361 mv.visitInsn(RETURN); |
|
362 mv.visitMaxs(2, 0); |
|
363 mv.visitEnd(); |
|
364 } |
|
365 addBadMethod(cw); |
|
366 cw.visitEnd(); |
|
367 |
|
368 return cw.toByteArray(); |
|
369 } |
|
370 |
|
371 // This was obtained using JDK8: java jdk.internal.org.objectweb.asm.util.ASMifier tmpclasses/UnverifiableIntf.class |
|
372 static byte[] makeUnverifiableIntf() throws Exception { |
|
373 ClassWriter cw = new ClassWriter(0); |
|
374 FieldVisitor fv; |
|
375 MethodVisitor mv; |
|
376 AnnotationVisitor av0; |
|
377 |
|
378 cw.visit(V1_8, ACC_ABSTRACT + ACC_INTERFACE, "UnverifiableIntf", null, "java/lang/Object", null); |
|
379 |
|
380 { |
|
381 fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "x", "LVerifierTest0;", null, null); |
|
382 fv.visitEnd(); |
|
383 } |
|
384 { |
|
385 mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); |
|
386 mv.visitCode(); |
|
387 mv.visitTypeInsn(NEW, "VerifierTest0"); |
|
388 mv.visitInsn(DUP); |
|
389 mv.visitMethodInsn(INVOKESPECIAL, "VerifierTest0", "<init>", "()V", false); |
|
390 mv.visitFieldInsn(PUTSTATIC, "UnverifiableIntf", "x", "LVerifierTest0;"); |
|
391 mv.visitInsn(RETURN); |
|
392 mv.visitMaxs(2, 0); |
|
393 mv.visitEnd(); |
|
394 } |
|
395 addBadMethod(cw); |
|
396 cw.visitEnd(); |
|
397 |
|
398 return cw.toByteArray(); |
|
399 } |
|
400 |
|
401 // Add a bad method to make the class fail verification. |
|
402 static void addBadMethod(ClassWriter cw) throws Exception { |
|
403 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "bad", "()V", null, null); |
|
404 mv.visitCode(); |
|
405 mv.visitInsn(ARETURN); // java.lang.VerifyError: Operand stack underflow |
|
406 mv.visitMaxs(2, 2); |
|
407 mv.visitEnd(); |
|
408 } |
|
409 } |
|