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