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 ---