1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.verifier.statics;
20
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Set;
26
27 import org.apache.bcel.Const;
28 import org.apache.bcel.Constants;
29 import org.apache.bcel.Repository;
30 import org.apache.bcel.classfile.Attribute;
31 import org.apache.bcel.classfile.ClassFormatException;
32 import org.apache.bcel.classfile.Code;
33 import org.apache.bcel.classfile.CodeException;
34 import org.apache.bcel.classfile.Constant;
35 import org.apache.bcel.classfile.ConstantClass;
36 import org.apache.bcel.classfile.ConstantDouble;
37 import org.apache.bcel.classfile.ConstantFieldref;
38 import org.apache.bcel.classfile.ConstantFloat;
39 import org.apache.bcel.classfile.ConstantInteger;
40 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
41 import org.apache.bcel.classfile.ConstantLong;
42 import org.apache.bcel.classfile.ConstantMethodref;
43 import org.apache.bcel.classfile.ConstantNameAndType;
44 import org.apache.bcel.classfile.ConstantPool;
45 import org.apache.bcel.classfile.ConstantString;
46 import org.apache.bcel.classfile.ConstantUtf8;
47 import org.apache.bcel.classfile.ConstantValue;
48 import org.apache.bcel.classfile.Deprecated;
49 import org.apache.bcel.classfile.DescendingVisitor;
50 import org.apache.bcel.classfile.EmptyVisitor;
51 import org.apache.bcel.classfile.ExceptionTable;
52 import org.apache.bcel.classfile.Field;
53 import org.apache.bcel.classfile.InnerClass;
54 import org.apache.bcel.classfile.InnerClasses;
55 import org.apache.bcel.classfile.JavaClass;
56 import org.apache.bcel.classfile.LineNumber;
57 import org.apache.bcel.classfile.LineNumberTable;
58 import org.apache.bcel.classfile.LocalVariable;
59 import org.apache.bcel.classfile.LocalVariableTable;
60 import org.apache.bcel.classfile.Method;
61 import org.apache.bcel.classfile.Node;
62 import org.apache.bcel.classfile.SourceFile;
63 import org.apache.bcel.classfile.Synthetic;
64 import org.apache.bcel.classfile.Unknown;
65 import org.apache.bcel.classfile.Utility;
66 import org.apache.bcel.generic.ArrayType;
67 import org.apache.bcel.generic.ObjectType;
68 import org.apache.bcel.generic.Type;
69 import org.apache.bcel.verifier.PassVerifier;
70 import org.apache.bcel.verifier.VerificationResult;
71 import org.apache.bcel.verifier.Verifier;
72 import org.apache.bcel.verifier.VerifierFactory;
73 import org.apache.bcel.verifier.exc.AssertionViolatedException;
74 import org.apache.bcel.verifier.exc.ClassConstraintException;
75 import org.apache.bcel.verifier.exc.LocalVariableInfoInconsistentException;
76 import org.apache.commons.lang3.StringUtils;
77
78
79
80
81
82
83
84 public final class Pass2Verifier extends PassVerifier implements Constants {
85
86
87
88
89
90
91
92 private final class CPESSC_Visitor extends EmptyVisitor {
93 private final Class<?> CONST_Class;
94
95
96
97
98 private final Class<?> CONST_String;
99 private final Class<?> CONST_Integer;
100 private final Class<?> CONST_Float;
101 private final Class<?> CONST_Long;
102 private final Class<?> CONST_Double;
103 private final Class<?> CONST_NameAndType;
104 private final Class<?> CONST_Utf8;
105
106 private final JavaClass jc;
107 private final ConstantPool cp;
108 private final int cplen;
109 private final DescendingVisitor carrier;
110
111 private final Set<String> fieldNames = new HashSet<>();
112 private final Set<String> fieldNamesAndDesc = new HashSet<>();
113 private final Set<String> methodNamesAndDesc = new HashSet<>();
114
115 private CPESSC_Visitor(final JavaClass jc) {
116 this.jc = jc;
117 this.cp = jc.getConstantPool();
118 this.cplen = cp.getLength();
119
120 this.CONST_Class = ConstantClass.class;
121
122
123
124
125 this.CONST_String = ConstantString.class;
126 this.CONST_Integer = ConstantInteger.class;
127 this.CONST_Float = ConstantFloat.class;
128 this.CONST_Long = ConstantLong.class;
129 this.CONST_Double = ConstantDouble.class;
130 this.CONST_NameAndType = ConstantNameAndType.class;
131 this.CONST_Utf8 = ConstantUtf8.class;
132
133 this.carrier = new DescendingVisitor(jc, this);
134 this.carrier.visit();
135 }
136
137 private void checkIndex(final Node referrer, final int index, final Class<?> shouldbe) {
138 if (index < 0 || index >= cplen) {
139 throw new ClassConstraintException("Invalid index '" + index + "' used by '" + tostring(referrer) + "'.");
140 }
141 final Constant c = cp.getConstant(index);
142 if (!shouldbe.isInstance(c)) {
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
613
614 } catch (final ClassFormatException cfe) {
615 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
616 }
617
618 final String nameanddesc = name + sig;
619 if (fieldNamesAndDesc.contains(nameanddesc)) {
620 throw new ClassConstraintException("No two fields (like '" + tostring(obj) + "') are allowed have same names and descriptors!");
621 }
622 if (fieldNames.contains(name)) {
623 addMessage("More than one field of name '" + name + "' detected (but with different type descriptors). This is very unusual.");
624 }
625 fieldNamesAndDesc.add(nameanddesc);
626 fieldNames.add(name);
627
628 final Attribute[] atts = obj.getAttributes();
629 for (final Attribute att : atts) {
630 if (!(att instanceof ConstantValue) && !(att instanceof Synthetic) && !(att instanceof Deprecated)) {
631 addMessage("Attribute '" + tostring(att) + "' as an attribute of Field '" + tostring(obj) + "' is unknown and will therefore be ignored.");
632 }
633 if (!(att instanceof ConstantValue)) {
634 addMessage("Attribute '" + tostring(att) + "' as an attribute of Field '" + tostring(obj)
635 + "' is not a ConstantValue and is therefore only of use for debuggers and such.");
636 }
637 }
638 }
639
640 @Override
641 public void visitInnerClass(final InnerClass obj) {
642
643
644 }
645
646 @Override
647 public void visitInnerClasses(final InnerClasses innerClasses) {
648
649
650
651 checkIndex(innerClasses, innerClasses.getNameIndex(), CONST_Utf8);
652
653 final String name = ((ConstantUtf8) cp.getConstant(innerClasses.getNameIndex())).getBytes();
654 if (!name.equals("InnerClasses")) {
655 throw new ClassConstraintException(
656 "The InnerClasses attribute '" + tostring(innerClasses) + "' is not correctly named 'InnerClasses' but '" + name + "'.");
657 }
658
659 innerClasses.forEach(ic -> {
660 checkIndex(innerClasses, ic.getInnerClassIndex(), CONST_Class);
661 final int outerIdx = ic.getOuterClassIndex();
662 if (outerIdx != 0) {
663 checkIndex(innerClasses, outerIdx, CONST_Class);
664 }
665 final int innernameIdx = ic.getInnerNameIndex();
666 if (innernameIdx != 0) {
667 checkIndex(innerClasses, innernameIdx, CONST_Utf8);
668 }
669 int acc = ic.getInnerAccessFlags();
670 acc &= ~(Const.ACC_PUBLIC | Const.ACC_PRIVATE | Const.ACC_PROTECTED | Const.ACC_STATIC | Const.ACC_FINAL | Const.ACC_INTERFACE |
671 Const.ACC_ABSTRACT);
672 if (acc != 0) {
673 addMessage("Unknown access flag for inner class '" + tostring(ic) + "' set (InnerClasses attribute '" + tostring(innerClasses) + "').");
674 }
675 });
676
677
678 }
679
680
681
682
683 @Override
684 public void visitJavaClass(final JavaClass obj) {
685 final Attribute[] atts = obj.getAttributes();
686 boolean foundSourceFile = false;
687 boolean foundInnerClasses = false;
688
689
690
691 final boolean hasInnerClass = new InnerClassDetector(jc).innerClassReferenced();
692
693 for (final Attribute att : atts) {
694 if (!(att instanceof SourceFile) && !(att instanceof Deprecated) && !(att instanceof InnerClasses) && !(att instanceof Synthetic)) {
695 addMessage("Attribute '" + tostring(att) + "' as an attribute of the ClassFile structure '" + tostring(obj)
696 + "' is unknown and will therefore be ignored.");
697 }
698
699 if (att instanceof SourceFile) {
700 if (foundSourceFile) {
701 throw new ClassConstraintException(
702 "A ClassFile structure (like '" + tostring(obj) + "') may have no more than one SourceFile attribute.");
703 }
704 foundSourceFile = true;
705 }
706
707 if (att instanceof InnerClasses) {
708 if (!foundInnerClasses) {
709 foundInnerClasses = true;
710 } else if (hasInnerClass) {
711 throw new ClassConstraintException("A Classfile structure (like '" + tostring(obj) + "') must have exactly one InnerClasses attribute"
712 + " if at least one Inner Class is referenced (which is the case). More than one InnerClasses attribute was found.");
713 }
714 if (!hasInnerClass) {
715 addMessage("No referenced Inner Class found, but InnerClasses attribute '" + tostring(att)
716 + "' found. Strongly suggest removal of that attribute.");
717 }
718 }
719
720 }
721 if (hasInnerClass && !foundInnerClasses) {
722
723
724
725
726
727
728 addMessage("A Classfile structure (like '" + tostring(obj)
729 + "') must have exactly one InnerClasses attribute if at least one Inner Class is referenced (which is the case)."
730 + " No InnerClasses attribute was found.");
731 }
732 }
733
734 @Override
735 public void visitLineNumber(final LineNumber obj) {
736
737
738
739
740 }
741
742
743
744
745
746
747 @Override
748 public void visitLineNumberTable(final LineNumberTable obj) {
749 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
750
751 final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
752 if (!name.equals("LineNumberTable")) {
753 throw new ClassConstraintException(
754 "The LineNumberTable attribute '" + tostring(obj) + "' is not correctly named 'LineNumberTable' but '" + name + "'.");
755 }
756
757
758
759
760
761 }
762
763
764
765
766 @Override
767 public void visitLocalVariable(final LocalVariable obj) {
768
769
770
771
772 }
773
774 @Override
775 public void visitLocalVariableTable(final LocalVariableTable obj) {
776
777
778 }
779
780
781
782
783 @Override
784 public void visitMethod(final Method obj) {
785
786 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
787
788 final String name = obj.getName();
789 if (!validMethodName(name, true)) {
790 throw new ClassConstraintException("Method '" + tostring(obj) + "' has illegal name '" + name + "'.");
791 }
792
793
794 checkIndex(obj, obj.getSignatureIndex(), CONST_Utf8);
795
796 final String sig = ((ConstantUtf8) cp.getConstant(obj.getSignatureIndex())).getBytes();
797
798 final Type t;
799 final Type[] ts;
800 try {
801 t = Type.getReturnType(sig);
802 ts = Type.getArgumentTypes(sig);
803 } catch (final ClassFormatException cfe) {
804 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by Method '" + tostring(obj) + "'.", cfe);
805 }
806
807
808 Type act = t;
809 if (act instanceof ArrayType) {
810 act = ((ArrayType) act).getBasicType();
811 }
812 if (act instanceof ObjectType) {
813 final Verifier v = VerifierFactory.getVerifier(((ObjectType) act).getClassName());
814 final VerificationResult vr = v.doPass1();
815 if (vr != VerificationResult.VR_OK) {
816 throw new ClassConstraintException(
817 "Method '" + tostring(obj) + "' has a return type that does not pass verification pass 1: '" + vr + "'.");
818 }
819 }
820
821 for (final Type element : ts) {
822 act = element;
823 if (act instanceof ArrayType) {
824 act = ((ArrayType) act).getBasicType();
825 }
826 if (act instanceof ObjectType) {
827 final Verifier v = VerifierFactory.getVerifier(((ObjectType) act).getClassName());
828 final VerificationResult vr = v.doPass1();
829 if (vr != VerificationResult.VR_OK) {
830 throw new ClassConstraintException(
831 "Method '" + tostring(obj) + "' has an argument type that does not pass verification pass 1: '" + vr + "'.");
832 }
833 }
834 }
835
836
837
838 if (name.equals(Const.STATIC_INITIALIZER_NAME) && ts.length != 0) {
839 throw new ClassConstraintException("Method '" + tostring(obj) + "' has illegal name '" + name + "'."
840 + " Its name resembles the class or interface initialization method which it isn't because of its arguments (==descriptor).");
841 }
842
843 if (jc.isClass()) {
844 int maxone = 0;
845 if (obj.isPrivate()) {
846 maxone++;
847 }
848 if (obj.isProtected()) {
849 maxone++;
850 }
851 if (obj.isPublic()) {
852 maxone++;
853 }
854 if (maxone > 1) {
855 throw new ClassConstraintException(
856 "Method '" + tostring(obj) + "' must only have at most one of its ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC modifiers set.");
857 }
858
859 if (obj.isAbstract()) {
860 if (obj.isFinal()) {
861 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_FINAL modifier set.");
862 }
863 if (obj.isNative()) {
864 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_NATIVE modifier set.");
865 }
866 if (obj.isPrivate()) {
867 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_PRIVATE modifier set.");
868 }
869 if (obj.isStatic()) {
870 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_STATIC modifier set.");
871 }
872 if (obj.isStrictfp()) {
873 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_STRICT modifier set.");
874 }
875 if (obj.isSynchronized()) {
876 throw new ClassConstraintException("Abstract method '" + tostring(obj) + "' must not have the ACC_SYNCHRONIZED modifier set.");
877 }
878 }
879
880
881
882
883 if (name.equals(Const.CONSTRUCTOR_NAME) && (obj.isStatic() || obj.isFinal() || obj.isSynchronized() || obj.isNative() || obj.isAbstract())) {
884 throw new ClassConstraintException("Instance initialization method '" + tostring(obj) + "' must not have"
885 + " any of the ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT modifiers set.");
886 }
887 } else if (!name.equals(Const.STATIC_INITIALIZER_NAME)) {
888 if (jc.getMajor() >= Const.MAJOR_1_8) {
889 if (obj.isPublic() == obj.isPrivate()) {
890 throw new ClassConstraintException(
891 "Interface method '" + tostring(obj) + "' must have exactly one of its ACC_PUBLIC and ACC_PRIVATE modifiers set.");
892 }
893 if (obj.isProtected() || obj.isFinal() || obj.isSynchronized() || obj.isNative()) {
894 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must not have"
895 + " any of the ACC_PROTECTED, ACC_FINAL, ACC_SYNCHRONIZED, or ACC_NATIVE modifiers set.");
896 }
897
898 } else {
899 if (!obj.isPublic()) {
900 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must have the ACC_PUBLIC modifier set but hasn't!");
901 }
902 if (!obj.isAbstract()) {
903 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must have the ACC_ABSTRACT modifier set but hasn't!");
904 }
905 if (obj.isPrivate() || obj.isProtected() || obj.isStatic() || obj.isFinal() || obj.isSynchronized() || obj.isNative() || obj.isStrictfp()) {
906 throw new ClassConstraintException("Interface method '" + tostring(obj) + "' must not have"
907 + " any of the ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,"
908 + " ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT modifiers set.");
909 }
910 }
911 }
912
913 if ((obj.getAccessFlags() & ~(Const.ACC_PUBLIC | Const.ACC_PRIVATE | Const.ACC_PROTECTED | Const.ACC_STATIC | Const.ACC_FINAL |
914 Const.ACC_SYNCHRONIZED | Const.ACC_NATIVE | Const.ACC_ABSTRACT | Const.ACC_STRICT)) > 0) {
915 addMessage("Method '" + tostring(obj) + "' has access flag(s) other than ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,"
916 + " ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT set (ignored).");
917 }
918
919 final String nameanddesc = name + sig;
920 if (methodNamesAndDesc.contains(nameanddesc)) {
921 throw new ClassConstraintException("No two methods (like '" + tostring(obj) + "') are allowed have same names and desciptors!");
922 }
923 methodNamesAndDesc.add(nameanddesc);
924
925 final Attribute[] atts = obj.getAttributes();
926 int numCodeAtts = 0;
927 for (final Attribute att : atts) {
928 if (!(att instanceof Code) && !(att instanceof ExceptionTable) && !(att instanceof Synthetic) && !(att instanceof Deprecated)) {
929 addMessage("Attribute '" + tostring(att) + "' as an attribute of Method '" + tostring(obj) + "' is unknown and will therefore be ignored.");
930 }
931 if (!(att instanceof Code) && !(att instanceof ExceptionTable)) {
932 addMessage("Attribute '" + tostring(att) + "' as an attribute of Method '" + tostring(obj)
933 + "' is neither Code nor Exceptions and is therefore only of use for debuggers and such.");
934 }
935 if (att instanceof Code && (obj.isNative() || obj.isAbstract())) {
936 throw new ClassConstraintException(
937 "Native or abstract methods like '" + tostring(obj) + "' must not have a Code attribute like '" + tostring(att) + "'.");
938
939
940 }
941 if (att instanceof Code) {
942 numCodeAtts++;
943 }
944 }
945 if (!obj.isNative() && !obj.isAbstract() && numCodeAtts != 1) {
946 throw new ClassConstraintException(
947 "Non-native, non-abstract methods like '" + tostring(obj) + "' must have exactly one Code attribute (found: " + numCodeAtts + ").");
948 }
949 }
950
951
952
953
954 @Override
955 public void visitSourceFile(final SourceFile obj) {
956
957
958
959 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
960
961 final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
962 if (!name.equals("SourceFile")) {
963 throw new ClassConstraintException("The SourceFile attribute '" + tostring(obj) + "' is not correctly named 'SourceFile' but '" + name + "'.");
964 }
965
966 checkIndex(obj, obj.getSourceFileIndex(), CONST_Utf8);
967
968 final String sourceFileName = ((ConstantUtf8) cp.getConstant(obj.getSourceFileIndex())).getBytes();
969 final String sourceFileNameLc = StringUtils.toRootLowerCase(sourceFileName);
970
971 if (sourceFileName.indexOf('/') != -1 || sourceFileName.indexOf('\\') != -1 || sourceFileName.indexOf(':') != -1
972 || sourceFileNameLc.lastIndexOf(".java") == -1) {
973 addMessage("SourceFile attribute '" + tostring(obj)
974 + "' has a funny name: remember not to confuse certain parsers working on javap's output. Also, this name ('" + sourceFileName
975 + "') is considered an unqualified (simple) file name only.");
976 }
977 }
978
979 @Override
980 public void visitSynthetic(final Synthetic obj) {
981 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
982 final String name = ((ConstantUtf8) cp.getConstant(obj.getNameIndex())).getBytes();
983 if (!name.equals("Synthetic")) {
984 throw new ClassConstraintException("The Synthetic attribute '" + tostring(obj) + "' is not correctly named 'Synthetic' but '" + name + "'.");
985 }
986 }
987
988
989
990
991 @Override
992 public void visitUnknown(final Unknown obj) {
993
994 checkIndex(obj, obj.getNameIndex(), CONST_Utf8);
995
996
997 addMessage("Unknown attribute '" + tostring(obj) + "'. This attribute is not known in any context!");
998 }
999 }
1000
1001
1002
1003
1004
1005
1006
1007
1008 private final class FAMRAV_Visitor extends EmptyVisitor {
1009 private final ConstantPool cp;
1010
1011 private FAMRAV_Visitor(final JavaClass jc) {
1012 this.cp = jc.getConstantPool();
1013 }
1014
1015 @Override
1016 public void visitConstantFieldref(final ConstantFieldref obj) {
1017 if (obj.getTag() != Const.CONSTANT_Fieldref) {
1018 throw new ClassConstraintException("ConstantFieldref '" + tostring(obj) + "' has wrong tag!");
1019 }
1020 final int nameAndTypeIndex = obj.getNameAndTypeIndex();
1021 final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(nameAndTypeIndex);
1022 final String name = ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes();
1023 if (!validFieldName(name)) {
1024 throw new ClassConstraintException("Invalid field name '" + name + "' referenced by '" + tostring(obj) + "'.");
1025 }
1026
1027 final int classIndex = obj.getClassIndex();
1028 final ConstantClass cc = (ConstantClass) cp.getConstant(classIndex);
1029 final String className = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes();
1030 if (!validClassName(className)) {
1031 throw new ClassConstraintException("Illegal class name '" + className + "' used by '" + tostring(obj) + "'.");
1032 }
1033
1034 final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
1035
1036 try {
1037 Type.getType(sig);
1038 } catch (final ClassFormatException cfe) {
1039 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
1040 }
1041 }
1042
1043 @Override
1044 public void visitConstantInterfaceMethodref(final ConstantInterfaceMethodref obj) {
1045 if (obj.getTag() != Const.CONSTANT_InterfaceMethodref) {
1046 throw new ClassConstraintException("ConstantInterfaceMethodref '" + tostring(obj) + "' has wrong tag!");
1047 }
1048 final int nameAndTypeIndex = obj.getNameAndTypeIndex();
1049 final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(nameAndTypeIndex);
1050 final String name = ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes();
1051 if (!validInterfaceMethodName(name)) {
1052 throw new ClassConstraintException("Invalid (interface) method name '" + name + "' referenced by '" + tostring(obj) + "'.");
1053 }
1054
1055 final int classIndex = obj.getClassIndex();
1056 final ConstantClass cc = (ConstantClass) cp.getConstant(classIndex);
1057 final String className = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes();
1058 if (!validClassName(className)) {
1059 throw new ClassConstraintException("Illegal class name '" + className + "' used by '" + tostring(obj) + "'.");
1060 }
1061
1062 final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
1063
1064 try {
1065 final Type t = Type.getReturnType(sig);
1066 if (name.equals(Const.STATIC_INITIALIZER_NAME) && t != Type.VOID) {
1067 addMessage("Class or interface initialization method '" + Const.STATIC_INITIALIZER_NAME + "' usually has VOID return type instead of '" + t
1068 + "'. Note this is really not a requirement of The Java Virtual Machine Specification, Second Edition.");
1069 }
1070 } catch (final ClassFormatException cfe) {
1071 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
1072 }
1073
1074 }
1075
1076 @Override
1077 public void visitConstantMethodref(final ConstantMethodref obj) {
1078 if (obj.getTag() != Const.CONSTANT_Methodref) {
1079 throw new ClassConstraintException("ConstantMethodref '" + tostring(obj) + "' has wrong tag!");
1080 }
1081 final int nameAndTypeIndex = obj.getNameAndTypeIndex();
1082 final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(nameAndTypeIndex);
1083 final String name = ((ConstantUtf8) cp.getConstant(cnat.getNameIndex())).getBytes();
1084 if (!validClassMethodName(name)) {
1085 throw new ClassConstraintException("Invalid (non-interface) method name '" + name + "' referenced by '" + tostring(obj) + "'.");
1086 }
1087
1088 final int classIndex = obj.getClassIndex();
1089 final ConstantClass cc = (ConstantClass) cp.getConstant(classIndex);
1090 final String className = ((ConstantUtf8) cp.getConstant(cc.getNameIndex())).getBytes();
1091 if (!validClassName(className)) {
1092 throw new ClassConstraintException("Illegal class name '" + className + "' used by '" + tostring(obj) + "'.");
1093 }
1094
1095 final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
1096
1097 try {
1098 final Type t = Type.getReturnType(sig);
1099 if (name.equals(Const.CONSTRUCTOR_NAME) && t != Type.VOID) {
1100 throw new ClassConstraintException("Instance initialization method must have VOID return type.");
1101 }
1102 } catch (final ClassFormatException cfe) {
1103 throw new ClassConstraintException("Illegal descriptor (==signature) '" + sig + "' used by '" + tostring(obj) + "'.", cfe);
1104 }
1105 }
1106
1107 }
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122 private static final class InnerClassDetector extends EmptyVisitor {
1123 private boolean hasInnerClass;
1124 private final JavaClass jc;
1125 private final ConstantPool cp;
1126
1127
1128 InnerClassDetector(final JavaClass javaClass) {
1129 this.jc = javaClass;
1130 this.cp = jc.getConstantPool();
1131 new DescendingVisitor(jc, this).visit();
1132 }
1133
1134
1135
1136
1137
1138
1139 public boolean innerClassReferenced() {
1140 return hasInnerClass;
1141 }
1142
1143
1144 @Override
1145 public void visitConstantClass(final ConstantClass obj) {
1146 final Constant c = cp.getConstant(obj.getNameIndex());
1147 if (c instanceof ConstantUtf8) {
1148 final String className = ((ConstantUtf8) c).getBytes();
1149 if (className.startsWith(Utility.packageToPath(jc.getClassName()) + "$")) {
1150 hasInnerClass = true;
1151 }
1152 }
1153 }
1154 }
1155
1156
1157
1158
1159 private static String tostring(final Node n) {
1160 return new StringRepresentation(n).toString();
1161 }
1162
1163
1164
1165
1166
1167 private static boolean validClassMethodName(final String name) {
1168 return validMethodName(name, false);
1169 }
1170
1171
1172
1173
1174 private static boolean validClassName(final String name) {
1175
1176
1177
1178 Objects.requireNonNull(name, "name");
1179 return true;
1180 }
1181
1182
1183
1184
1185 private static boolean validFieldName(final String name) {
1186
1187 return validJavaIdentifier(name);
1188 }
1189
1190
1191
1192
1193
1194 private static boolean validInterfaceMethodName(final String name) {
1195
1196 if (name.startsWith("<")) {
1197 return false;
1198 }
1199 return validJavaLangMethodName(name);
1200 }
1201
1202
1203
1204
1205
1206 private static boolean validJavaIdentifier(final String name) {
1207
1208 if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) {
1209 return false;
1210 }
1211
1212 for (int i = 1; i < name.length(); i++) {
1213 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1214 return false;
1215 }
1216 }
1217 return true;
1218 }
1219
1220
1221
1222
1223
1224
1225 private static boolean validJavaLangMethodName(final String name) {
1226 return validJavaIdentifier(name);
1227 }
1228
1229
1230
1231
1232
1233
1234 private static boolean validMethodName(final String name, final boolean allowStaticInit) {
1235 if (validJavaLangMethodName(name)) {
1236 return true;
1237 }
1238
1239 if (allowStaticInit) {
1240 return name.equals(Const.CONSTRUCTOR_NAME) || name.equals(Const.STATIC_INITIALIZER_NAME);
1241 }
1242 return name.equals(Const.CONSTRUCTOR_NAME);
1243 }
1244
1245
1246
1247
1248
1249 private LocalVariablesInfo[] localVariablesInfos;
1250
1251 private final Verifier verifier;
1252
1253
1254
1255
1256
1257
1258 public Pass2Verifier(final Verifier verifier) {
1259 this.verifier = verifier;
1260 }
1261
1262
1263
1264
1265
1266
1267
1268 private void constantPoolEntriesSatisfyStaticConstraints() {
1269 try {
1270
1271
1272
1273 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
1274 new CPESSC_Visitor(jc);
1275
1276 } catch (final ClassNotFoundException e) {
1277
1278 throw new AssertionViolatedException("Missing class: " + e, e);
1279 }
1280 }
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295 @Override
1296 public VerificationResult do_verify() {
1297 try {
1298 final VerificationResult vr1 = verifier.doPass1();
1299 if (vr1.equals(VerificationResult.VR_OK)) {
1300
1301
1302
1303 localVariablesInfos = new LocalVariablesInfo[Repository.lookupClass(verifier.getClassName()).getMethods().length];
1304
1305 VerificationResult vr = VerificationResult.VR_OK;
1306 try {
1307 constantPoolEntriesSatisfyStaticConstraints();
1308 fieldAndMethodRefsAreValid();
1309 everyClassHasAnAccessibleSuperclass();
1310 finalMethodsAreNotOverridden();
1311 } catch (final ClassConstraintException cce) {
1312 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
1313 }
1314 return vr;
1315 }
1316 return VerificationResult.VR_NOTYET;
1317
1318 } catch (final ClassNotFoundException e) {
1319
1320 throw new AssertionViolatedException("Missing class: " + e, e);
1321 }
1322 }
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332 private void everyClassHasAnAccessibleSuperclass() {
1333 try {
1334 final Set<String> hs = new HashSet<>();
1335 JavaClass jc = Repository.lookupClass(verifier.getClassName());
1336 int supidx = -1;
1337
1338 while (supidx != 0) {
1339 supidx = jc.getSuperclassNameIndex();
1340
1341 if (supidx == 0) {
1342 if (jc != Repository.lookupClass(Type.OBJECT.getClassName())) {
1343 throw new ClassConstraintException(
1344 "Superclass of '" + jc.getClassName() + "' missing but not " + Type.OBJECT.getClassName() + " itself!");
1345 }
1346 } else {
1347 final String supername = jc.getSuperclassName();
1348 if (!hs.add(supername)) {
1349 throw new ClassConstraintException("Circular superclass hierarchy detected.");
1350 }
1351 final Verifier v = VerifierFactory.getVerifier(supername);
1352 final VerificationResult vr = v.doPass1();
1353
1354 if (vr != VerificationResult.VR_OK) {
1355 throw new ClassConstraintException("Could not load in ancestor class '" + supername + "'.");
1356 }
1357 jc = Repository.lookupClass(supername);
1358
1359 if (jc.isFinal()) {
1360 throw new ClassConstraintException(
1361 "Ancestor class '" + supername + "' has the FINAL access modifier and must therefore not be subclassed.");
1362 }
1363 }
1364 }
1365
1366 } catch (final ClassNotFoundException e) {
1367
1368 throw new AssertionViolatedException("Missing class: " + e, e);
1369 }
1370 }
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382 private void fieldAndMethodRefsAreValid() {
1383 try {
1384 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
1385 final DescendingVisitor v = new DescendingVisitor(jc, new FAMRAV_Visitor(jc));
1386 v.visit();
1387
1388 } catch (final ClassNotFoundException e) {
1389
1390 throw new AssertionViolatedException("Missing class: " + e, e);
1391 }
1392 }
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403 private void finalMethodsAreNotOverridden() {
1404 try {
1405 final Map<String, String> map = new HashMap<>();
1406 JavaClass jc = Repository.lookupClass(verifier.getClassName());
1407
1408 int supidx = -1;
1409 while (supidx != 0) {
1410 supidx = jc.getSuperclassNameIndex();
1411
1412 final Method[] methods = jc.getMethods();
1413 for (final Method method : methods) {
1414 final String nameAndSig = method.getName() + method.getSignature();
1415
1416 if (map.containsKey(nameAndSig) && method.isFinal()) {
1417 if (!method.isPrivate()) {
1418 throw new ClassConstraintException("Method '" + nameAndSig + "' in class '" + map.get(nameAndSig)
1419 + "' overrides the final (not-overridable) definition in class '" + jc.getClassName() + "'.");
1420 }
1421 addMessage("Method '" + nameAndSig + "' in class '" + map.get(nameAndSig)
1422 + "' overrides the final (not-overridable) definition in class '" + jc.getClassName()
1423 + "'. This is okay, as the original definition was private; however this constraint leverage"
1424 + " was introduced by JLS 8.4.6 (not vmspec2) and the behavior of the Sun verifiers.");
1425 } else if (!method.isStatic()) {
1426 map.put(nameAndSig, jc.getClassName());
1427 }
1428 }
1429
1430 jc = Repository.lookupClass(jc.getSuperclassName());
1431
1432 }
1433
1434 } catch (final ClassNotFoundException e) {
1435
1436 throw new AssertionViolatedException("Missing class: " + e, e);
1437 }
1438
1439 }
1440
1441
1442
1443
1444
1445
1446
1447
1448 public LocalVariablesInfo getLocalVariablesInfo(final int methodNr) {
1449 if (verify() != VerificationResult.VR_OK) {
1450 return null;
1451 }
1452 if (methodNr < 0 || methodNr >= localVariablesInfos.length) {
1453 throw new AssertionViolatedException("Method number out of range.");
1454 }
1455 return localVariablesInfos[methodNr];
1456 }
1457 }