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 }