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