1 /*
  2  * Copyright (c) 2023, 2024, 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 
 47 /**
 48  * ParserVerifier performs selected checks of the class file format according to
 49  * {@jvms 4.8 Format Checking}
 50  *
 51  * @see <a href="https://raw.githubusercontent.com/openjdk/jdk/master/src/hotspot/share/classfile/classFileParser.cpp">hotspot/share/classfile/classFileParser.cpp</a>
 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 LoadableDescriptorsAttribute lda ->
281                 2 + 2 * lda.loadableDescriptors().size();
282             case LocalVariableTableAttribute lvta ->
283                 2 + 10 * lvta.localVariables().size();
284             case LocalVariableTypeTableAttribute lvta ->
285                 2 + 10 * lvta.localVariableTypes().size();
286             case MethodParametersAttribute mpa ->
287                 1 + 4 * mpa.parameters().size();
288             case ModuleAttribute ma ->
289                 16 + subSize(ma.exports(), ModuleExportInfo::exportsTo, 6, 2)
290                    + subSize(ma.opens(), ModuleOpenInfo::opensTo, 6, 2)
291                    + subSize(ma.provides(), ModuleProvideInfo::providesWith, 4, 2)
292                    + 6 * ma.requires().size()
293                    + 2 * ma.uses().size();
294             case ModuleHashesAttribute mha ->
295                 2 + moduleHashesSize(mha.hashes());
296             case ModuleMainClassAttribute mmca -> {
297                 mmca.mainClass();
298                 yield 2;
299             }
300             case ModulePackagesAttribute mpa ->
301                 2 + 2 * mpa.packages().size();
302             case ModuleResolutionAttribute mra ->
303                 2;
304             case ModuleTargetAttribute mta -> {
305                 mta.targetPlatform();
306                 yield 2;
307             }
308             case NestHostAttribute nha -> {
309                 nha.nestHost();
310                 yield 2;
311             }
312             case NestMembersAttribute nma -> {
313                 if (ae.findAttribute(Attributes.nestHost()).isPresent()) {
314                     errors.add(new VerifyError("Conflicting NestHost and NestMembers attributes in %s".formatted(toString(ae))));
315                 }
316                 yield 2 + 2 * nma.nestMembers().size();
317             }
318             case PermittedSubclassesAttribute psa -> {
319                 if (classModel.flags().has(AccessFlag.FINAL)) {
320                     errors.add(new VerifyError("PermittedSubclasses attribute in final %s".formatted(toString(ae))));
321                 }
322                 yield 2 + 2 * psa.permittedSubclasses().size();
323             }
324             case RecordAttribute ra ->
325                 componentsSize(ra.components());
326             case RuntimeVisibleAnnotationsAttribute aa ->
327                 annotationsSize(aa.annotations());
328             case RuntimeInvisibleAnnotationsAttribute aa ->
329                 annotationsSize(aa.annotations());
330             case RuntimeVisibleTypeAnnotationsAttribute aa ->
331                 typeAnnotationsSize(aa.annotations());
332             case RuntimeInvisibleTypeAnnotationsAttribute aa ->
333                 typeAnnotationsSize(aa.annotations());
334             case RuntimeVisibleParameterAnnotationsAttribute aa ->
335                 parameterAnnotationsSize(aa.parameterAnnotations());
336             case RuntimeInvisibleParameterAnnotationsAttribute aa ->
337                 parameterAnnotationsSize(aa.parameterAnnotations());
338             case SignatureAttribute sa -> {
339                 sa.signature();
340                 yield 2;
341             }
342             case SourceDebugExtensionAttribute sda ->
343                 sda.contents().length;
344             case SourceFileAttribute sfa -> {
345                 sfa.sourceFile();
346                 yield 2;
347             }
348             case SourceIDAttribute sida -> {
349                 sida.sourceId();
350                 yield 2;
351             }
352             case StackMapTableAttribute smta ->
353                 2 + subSize(smta.entries(), frame -> stackMapFrameSize(frame));
354             case SyntheticAttribute _ ->
355                 0;
356             case UnknownAttribute _ ->
357                 -1;
358             case CustomAttribute<?> _ ->
359                 -1;
360             default -> // should not happen if all known attributes are verified
361                 throw new AssertionError(a);
362         };
363         if (size >= 0 && size != ((BoundAttribute)a).payloadLen()) {
364             errors.add(new VerifyError("Wrong %s attribute length in %s".formatted(a.attributeName().stringValue(), toString(ae))));
365         }
366     }
367 
368     private static <T, S extends Collection<?>> int subSize(Collection<T> entries, Function<T, S> subMH, int entrySize, int subSize) {
369         return subSize(entries, (ToIntFunction<T>) t -> entrySize + subSize * subMH.apply(t).size());
370     }
371 
372     private static <T> int subSize(Collection<T> entries, ToIntFunction<T> subMH) {
373         int l = 0;
374         for (T entry : entries) {
375             l += subMH.applyAsInt(entry);
376         }
377         return l;
378     }
379 
380     private static int componentsSize(List<RecordComponentInfo> comps) {
381         int l = 2;
382         for (var rc : comps) {
383             l += 4 + attributesSize(rc.attributes());
384         }
385         return l;
386     }
387 
388     private static int attributesSize(List<Attribute<?>> attrs) {
389         int l = 2;
390         for (var a : attrs) {
391             l += 6 + ((BoundAttribute)a).payloadLen();
392         }
393         return l;
394     }
395 
396     private static int parameterAnnotationsSize(List<List<Annotation>> pans) {
397         int l = 1;
398         for (var ans : pans) {
399             l += annotationsSize(ans);
400         }
401         return l;
402     }
403 
404     private static int annotationsSize(List<Annotation> ans) {
405         int l = 2;
406         for (var an : ans) {
407             l += annotationSize(an);
408         }
409         return l;
410     }
411 
412     private static int typeAnnotationsSize(List<TypeAnnotation> ans) {
413         int l = 2;
414         for (var an : ans) {
415             l += 2 + an.targetInfo().size() + 2 * an.targetPath().size() + annotationSize(an.annotation());
416         }
417         return l;
418     }
419 
420     private static int annotationSize(Annotation an) {
421         int l = 4;
422         for (var el : an.elements()) {
423             l += 2 + valueSize(el.value());
424         }
425         return l;
426     }
427 
428     private static int valueSize(AnnotationValue val) {
429         return 1 + switch (val) {
430             case AnnotationValue.OfAnnotation oan ->
431                 annotationSize(oan.annotation());
432             case AnnotationValue.OfArray oar -> {
433                 int l = 2;
434                 for (var v : oar.values()) {
435                     l += valueSize(v);
436                 }
437                 yield l;
438             }
439             case AnnotationValue.OfConstant _, AnnotationValue.OfClass _ -> 2;
440             case AnnotationValue.OfEnum _ -> 4;
441         };
442     }
443 
444     private static int moduleHashesSize(List<ModuleHashInfo> hashes) {
445         int l = 2;
446         for (var h : hashes) {
447             h.moduleName();
448             l += 4 + h.hash().length;
449         }
450         return l;
451     }
452 
453     private int stackMapFrameSize(StackMapFrameInfo frame) {
454         int ft = frame.frameType();
455         if (ft < 64) return 1;
456         if (ft < 128) return 1 + verificationTypeSize(frame.stack().getFirst());
457         if (ft > 246) {
458             if (ft == 247) return 3 + verificationTypeSize(frame.stack().getFirst());
459             if (ft < 252) return 3;
460             if (ft < 255) {
461                 var loc = frame.locals();
462                 int l = 3;
463                 for (int i = loc.size() + 251 - ft; i < loc.size(); i++) {
464                     l += verificationTypeSize(loc.get(i));
465                 }
466                 return l;
467             }
468             if (ft == 255) {
469                 int l = 7;
470                 for (var vt : frame.stack()) {
471                     l += verificationTypeSize(vt);
472                 }
473                 for (var vt : frame.locals()) {
474                     l += verificationTypeSize(vt);
475                 }
476                 return l;
477             }
478         }
479         throw new IllegalArgumentException("Invalid stack map frame type " + ft);
480     }
481 
482     private static int verificationTypeSize(StackMapFrameInfo.VerificationTypeInfo vti) {
483         return switch (vti) {
484             case StackMapFrameInfo.SimpleVerificationTypeInfo _ -> 1;
485             case StackMapFrameInfo.ObjectVerificationTypeInfo ovti -> {
486                 ovti.classSymbol();
487                 yield 3;
488             }
489             case StackMapFrameInfo.UninitializedVerificationTypeInfo _ -> 3;
490         };
491     }
492 
493     private String className() {
494         return classModel.thisClass().asSymbol().displayName();
495     }
496 
497     private String toString(AttributedElement ae) {
498         return switch (ae) {
499             case CodeModel m -> "Code attribute for " + toString(m.parent().get());
500             case FieldModel m -> "field %s.%s".formatted(
501                     className(),
502                     m.fieldName().stringValue());
503             case MethodModel m -> "method %s::%s(%s)".formatted(
504                     className(),
505                     m.methodName().stringValue(),
506                     m.methodTypeSymbol().parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")));
507             case RecordComponentInfo i -> "Record component %s of class %s".formatted(
508                     i.name().stringValue(),
509                     className());
510             default -> "class " + className();
511         };
512     }
513 }