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