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