1 /*
2 * Copyright (c) 2023, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package jdk.internal.classfile.impl.verifier;
26
27 import java.lang.classfile.*;
28 import java.lang.classfile.attribute.*;
29 import java.lang.classfile.constantpool.*;
30 import java.lang.constant.ClassDesc;
31 import java.lang.constant.ConstantDescs;
32 import java.lang.reflect.AccessFlag;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.stream.Collectors;
38 import java.util.function.Function;
39 import java.util.function.ToIntFunction;
40
41 import jdk.internal.classfile.impl.BoundAttribute;
42 import jdk.internal.classfile.impl.Util;
43
44 import static java.lang.constant.ConstantDescs.CLASS_INIT_NAME;
45 import static java.lang.constant.ConstantDescs.INIT_NAME;
46 import static jdk.internal.classfile.impl.StackMapGenerator.*;
47
48 /// ParserVerifier performs selected checks of the class file format according to
49 /// {@jvms 4.8 Format Checking}.
50 ///
51 /// From `classFileParser.cpp`.
52 ///
53 public record ParserVerifier(ClassModel classModel) {
54
55 List<VerifyError> verify() {
56 var errors = new ArrayList<VerifyError>();
57 verifyConstantPool(errors);
58 verifyInterfaces(errors);
59 verifyFields(errors);
60 verifyMethods(errors);
61 verifyAttributes(classModel, errors);
62 return errors;
63 }
64
65 private void verifyConstantPool(List<VerifyError> errors) {
66 for (var cpe : classModel.constantPool()) {
67 try {
68 switch (cpe) {
69 case DoubleEntry de -> de.doubleValue();
70 case FloatEntry fe -> fe.floatValue();
71 case IntegerEntry ie -> ie.intValue();
72 case LongEntry le -> le.longValue();
73 case Utf8Entry ue -> ue.stringValue();
74 case ConstantDynamicEntry cde -> cde.asSymbol();
75 case InvokeDynamicEntry ide -> ide.asSymbol();
76 case ClassEntry ce -> ce.asSymbol();
77 case StringEntry se -> se.stringValue();
78 case MethodHandleEntry mhe -> mhe.asSymbol();
79 case MethodTypeEntry mte -> mte.asSymbol();
80 case FieldRefEntry fre -> {
81 try {
82 fre.owner().asSymbol();
83 } catch (VerifyError|Exception e) {
84 errors.add(cpeVerifyError(cpe, e));
85 }
86 try {
87 fre.typeSymbol();
88 } catch (VerifyError|Exception e) {
89 errors.add(cpeVerifyError(cpe, e));
90 }
91 verifyFieldName(fre.name().stringValue());
92 }
93 case InterfaceMethodRefEntry imre -> {
94 try {
95 imre.owner().asSymbol();
96 } catch (VerifyError|Exception e) {
97 errors.add(cpeVerifyError(cpe, e));
98 }
99 try {
100 imre.typeSymbol();
101 } catch (VerifyError|Exception e) {
102 errors.add(cpeVerifyError(cpe, e));
103 }
104 verifyMethodName(imre.name().stringValue());
105 }
106 case MethodRefEntry mre -> {
107 try {
108 mre.owner().asSymbol();
109 } catch (VerifyError|Exception e) {
110 errors.add(cpeVerifyError(cpe, e));
111 }
112 try {
113 mre.typeSymbol();
114 } catch (VerifyError|Exception e) {
115 errors.add(cpeVerifyError(cpe, e));
116 }
117 verifyMethodName(mre.name().stringValue());
118 }
119 case ModuleEntry me -> me.asSymbol();
120 case NameAndTypeEntry nate -> {
121 try {
122 nate.name().stringValue();
123 } catch (VerifyError|Exception e) {
124 errors.add(cpeVerifyError(cpe, e));
125 }
126 nate.type().stringValue();
127 }
128 case PackageEntry pe -> pe.asSymbol();
129 }
130 } catch (VerifyError|Exception e) {
131 errors.add(cpeVerifyError(cpe, e));
132 }
133 }
134 }
135
136 private VerifyError cpeVerifyError(final PoolEntry cpe, final Throwable e) {
137 return new VerifyError("%s at constant pool index %d in %s".formatted(e.getMessage(), cpe.index(), toString(classModel)));
138 }
139
140 private void verifyFieldName(String name) {
141 if (name.length() == 0 || name.chars().anyMatch(ch -> switch(ch) {
142 case '.', ';', '[', '/' -> true;
143 default -> false;
144 })) {
145 throw new VerifyError("Illegal field name %s in %s".formatted(name, toString(classModel)));
146 }
147 }
148
149 private void verifyMethodName(String name) {
150 if (!name.equals(INIT_NAME)
151 && !name.equals(CLASS_INIT_NAME)
152 && (name.length() == 0 || name.chars().anyMatch(ch -> switch(ch) {
153 case '.', ';', '[', '/', '<', '>' -> true;
154 default -> false;
155 }))) {
156 throw new VerifyError("Illegal method name %s in %s".formatted(name, toString(classModel)));
157 }
158 }
159
160 private void verifyInterfaces(List<VerifyError> errors) {
161 var intfs = new HashSet<ClassEntry>();
162 for (var intf : classModel.interfaces()) {
163 if (!intfs.add(intf)) {
164 errors.add(new VerifyError("Duplicate interface %s in %s".formatted(intf.asSymbol().displayName(), toString(classModel))));
165 }
166 }
167 }
168
169 private void verifyFields(List<VerifyError> errors) {
170 record F(Utf8Entry name, Utf8Entry type) {};
171 var fields = new HashSet<F>();
172 for (var f : classModel.fields()) try {
173 if (!fields.add(new F(f.fieldName(), f.fieldType()))) {
174 errors.add(new VerifyError("Duplicate field name %s with signature %s in %s".formatted(f.fieldName().stringValue(), f.fieldType().stringValue(), toString(classModel))));
175 }
176 verifyFieldName(f.fieldName().stringValue());
177 } catch (VerifyError ve) {
178 errors.add(ve);
179 }
180 }
181
182 private void verifyMethods(List<VerifyError> errors) {
183 record M(Utf8Entry name, Utf8Entry type) {};
184 var methods = new HashSet<M>();
185 for (var m : classModel.methods()) try {
186 if (!methods.add(new M(m.methodName(), m.methodType()))) {
187 errors.add(new VerifyError("Duplicate method name %s with signature %s in %s".formatted(m.methodName().stringValue(), m.methodType().stringValue(), toString(classModel))));
188 }
189 if (m.methodName().equalsString(CLASS_INIT_NAME)
190 && !m.flags().has(AccessFlag.STATIC)) {
191 errors.add(new VerifyError("Method <clinit> is not static in %s".formatted(toString(classModel))));
192 }
193 if (classModel.flags().has(AccessFlag.INTERFACE)
194 && m.methodName().equalsString(INIT_NAME)) {
195 errors.add(new VerifyError("Interface cannot have a method named <init> in %s".formatted(toString(classModel))));
196 }
197 verifyMethodName(m.methodName().stringValue());
198 } catch (VerifyError ve) {
199 errors.add(ve);
200 }
201 }
202
203 private void verifyAttributes(ClassFileElement cfe, List<VerifyError> errors) {
204 if (cfe instanceof AttributedElement ae) {
205 var attrNames = new HashSet<String>();
206 for (var a : ae.attributes()) {
207 if (!a.attributeMapper().allowMultiple() && !attrNames.add(a.attributeName().stringValue())) {
208 errors.add(new VerifyError("Multiple %s attributes in %s".formatted(a.attributeName().stringValue(), toString(ae))));
209 }
210 verifyAttribute(ae, a, errors);
211 }
212 }
213 switch (cfe) {
214 case CompoundElement<?> comp -> {
215 for (var e : comp) verifyAttributes(e, errors);
216 }
217 case RecordAttribute ra -> {
218 for(var rc : ra.components()) verifyAttributes(rc, errors);
219 }
220 default -> {}
221 }
222 }
223
224 private void verifyAttribute(AttributedElement ae, Attribute<?> a, List<VerifyError> errors) {
225 int size = switch (a) {
226 case AnnotationDefaultAttribute aa ->
227 valueSize(aa.defaultValue());
228 case BootstrapMethodsAttribute bma ->
229 2 + bma.bootstrapMethods().stream().mapToInt(bm -> 4 + 2 * bm.arguments().size()).sum();
230 case CharacterRangeTableAttribute cra ->
231 2 + 14 * cra.characterRangeTable().size();
232 case CodeAttribute ca -> {
233 MethodModel mm = (MethodModel)ae;
234 if (mm.flags().has(AccessFlag.NATIVE) || mm.flags().has(AccessFlag.ABSTRACT)) {
235 errors.add(new VerifyError("Code attribute in native or abstract %s".formatted(toString(ae))));
236 }
237 if (ca.maxLocals() < Util.maxLocals(mm.flags().flagsMask(), mm.methodTypeSymbol())) {
238 errors.add(new VerifyError("Arguments can't fit into locals in %s".formatted(toString(ae))));
239 }
240 yield 10 + ca.codeLength() + 8 * ca.exceptionHandlers().size() + attributesSize(ca.attributes());
241 }
242 case CompilationIDAttribute cida -> {
243 cida.compilationId();
244 yield 2;
245 }
246 case ConstantValueAttribute cva -> {
247 ClassDesc type = ((FieldModel)ae).fieldTypeSymbol();
248 ConstantValueEntry cve = cva.constant();
249 if (!switch (TypeKind.from(type)) {
250 case BOOLEAN, BYTE, CHAR, INT, SHORT -> cve instanceof IntegerEntry;
251 case DOUBLE -> cve instanceof DoubleEntry;
252 case FLOAT -> cve instanceof FloatEntry;
253 case LONG -> cve instanceof LongEntry;
254 case REFERENCE -> type.equals(ConstantDescs.CD_String) && cve instanceof StringEntry;
255 case VOID -> false;
256 }) {
257 errors.add(new VerifyError("Bad constant value type in %s".formatted(toString(ae))));
258 }
259 yield 2;
260 }
261 case DeprecatedAttribute _ ->
262 0;
263 case EnclosingMethodAttribute ema -> {
264 ema.enclosingClass();
265 ema.enclosingMethod();
266 yield 4;
267 }
268 case ExceptionsAttribute ea ->
269 2 + 2 * ea.exceptions().size();
270 case InnerClassesAttribute ica -> {
271 for (var ici : ica.classes()) {
272 if (ici.outerClass().isPresent() && ici.outerClass().get().equals(ici.innerClass())) {
273 errors.add(new VerifyError("Class is both outer and inner class in %s".formatted(toString(ae))));
274 }
275 }
276 yield 2 + 8 * ica.classes().size();
277 }
278 case LineNumberTableAttribute lta ->
279 2 + 4 * lta.lineNumbers().size();
280 case LocalVariableTableAttribute lvta ->
281 2 + 10 * lvta.localVariables().size();
282 case LocalVariableTypeTableAttribute lvta ->
283 2 + 10 * lvta.localVariableTypes().size();
284 case MethodParametersAttribute mpa ->
285 1 + 4 * mpa.parameters().size();
286 case ModuleAttribute ma ->
287 16 + subSize(ma.exports(), ModuleExportInfo::exportsTo, 6, 2)
288 + subSize(ma.opens(), ModuleOpenInfo::opensTo, 6, 2)
289 + subSize(ma.provides(), ModuleProvideInfo::providesWith, 4, 2)
290 + 6 * ma.requires().size()
291 + 2 * ma.uses().size();
292 case ModuleHashesAttribute mha ->
293 2 + moduleHashesSize(mha.hashes());
294 case ModuleMainClassAttribute mmca -> {
295 mmca.mainClass();
296 yield 2;
297 }
298 case ModulePackagesAttribute mpa ->
299 2 + 2 * mpa.packages().size();
300 case ModuleResolutionAttribute mra ->
301 2;
302 case ModuleTargetAttribute mta -> {
303 mta.targetPlatform();
304 yield 2;
305 }
306 case NestHostAttribute nha -> {
307 nha.nestHost();
308 yield 2;
309 }
310 case NestMembersAttribute nma -> {
311 if (ae.findAttribute(Attributes.nestHost()).isPresent()) {
312 errors.add(new VerifyError("Conflicting NestHost and NestMembers attributes in %s".formatted(toString(ae))));
313 }
314 yield 2 + 2 * nma.nestMembers().size();
315 }
316 case PermittedSubclassesAttribute psa -> {
317 if (classModel.flags().has(AccessFlag.FINAL)) {
318 errors.add(new VerifyError("PermittedSubclasses attribute in final %s".formatted(toString(ae))));
319 }
320 yield 2 + 2 * psa.permittedSubclasses().size();
321 }
322 case RecordAttribute ra ->
323 componentsSize(ra.components());
324 case RuntimeVisibleAnnotationsAttribute aa ->
325 annotationsSize(aa.annotations());
326 case RuntimeInvisibleAnnotationsAttribute aa ->
327 annotationsSize(aa.annotations());
328 case RuntimeVisibleTypeAnnotationsAttribute aa ->
329 typeAnnotationsSize(aa.annotations());
330 case RuntimeInvisibleTypeAnnotationsAttribute aa ->
331 typeAnnotationsSize(aa.annotations());
332 case RuntimeVisibleParameterAnnotationsAttribute aa ->
333 parameterAnnotationsSize(aa.parameterAnnotations());
334 case RuntimeInvisibleParameterAnnotationsAttribute aa ->
335 parameterAnnotationsSize(aa.parameterAnnotations());
336 case SignatureAttribute sa -> {
337 sa.signature();
338 yield 2;
339 }
340 case SourceDebugExtensionAttribute sda ->
341 sda.contents().length;
342 case SourceFileAttribute sfa -> {
343 sfa.sourceFile();
344 yield 2;
345 }
346 case SourceIDAttribute sida -> {
347 sida.sourceId();
348 yield 2;
349 }
350 case StackMapTableAttribute smta ->
351 2 + subSize(smta.entries(), frame -> stackMapFrameSize(frame));
352 case SyntheticAttribute _ ->
353 0;
354 case UnknownAttribute _ ->
355 -1;
356 case CustomAttribute<?> _ ->
357 -1;
358 default -> // should not happen if all known attributes are verified
359 throw new AssertionError(a);
360 };
361 if (size >= 0 && size != ((BoundAttribute)a).payloadLen()) {
362 errors.add(new VerifyError("Wrong %s attribute length in %s".formatted(a.attributeName().stringValue(), toString(ae))));
363 }
364 }
365
366 private static <T, S extends Collection<?>> int subSize(Collection<T> entries, Function<T, S> subMH, int entrySize, int subSize) {
367 return subSize(entries, (ToIntFunction<T>) t -> entrySize + subSize * subMH.apply(t).size());
368 }
369
370 private static <T> int subSize(Collection<T> entries, ToIntFunction<T> subMH) {
371 int l = 0;
372 for (T entry : entries) {
373 l += subMH.applyAsInt(entry);
374 }
375 return l;
376 }
377
378 private static int componentsSize(List<RecordComponentInfo> comps) {
379 int l = 2;
380 for (var rc : comps) {
381 l += 4 + attributesSize(rc.attributes());
382 }
383 return l;
384 }
385
386 private static int attributesSize(List<Attribute<?>> attrs) {
387 int l = 2;
388 for (var a : attrs) {
389 l += 6 + ((BoundAttribute)a).payloadLen();
390 }
391 return l;
392 }
393
394 private static int parameterAnnotationsSize(List<List<Annotation>> pans) {
395 int l = 1;
396 for (var ans : pans) {
397 l += annotationsSize(ans);
398 }
399 return l;
400 }
401
402 private static int annotationsSize(List<Annotation> ans) {
403 int l = 2;
404 for (var an : ans) {
405 l += annotationSize(an);
406 }
407 return l;
408 }
409
410 private static int typeAnnotationsSize(List<TypeAnnotation> ans) {
411 int l = 2;
412 for (var an : ans) {
413 l += 2 + an.targetInfo().size() + 2 * an.targetPath().size() + annotationSize(an.annotation());
414 }
415 return l;
416 }
417
418 private static int annotationSize(Annotation an) {
419 int l = 4;
420 for (var el : an.elements()) {
421 l += 2 + valueSize(el.value());
422 }
423 return l;
424 }
425
426 private static int valueSize(AnnotationValue val) {
427 return 1 + switch (val) {
428 case AnnotationValue.OfAnnotation oan ->
429 annotationSize(oan.annotation());
430 case AnnotationValue.OfArray oar -> {
431 int l = 2;
432 for (var v : oar.values()) {
433 l += valueSize(v);
434 }
435 yield l;
436 }
437 case AnnotationValue.OfConstant _, AnnotationValue.OfClass _ -> 2;
438 case AnnotationValue.OfEnum _ -> 4;
439 };
440 }
441
442 private static int moduleHashesSize(List<ModuleHashInfo> hashes) {
443 int l = 2;
444 for (var h : hashes) {
445 h.moduleName();
446 l += 4 + h.hash().length;
447 }
448 return l;
449 }
450
451 private int stackMapFrameSize(StackMapFrameInfo frame) {
452 int ft = frame.frameType();
453 if (ft <= SAME_FRAME_END) return 1;
454 if (ft <= SAME_LOCALS_1_STACK_ITEM_FRAME_END) return 1 + verificationTypeSize(frame.stack().getFirst());
455 if (ft > RESERVED_END) {
456 if (ft == SAME_LOCALS_1_STACK_ITEM_EXTENDED) return 3 + verificationTypeSize(frame.stack().getFirst());
457 if (ft <= SAME_FRAME_EXTENDED) return 3;
458 if (ft <= APPEND_FRAME_END) {
459 var loc = frame.locals();
460 int l = 3;
461 var k = ft - APPEND_FRAME_START + 1;
462 for (int i = loc.size() - k; i < loc.size(); i++) {
463 l += verificationTypeSize(loc.get(i));
464 }
465 return l;
466 }
467 if (ft == FULL_FRAME) {
468 int l = 7;
469 for (var vt : frame.stack()) {
470 l += verificationTypeSize(vt);
471 }
472 for (var vt : frame.locals()) {
473 l += verificationTypeSize(vt);
474 }
475 return l;
476 }
477 }
478 throw new IllegalArgumentException("Invalid stack map frame type " + ft);
479 }
480
481 private static int verificationTypeSize(StackMapFrameInfo.VerificationTypeInfo vti) {
482 return switch (vti) {
483 case StackMapFrameInfo.SimpleVerificationTypeInfo _ -> 1;
484 case StackMapFrameInfo.ObjectVerificationTypeInfo ovti -> {
485 ovti.classSymbol();
486 yield 3;
487 }
488 case StackMapFrameInfo.UninitializedVerificationTypeInfo _ -> 3;
489 };
490 }
491
492 private String className() {
493 return classModel.thisClass().asSymbol().displayName();
494 }
495
496 private String toString(AttributedElement ae) {
497 return switch (ae) {
498 case CodeModel m -> "Code attribute for " + toString(m.parent().get());
499 case FieldModel m -> "field %s.%s".formatted(
500 className(),
501 m.fieldName().stringValue());
502 case MethodModel m -> "method %s::%s(%s)".formatted(
503 className(),
504 m.methodName().stringValue(),
505 m.methodTypeSymbol().parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")));
506 case RecordComponentInfo i -> "Record component %s of class %s".formatted(
507 i.name().stringValue(),
508 className());
509 default -> "class " + className();
510 };
511 }
512 }