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