1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.verifier.statics;
18
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Set;
25
26 import org.apache.bcel.Const;
27 import org.apache.bcel.Constants;
28 import org.apache.bcel.Repository;
29 import org.apache.bcel.classfile.Attribute;
30 import org.apache.bcel.classfile.ClassFormatException;
31 import org.apache.bcel.classfile.Code;
32 import org.apache.bcel.classfile.CodeException;
33 import org.apache.bcel.classfile.Constant;
34 import org.apache.bcel.classfile.ConstantClass;
35 import org.apache.bcel.classfile.ConstantDouble;
36 import org.apache.bcel.classfile.ConstantFieldref;
37 import org.apache.bcel.classfile.ConstantFloat;
38 import org.apache.bcel.classfile.ConstantInteger;
39 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
40 import org.apache.bcel.classfile.ConstantLong;
41 import org.apache.bcel.classfile.ConstantMethodref;
42 import org.apache.bcel.classfile.ConstantNameAndType;
43 import org.apache.bcel.classfile.ConstantPool;
44 import org.apache.bcel.classfile.ConstantString;
45 import org.apache.bcel.classfile.ConstantUtf8;
46 import org.apache.bcel.classfile.ConstantValue;
47 import org.apache.bcel.classfile.Deprecated;
48 import org.apache.bcel.classfile.DescendingVisitor;
49 import org.apache.bcel.classfile.EmptyVisitor;
50 import org.apache.bcel.classfile.ExceptionTable;
51 import org.apache.bcel.classfile.Field;
52 import org.apache.bcel.classfile.InnerClass;
53 import org.apache.bcel.classfile.InnerClasses;
54 import org.apache.bcel.classfile.JavaClass;
55 import org.apache.bcel.classfile.LineNumber;
56 import org.apache.bcel.classfile.LineNumberTable;
57 import org.apache.bcel.classfile.LocalVariable;
58 import org.apache.bcel.classfile.LocalVariableTable;
59 import org.apache.bcel.classfile.Method;
60 import org.apache.bcel.classfile.Node;
61 import org.apache.bcel.classfile.SourceFile;
62 import org.apache.bcel.classfile.Synthetic;
63 import org.apache.bcel.classfile.Unknown;
64 import org.apache.bcel.classfile.Utility;
65 import org.apache.bcel.generic.ArrayType;
66 import org.apache.bcel.generic.ObjectType;
67 import org.apache.bcel.generic.Type;
68 import org.apache.bcel.verifier.PassVerifier;
69 import org.apache.bcel.verifier.VerificationResult;
70 import org.apache.bcel.verifier.Verifier;
71 import org.apache.bcel.verifier.VerifierFactory;
72 import org.apache.bcel.verifier.exc.AssertionViolatedException;
73 import org.apache.bcel.verifier.exc.ClassConstraintException;
74 import org.apache.bcel.verifier.exc.LocalVariableInfoInconsistentException;
75
76
77
78
79
80
81
82 public final class Pass2Verifier extends PassVerifier implements Constants {
83
84
85
86
87
88
89
90 private final class CPESSC_Visitor extends EmptyVisitor {
91 private final Class<?> CONST_Class;
92
93
94
95
96 private final Class<?> CONST_String;
97 private final Class<?> CONST_Integer;
98 private final Class<?> CONST_Float;
99 private final Class<?> CONST_Long;
100 private final Class<?> CONST_Double;
101 private final Class<?> CONST_NameAndType;
102 private final Class<?> CONST_Utf8;
103
104 private final JavaClass jc;
105 private final ConstantPool cp;
106 private final int cplen;
107 private final DescendingVisitor carrier;
108
109 private final Set<String> fieldNames = new HashSet<>();
110 private final Set<String> fieldNamesAndDesc = new HashSet<>();
111 private final Set<String> methodNamesAndDesc = new HashSet<>();
112
113 private CPESSC_Visitor(final JavaClass jc) {
114 this.jc = jc;
115 this.cp = jc.getConstantPool();
116 this.cplen = cp.getLength();
117
118 this.CONST_Class = ConstantClass.class;
119
120
121
122
123 this.CONST_String = ConstantString.class;
124 this.CONST_Integer = ConstantInteger.class;
125 this.CONST_Float = ConstantFloat.class;
126 this.CONST_Long = ConstantLong.class;
127 this.CONST_Double = ConstantDouble.class;
128 this.CONST_NameAndType = ConstantNameAndType.class;
129 this.CONST_Utf8 = ConstantUtf8.class;
130
131 this.carrier = new DescendingVisitor(jc, this);
132 this.carrier.visit();
133 }
134
135 private void checkIndex(final Node referrer, final int index, final Class<?> shouldbe) {
136 if (index < 0 || index >= cplen) {
137 throw new ClassConstraintException("Invalid index '" + index + "' used by '" + tostring(referrer) + "'.");
138 }
139 final Constant c = cp.getConstant(index);
140 if (!shouldbe.isInstance(c)) {
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612 } catch (final ClassFormatException cfe) {
613 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
614 }
615
616 final String nameanddesc = name + sig;
617 if (fieldNamesAndDesc.contains(nameanddesc)) {
618 throw new ClassConstraintException("No two fields (like '" + tostring(obj) + "') are allowed have same names and descriptors!");
619 }
620 if (fieldNames.contains(name)) {
621 addMessage("More than one field of name '" + name + "' detected (but with different type descriptors). This is very unusual.");
622 }
623 fieldNamesAndDesc.add(nameanddesc);
624 fieldNames.add(name);
625
626 final Attribute[] atts = obj.getAttributes();
627 for (final Attribute att : atts) {
628 if (!(att instanceof ConstantValue) && !(att instanceof Synthetic) && !(att instanceof Deprecated)) {
629 addMessage("Attribute '" + tostring(att) + "' as an attribute of Field '" + tostring(obj) + "' is unknown and will therefore be ignored.");
630 }
631 if (!(att instanceof ConstantValue)) {
632 addMessage("Attribute '" + tostring(att) + "' as an attribute of Field '" + tostring(obj)
633 + "' is not a ConstantValue and is therefore only of use for debuggers and such.");
634 }
635 }
636 }
637
638 @Override
639 public void visitInnerClass(final InnerClass obj) {
640
641
642 }
643
644 @Override
645 public void visitInnerClasses(final InnerClasses innerClasses) {
646
647
648
649 checkIndex(innerClasses, innerClasses.getNameIndex(), CONST_Utf8);
650
651 final String name = ((ConstantUtf8) cp.getConstant(innerClasses.getNameIndex())).getBytes();
652 if (!name.equals("InnerClasses")) {
653 throw new ClassConstraintException(
654 "The InnerClasses attribute '" + tostring(innerClasses) + "' is not correctly named 'InnerClasses' but '" + name + "'.");
655 }
656
657 innerClasses.forEach(ic -> {
658 checkIndex(innerClasses, ic.getInnerClassIndex(), CONST_Class);
659 final int outerIdx = ic.getOuterClassIndex();
660 if (outerIdx != 0) {
661 checkIndex(innerClasses, outerIdx, CONST_Class);
662 }
663 final int innernameIdx = ic.getInnerNameIndex();
664 if (innernameIdx != 0) {
665 checkIndex(innerClasses, innernameIdx, CONST_Utf8);
666 }
667 int acc = ic.getInnerAccessFlags();
668 acc &= ~(Const.ACC_PUBLIC | Const.ACC_PRIVATE | Const.ACC_PROTECTED | Const.ACC_STATIC | Const.ACC_FINAL | Const.ACC_INTERFACE |
669 Const.ACC_ABSTRACT);
670 if (acc != 0) {
671 addMessage("Unknown access flag for inner class '" + tostring(ic) + "' set (InnerClasses attribute '" + tostring(innerClasses) + "').");
672 }
673 });
674
675
676 }
677
678
679
680
681 @Override
682 public void visitJavaClass(final JavaClass obj) {
683 final Attribute[] atts = obj.getAttributes();
684 boolean foundSourceFile = false;
685 boolean foundInnerClasses = false;
686
687
688
689 final boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
690
691 for (final Attribute att : atts) {
692 if (!(att instanceof SourceFile) && !(att instanceof Deprecated) && !(att instanceof InnerClasses) && !(att instanceof Synthetic)) {
693 addMessage("Attribute '" + tostring(att) + "' as an attribute of the ClassFile structure '" + tostring(obj)
694 + "' is unknown and will therefore be ignored.");
695 }
696
697 if (att instanceof SourceFile) {
698 if (foundSourceFile) {
699 throw new ClassConstraintException(
700 "A ClassFile structure (like '" + tostring(obj) + "') may have no more than one SourceFile attribute.");
701 }
702 foundSourceFile = true;
703 }
704
705 if (att instanceof InnerClasses) {
706 if (!foundInnerClasses) {
707 foundInnerClasses = true;
708 } else if (hasInnerClass) {
709 throw new ClassConstraintException("A Classfile structure (like '" + tostring(obj) + "') must have exactly one InnerClasses attribute"
710 + " if at least one Inner Class is referenced (which is the case)." + " More than one InnerClasses attribute was found.");
711 }
712 if (!hasInnerClass) {
713 addMessage("No referenced Inner Class found, but InnerClasses attribute '" + tostring(att)
714 + "' found. Strongly suggest removal of that attribute.");
715 }
716 }
717
718 }
719 if (hasInnerClass && !foundInnerClasses) {
720
721
722
723
724
725
726 addMessage("A Classfile structure (like '" + tostring(obj)
727 + "') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case)."
728 + " No InnerClasses attribute was found.");
729 }
730 }
731
732 @Override
733 public void visitLineNumber(final LineNumber obj) {
734
735
736
737
738 }
739
740
741
742
743
744
745 @Override
746 public void visitLineNumberTable(final LineNumberTable obj) {
747 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
748
749 final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
750 if (!name.equals("LineNumberTable")) {
751 throw new ClassConstraintException(
752 "The LineNumberTable attribute '" + tostring(obj) + "' is not correctly named 'LineNumberTable' but '" + name + "'.");
753 }
754
755
756
757
758
759 }
760
761
762
763
764 @Override
765 public void visitLocalVariable(final LocalVariable obj) {
766
767
768
769
770 }
771
772 @Override
773 public void visitLocalVariableTable(final LocalVariableTable obj) {
774
775
776 }
777
778
779
780
781 @Override
782 public void visitMethod(final Method obj) {
783
784 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
785
786 final String name = obj.getName();
787 if (!validMethodName(name, true)) {
788 throw new ClassConstraintException("Method '" + tostring(obj) + "' has illegal name '" + name + "'.");
789 }
790
791
792 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
793
794 final String sig = ((ConstantUtf8) cp.getConstant(obj.getSignatureIndex())).getBytes();
795
796 Type t;
797 Type[] ts;
798 try {
799 t = Type.getReturnType(sig);
800 ts = Type.getArgumentTypes(sig);
801 } catch (final ClassFormatException cfe) {
802 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by Method '" + tostring(obj) + "'.", cfe);
803 }
804
805
806 Type act = t;
807 if (act instanceof ArrayType) {
808 act = ((ArrayType) act).getBasicType();
809 }
810 if (act instanceof ObjectType) {
811 final Verifier v = VerifierFactory.getVerifier(((ObjectType) act).getClassName());
812 final VerificationResult vr = v.doPass1();
813 if (vr != VerificationResult.VR_OK) {
814 throw new ClassConstraintException(
815 "Method '" + tostring(obj) + "' has a return type that does not pass verification pass 1: '" + vr + "'.");
816 }
817 }
818
819 for (final Type element : ts) {
820 act = element;
821 if (act instanceof ArrayType) {
822 act = ((ArrayType) act).getBasicType();
823 }
824 if (act instanceof ObjectType) {
825 final Verifier v = VerifierFactory.getVerifier(((ObjectType) act).getClassName());
826 final VerificationResult vr = v.doPass1();
827 if (vr != VerificationResult.VR_OK) {
828 throw new ClassConstraintException(
829 "Method '" + tostring(obj) + "' has an argument type that does not pass verification pass 1: '" + vr + "'.");
830 }
831 }
832 }
833
834
835
836 if (name.equals(Const.STATIC_INITIALIZER_NAME) && ts.length != 0) {
837 throw new ClassConstraintException("Method '" + tostring(obj) + "' has illegal name '" + name + "'."
838 + " Its name resembles the class or interface initialization method" + " which it isn't because of its arguments (==descriptor).");
839 }
840
841 if (jc.isClass()) {
842 int maxone = 0;
843 if (obj.isPrivate()) {
844 maxone++;
845 }
846 if (obj.isProtected()) {
847 maxone++;
848 }
849 if (obj.isPublic()) {
850 maxone++;
851 }
852 if (maxone > 1) {
853 throw new ClassConstraintException(
854 "Method '" + tostring(obj) + "' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
855 }
856
857 if (obj.isAbstract()) {
858 if (obj.isFinal()) {
859 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_FINAL modifier set.");
860 }
861 if (obj.isNative()) {
862 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_NATIVE modifier set.");
863 }
864 if (obj.isPrivate()) {
865 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_PRIVATE modifier set.");
866 }
867 if (obj.isStatic()) {
868 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_STATIC modifier set.");
869 }
870 if (obj.isStrictfp()) {
871 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_STRICT modifier set.");
872 }
873 if (obj.isSynchronized()) {
874 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_SYNCHRONIZED modifier set.");
875 }
876 }
877
878
879
880
881 if (name.equals(Const.CONSTRUCTOR_NAME) && (obj.isStatic() || obj.isFinal() || obj.isSynchronized() || obj.isNative() || obj.isAbstract())) {
882 throw new ClassConstraintException("Instance initialization method '" + tostring(obj) + "' must not have"
883 + " any of the ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT modifiers set.");
884 }
885 } else if (!name.equals(Const.STATIC_INITIALIZER_NAME)) {
886 if (jc.getMajor() >= Const.MAJOR_1_8) {
887 if (obj.isPublic() == obj.isPrivate()) {
888 throw new ClassConstraintException(
889 "Interface method '" + tostring(obj) + "' must have" + " exactly one of its ACC_PUBLIC and ACC_PRIVATE modifiers set.");
890 }
891 if (obj.isProtected() || obj.isFinal() || obj.isSynchronized() || obj.isNative()) {
892 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must not have"
893 + " any of the ACC_PROTECTED, ACC_FINAL, ACC_SYNCHRONIZED, or ACC_NATIVE modifiers set.");
894 }
895
896 } else {
897 if (!obj.isPublic()) {
898 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must have the ACC_PUBLIC modifier set but hasn't!");
899 }
900 if (!obj.isAbstract()) {
901 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must have the ACC_ABSTRACT modifier set but hasn't!");
902 }
903 if (obj.isPrivate() || obj.isProtected() || obj.isStatic() || obj.isFinal() || obj.isSynchronized() || obj.isNative() || obj.isStrictfp()) {
904 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must not have"
905 + " any of the ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,"
906 + " ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT modifiers set.");
907 }
908 }
909 }
910
911 if ((obj.getAccessFlags() & ~(Const.ACC_PUBLIC | Const.ACC_PRIVATE | Const.ACC_PROTECTED | Const.ACC_STATIC | Const.ACC_FINAL |
912 Const.ACC_SYNCHRONIZED | Const.ACC_NATIVE | Const.ACC_ABSTRACT | Const.ACC_STRICT)) > 0) {
913 addMessage("Method '" + tostring(obj) + "' has access flag(s) other than" + " ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,"
914 + " ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT set (ignored).");
915 }
916
917 final String nameanddesc = name + sig;
918 if (methodNamesAndDesc.contains(nameanddesc)) {
919 throw new ClassConstraintException("No two methods (like '" + tostring(obj) + "') are allowed have same names and desciptors!");
920 }
921 methodNamesAndDesc.add(nameanddesc);
922
923 final Attribute[] atts = obj.getAttributes();
924 int numCodeAtts = 0;
925 for (final Attribute att : atts) {
926 if (!(att instanceof Code) && !(att instanceof ExceptionTable) && !(att instanceof Synthetic) && !(att instanceof Deprecated)) {
927 addMessage("Attribute '" + tostring(att) + "' as an attribute of Method '" + tostring(obj) + "' is unknown and will therefore be ignored.");
928 }
929 if (!(att instanceof Code) && !(att instanceof ExceptionTable)) {
930 addMessage("Attribute '" + tostring(att) + "' as an attribute of Method '" + tostring(obj)
931 + "' is neither Code nor Exceptions and is therefore only of use for debuggers and such.");
932 }
933 if (att instanceof Code && (obj.isNative() || obj.isAbstract())) {
934 throw new ClassConstraintException(
935 "Native or abstract methods like '" + tostring(obj) + "' must not have a Code attribute like '" + tostring(att) + "'.");
936
937
938 }
939 if (att instanceof Code) {
940 numCodeAtts++;
941 }
942 }
943 if (!obj.isNative() && !obj.isAbstract() && numCodeAtts != 1) {
944 throw new ClassConstraintException(
945 "Non-native, non-abstract methods like '" + tostring(obj) + "' must have exactly one Code attribute (found: " + numCodeAtts + ").");
946 }
947 }
948
949
950
951
952 @Override
953 public void visitSourceFile(final SourceFile obj) {
954
955
956
957 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
958
959 final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
960 if (!name.equals("SourceFile")) {
961 throw new ClassConstraintException("The SourceFile attribute '" + tostring(obj) + "' is not correctly named 'SourceFile' but '" + name + "'.");
962 }
963
964 checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
965
966 final String sourceFileName = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes();
967 final String sourceFileNameLc = sourceFileName.toLowerCase(Locale.ENGLISH);
968
969 if (sourceFileName.indexOf('/') != -1 || sourceFileName.indexOf('\\') != -1 || sourceFileName.indexOf(':') != -1
970 || sourceFileNameLc.lastIndexOf(".java") == -1) {
971 addMessage("SourceFile attribute '" + tostring(obj)
972 + "' has a funny name: remember not to confuse certain parsers working on javap's output. Also, this name ('" + sourceFileName
973 + "') is considered an unqualified (simple) file name only.");
974 }
975 }
976
977 @Override
978 public void visitSynthetic(final Synthetic obj) {
979 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
980 final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
981 if (!name.equals("Synthetic")) {
982 throw new ClassConstraintException("The Synthetic attribute '" + tostring(obj) + "' is not correctly named 'Synthetic' but '" + name + "'.");
983 }
984 }
985
986
987
988
989 @Override
990 public void visitUnknown(final Unknown obj) {
991
992 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
993
994
995 addMessage("Unknown attribute '" + tostring(obj) + "'. This attribute is not known in any context!");
996 }
997 }
998
999
1000
1001
1002
1003
1004
1005
1006 private final class FAMRAV_Visitor extends EmptyVisitor {
1007 private final ConstantPool cp;
1008
1009 private FAMRAV_Visitor(final JavaClass jc) {
1010 this.cp = jc.getConstantPool();
1011 }
1012
1013 @Override
1014 public void visitConstantFieldref(final ConstantFieldref obj) {
1015 if (obj.getTag() != Const.CONSTANT_Fieldref) {
1016 throw new ClassConstraintException("ConstantFieldref '" + tostring(obj) + "' has wrong tag!");
1017 }
1018 final int nameAndTypeIndex = obj.getNameAndTypeIndex();
1019 final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(nameAndTypeIndex);
1020 final String name = ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes();
1021 if (!validFieldName(name)) {
1022 throw new ClassConstraintException("Invalid field name '" + name + "' referenced by '" + tostring(obj) + "'.");
1023 }
1024
1025 final int classIndex = obj.getClassIndex();
1026 final ConstantClass cc = (ConstantClass) cp.getConstant(classIndex);
1027 final String className = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes();
1028 if (!validClassName(className)) {
1029 throw new ClassConstraintException("Illegal class name '" + className + "' used by '" + tostring(obj) + "'.");
1030 }
1031
1032 final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
1033
1034 try {
1035 Type.getType(sig);
1036 } catch (final ClassFormatException cfe) {
1037 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
1038 }
1039 }
1040
1041 @Override
1042 public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) {
1043 if (obj.getTag() != Const.CONSTANT_InterfaceMethodref) {
1044 throw new ClassConstraintException("ConstantInterfaceMethodref '" + tostring(obj) + "' has wrong tag!");
1045 }
1046 final int nameAndTypeIndex = obj.getNameAndTypeIndex();
1047 final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(nameAndTypeIndex);
1048 final String name = ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes();
1049 if (!validInterfaceMethodName(name)) {
1050 throw new ClassConstraintException("Invalid (interface) method name '" + name + "' referenced by '" + tostring(obj) + "'.");
1051 }
1052
1053 final int classIndex = obj.getClassIndex();
1054 final ConstantClass cc = (ConstantClass) cp.getConstant(classIndex);
1055 final String className = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes();
1056 if (!validClassName(className)) {
1057 throw new ClassConstraintException("Illegal class name '" + className + "' used by '" + tostring(obj) + "'.");
1058 }
1059
1060 final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
1061
1062 try {
1063 final Type t = Type.getReturnType(sig);
1064 if (name.equals(Const.STATIC_INITIALIZER_NAME) && t != Type.VOID) {
1065 addMessage("Class or interface initialization method '" + Const.STATIC_INITIALIZER_NAME + "' usually has VOID return type instead of '" + t
1066 + "'. Note this is really not a requirement of The Java Virtual Machine Specification, Second Edition.");
1067 }
1068 } catch (final ClassFormatException cfe) {
1069 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
1070 }
1071
1072 }
1073
1074 @Override
1075 public void visitConstantMethodref(final ConstantMethodref obj) {
1076 if (obj.getTag() != Const.CONSTANT_Methodref) {
1077 throw new ClassConstraintException("ConstantMethodref '" + tostring(obj) + "' has wrong tag!");
1078 }
1079 final int nameAndTypeIndex = obj.getNameAndTypeIndex();
1080 final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(nameAndTypeIndex);
1081 final String name = ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes();
1082 if (!validClassMethodName(name)) {
1083 throw new ClassConstraintException("Invalid (non-interface) method name '" + name + "' referenced by '" + tostring(obj) + "'.");
1084 }
1085
1086 final int classIndex = obj.getClassIndex();
1087 final ConstantClass cc = (ConstantClass) cp.getConstant(classIndex);
1088 final String className = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes();
1089 if (!validClassName(className)) {
1090 throw new ClassConstraintException("Illegal class name '" + className + "' used by '" + tostring(obj) + "'.");
1091 }
1092
1093 final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
1094
1095 try {
1096 final Type t = Type.getReturnType(sig);
1097 if (name.equals(Const.CONSTRUCTOR_NAME) && t != Type.VOID) {
1098 throw new ClassConstraintException("Instance initialization method must have VOID return type.");
1099 }
1100 } catch (final ClassFormatException cfe) {
1101 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
1102 }
1103 }
1104
1105 }
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120 private static final class InnerClassDetector extends EmptyVisitor {
1121 private boolean hasInnerClass;
1122 private final JavaClass jc;
1123 private final ConstantPool cp;
1124
1125
1126 public InnerClassDetector(final JavaClass javaClass) {
1127 this.jc = javaClass;
1128 this.cp = jc.getConstantPool();
1129 new DescendingVisitor(jc, this).visit();
1130 }
1131
1132
1133
1134
1135
1136
1137 public boolean innerClassReferenced() {
1138 return hasInnerClass;
1139 }
1140
1141
1142 @Override
1143 public void visitConstantClass(final ConstantClass obj) {
1144 final Constant c = cp.getConstant(obj.getNameIndex());
1145 if (c instanceof ConstantUtf8) {
1146 final String className = ((ConstantUtf8) c).getBytes();
1147 if (className.startsWith(Utility.packageToPath(jc.getClassName()) + "$")) {
1148 hasInnerClass = true;
1149 }
1150 }
1151 }
1152 }
1153
1154
1155
1156
1157 private static String tostring(final Node n) {
1158 return new StringRepresentation(n).toString();
1159 }
1160
1161
1162
1163
1164
1165 private static boolean validClassMethodName(final String name) {
1166 return validMethodName(name, false);
1167 }
1168
1169
1170
1171
1172 private static boolean validClassName(final String name) {
1173
1174
1175
1176 Objects.requireNonNull(name, "name");
1177 return true;
1178 }
1179
1180
1181
1182
1183 private static boolean validFieldName(final String name) {
1184
1185 return validJavaIdentifier(name);
1186 }
1187
1188
1189
1190
1191
1192 private static boolean validInterfaceMethodName(final String name) {
1193
1194 if (name.startsWith("<")) {
1195 return false;
1196 }
1197 return validJavaLangMethodName(name);
1198 }
1199
1200
1201
1202
1203
1204 private static boolean validJavaIdentifier(final String name) {
1205
1206 if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) {
1207 return false;
1208 }
1209
1210 for (int i = 1; i < name.length(); i++) {
1211 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1212 return false;
1213 }
1214 }
1215 return true;
1216 }
1217
1218
1219
1220
1221
1222
1223 private static boolean validJavaLangMethodName(final String name) {
1224 return validJavaIdentifier(name);
1225 }
1226
1227
1228
1229
1230
1231
1232 private static boolean validMethodName(final String name, final boolean allowStaticInit) {
1233 if (validJavaLangMethodName(name)) {
1234 return true;
1235 }
1236
1237 if (allowStaticInit) {
1238 return name.equals(Const.CONSTRUCTOR_NAME) || name.equals(Const.STATIC_INITIALIZER_NAME);
1239 }
1240 return name.equals(Const.CONSTRUCTOR_NAME);
1241 }
1242
1243
1244
1245
1246
1247 private LocalVariablesInfo[] localVariablesInfos;
1248
1249 private final Verifier verifier;
1250
1251
1252
1253
1254
1255
1256 public Pass2Verifier(final Verifier verifier) {
1257 this.verifier = verifier;
1258 }
1259
1260
1261
1262
1263
1264
1265
1266 private void constantPoolEntriesSatisfyStaticConstraints() {
1267 try {
1268
1269
1270
1271 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
1272 new CPESSC_Visitor(jc);
1273
1274 } catch (final ClassNotFoundException e) {
1275
1276 throw new AssertionViolatedException("Missing class: " + e, e);
1277 }
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293 @Override
1294 public VerificationResult do_verify() {
1295 try {
1296 final VerificationResult vr1 = verifier.doPass1();
1297 if (vr1.equals(VerificationResult.VR_OK)) {
1298
1299
1300
1301 localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(verifier.getClassName()).getMethods().length];
1302
1303 VerificationResult vr = VerificationResult.VR_OK;
1304 try {
1305 constantPoolEntriesSatisfyStaticConstraints();
1306 fieldAndMethodRefsAreValid();
1307 everyClassHasAnAccessibleSuperclass();
1308 finalMethodsAreNotOverridden();
1309 } catch (final ClassConstraintException cce) {
1310 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
1311 }
1312 return vr;
1313 }
1314 return VerificationResult.VR_NOTYET;
1315
1316 } catch (final ClassNotFoundException e) {
1317
1318 throw new AssertionViolatedException("Missing class: " + e, e);
1319 }
1320 }
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330 private void everyClassHasAnAccessibleSuperclass() {
1331 try {
1332 final Set<String> hs = new HashSet<>();
1333 JavaClass jc = Repository.lookupClass(verifier.getClassName());
1334 int supidx = -1;
1335
1336 while (supidx != 0) {
1337 supidx = jc.getSuperclassNameIndex();
1338
1339 if (supidx == 0) {
1340 if (jc != Repository.lookupClass(Type.OBJECT.getClassName())) {
1341 throw new ClassConstraintException(
1342 "Superclass of '" + jc.getClassName() + "' missing but not " + Type.OBJECT.getClassName() + " itself!");
1343 }
1344 } else {
1345 final String supername = jc.getSuperclassName();
1346 if (!hs.add(supername)) {
1347 throw new ClassConstraintException("Circular superclass hierarchy detected.");
1348 }
1349 final Verifier v = VerifierFactory.getVerifier(supername);
1350 final VerificationResult vr = v.doPass1();
1351
1352 if (vr != VerificationResult.VR_OK) {
1353 throw new ClassConstraintException("Could not load in ancestor class '" + supername + "'.");
1354 }
1355 jc = Repository.lookupClass(supername);
1356
1357 if (jc.isFinal()) {
1358 throw new ClassConstraintException(
1359 "Ancestor class '" + supername + "' has the FINAL access modifier and must therefore not be subclassed.");
1360 }
1361 }
1362 }
1363
1364 } catch (final ClassNotFoundException e) {
1365
1366 throw new AssertionViolatedException("Missing class: " + e, e);
1367 }
1368 }
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380 private void fieldAndMethodRefsAreValid() {
1381 try {
1382 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
1383 final DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
1384 v.visit();
1385
1386 } catch (final ClassNotFoundException e) {
1387
1388 throw new AssertionViolatedException("Missing class: " + e, e);
1389 }
1390 }
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401 private void finalMethodsAreNotOverridden() {
1402 try {
1403 final Map<String, String> map = new HashMap<>();
1404 JavaClass jc = Repository.lookupClass(verifier.getClassName());
1405
1406 int supidx = -1;
1407 while (supidx != 0) {
1408 supidx = jc.getSuperclassNameIndex();
1409
1410 final Method[] methods = jc.getMethods();
1411 for (final Method method : methods) {
1412 final String nameAndSig = method.getName() + method.getSignature();
1413
1414 if (map.containsKey(nameAndSig) && method.isFinal()) {
1415 if (!method.isPrivate()) {
1416 throw new ClassConstraintException("Method '" + nameAndSig + "' in class '" + map.get(nameAndSig)
1417 + "' overrides the final (not-overridable) definition in class '" + jc.getClassName() + "'.");
1418 }
1419 addMessage("Method '" + nameAndSig + "' in class '" + map.get(nameAndSig)
1420 + "' overrides the final (not-overridable) definition in class '" + jc.getClassName()
1421 + "'. This is okay, as the original definition was private; however this constraint leverage"
1422 + " was introduced by JLS 8.4.6 (not vmspec2) and the behavior of the Sun verifiers.");
1423 } else if (!method.isStatic()) {
1424 map.put(nameAndSig, jc.getClassName());
1425 }
1426 }
1427
1428 jc = Repository.lookupClass(jc.getSuperclassName());
1429
1430 }
1431
1432 } catch (final ClassNotFoundException e) {
1433
1434 throw new AssertionViolatedException("Missing class: " + e, e);
1435 }
1436
1437 }
1438
1439
1440
1441
1442
1443
1444
1445
1446 public LocalVariablesInfo getLocalVariablesInfo(final int methodNr) {
1447 if (verify() != VerificationResult.VR_OK) {
1448 return null;
1449 }
1450 if (methodNr < 0 || methodNr >= localVariablesInfos.length) {
1451 throw new AssertionViolatedException("Method number out of range.");
1452 }
1453 return localVariablesInfos[methodNr];
1454 }
1455 }