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