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.Arrays;
22
23 import org.apache.bcel.Const;
24 import org.apache.bcel.Repository;
25 import org.apache.bcel.classfile.Attribute;
26 import org.apache.bcel.classfile.ClassFormatException;
27 import org.apache.bcel.classfile.Code;
28 import org.apache.bcel.classfile.CodeException;
29 import org.apache.bcel.classfile.Constant;
30 import org.apache.bcel.classfile.ConstantCP;
31 import org.apache.bcel.classfile.ConstantClass;
32 import org.apache.bcel.classfile.ConstantDouble;
33 import org.apache.bcel.classfile.ConstantDynamic;
34 import org.apache.bcel.classfile.ConstantFieldref;
35 import org.apache.bcel.classfile.ConstantFloat;
36 import org.apache.bcel.classfile.ConstantInteger;
37 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
38 import org.apache.bcel.classfile.ConstantInvokeDynamic;
39 import org.apache.bcel.classfile.ConstantLong;
40 import org.apache.bcel.classfile.ConstantMethodref;
41 import org.apache.bcel.classfile.ConstantNameAndType;
42 import org.apache.bcel.classfile.ConstantString;
43 import org.apache.bcel.classfile.ConstantUtf8;
44 import org.apache.bcel.classfile.Field;
45 import org.apache.bcel.classfile.JavaClass;
46 import org.apache.bcel.classfile.LineNumber;
47 import org.apache.bcel.classfile.LineNumberTable;
48 import org.apache.bcel.classfile.LocalVariableTable;
49 import org.apache.bcel.classfile.Method;
50 import org.apache.bcel.generic.ALOAD;
51 import org.apache.bcel.generic.ANEWARRAY;
52 import org.apache.bcel.generic.ASTORE;
53 import org.apache.bcel.generic.ATHROW;
54 import org.apache.bcel.generic.ArrayType;
55 import org.apache.bcel.generic.BREAKPOINT;
56 import org.apache.bcel.generic.CHECKCAST;
57 import org.apache.bcel.generic.ConstantPoolGen;
58 import org.apache.bcel.generic.DLOAD;
59 import org.apache.bcel.generic.DSTORE;
60 import org.apache.bcel.generic.FLOAD;
61 import org.apache.bcel.generic.FSTORE;
62 import org.apache.bcel.generic.FieldInstruction;
63 import org.apache.bcel.generic.GETSTATIC;
64 import org.apache.bcel.generic.GotoInstruction;
65 import org.apache.bcel.generic.IINC;
66 import org.apache.bcel.generic.ILOAD;
67 import org.apache.bcel.generic.IMPDEP1;
68 import org.apache.bcel.generic.IMPDEP2;
69 import org.apache.bcel.generic.INSTANCEOF;
70 import org.apache.bcel.generic.INVOKEDYNAMIC;
71 import org.apache.bcel.generic.INVOKEINTERFACE;
72 import org.apache.bcel.generic.INVOKESPECIAL;
73 import org.apache.bcel.generic.INVOKESTATIC;
74 import org.apache.bcel.generic.INVOKEVIRTUAL;
75 import org.apache.bcel.generic.ISTORE;
76 import org.apache.bcel.generic.Instruction;
77 import org.apache.bcel.generic.InstructionHandle;
78 import org.apache.bcel.generic.InstructionList;
79 import org.apache.bcel.generic.InvokeInstruction;
80 import org.apache.bcel.generic.JsrInstruction;
81 import org.apache.bcel.generic.LDC;
82 import org.apache.bcel.generic.LDC2_W;
83 import org.apache.bcel.generic.LLOAD;
84 import org.apache.bcel.generic.LOOKUPSWITCH;
85 import org.apache.bcel.generic.LSTORE;
86 import org.apache.bcel.generic.LoadClass;
87 import org.apache.bcel.generic.MULTIANEWARRAY;
88 import org.apache.bcel.generic.NEW;
89 import org.apache.bcel.generic.NEWARRAY;
90 import org.apache.bcel.generic.ObjectType;
91 import org.apache.bcel.generic.PUTSTATIC;
92 import org.apache.bcel.generic.RET;
93 import org.apache.bcel.generic.ReferenceType;
94 import org.apache.bcel.generic.ReturnInstruction;
95 import org.apache.bcel.generic.TABLESWITCH;
96 import org.apache.bcel.generic.Type;
97 import org.apache.bcel.verifier.PassVerifier;
98 import org.apache.bcel.verifier.VerificationResult;
99 import org.apache.bcel.verifier.Verifier;
100 import org.apache.bcel.verifier.VerifierFactory;
101 import org.apache.bcel.verifier.exc.AssertionViolatedException;
102 import org.apache.bcel.verifier.exc.ClassConstraintException;
103 import org.apache.bcel.verifier.exc.InvalidMethodException;
104 import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
105 import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
106 import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException;
107
108
109
110
111
112
113
114 public final class Pass3aVerifier extends PassVerifier {
115
116
117
118
119 private final class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor {
120
121
122 private final ConstantPoolGen constantPoolGen;
123
124
125
126
127 InstOperandConstraintVisitor(final ConstantPoolGen constantPoolGen) {
128 this.constantPoolGen = constantPoolGen;
129 }
130
131
132
133
134 private void constraintViolated(final Instruction i, final String message) {
135 throw new StaticCodeInstructionOperandConstraintException("Instruction " + tostring(i) + " constraint violated: " + message);
136 }
137
138
139
140
141
142
143
144
145 private Method getMethod(final JavaClass jc, final InvokeInstruction invoke) {
146 final Method[] ms = jc.getMethods();
147 for (final Method element : ms) {
148 if (element.getName().equals(invoke.getMethodName(constantPoolGen))
149 && Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(constantPoolGen))
150 && Arrays.equals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(constantPoolGen))) {
151 return element;
152 }
153 }
154
155 return null;
156 }
157
158
159
160
161
162
163
164
165
166 private Method getMethodRecursive(final JavaClass jc, final InvokeInstruction invoke) throws ClassNotFoundException {
167 Method m;
168
169 m = getMethod(jc, invoke);
170 if (m != null) {
171
172 return m;
173 }
174
175 for (final JavaClass superclass : jc.getSuperClasses()) {
176 m = getMethod(superclass, invoke);
177 if (m != null) {
178
179 return m;
180 }
181 }
182
183 for (final JavaClass superclass : jc.getInterfaces()) {
184 m = getMethod(superclass, invoke);
185 if (m != null) {
186
187 return m;
188 }
189 }
190
191 return null;
192 }
193
194 private ObjectType getObjectType(final FieldInstruction o) {
195 final ReferenceType rt = o.getReferenceType(constantPoolGen);
196 if (rt instanceof ObjectType) {
197 return (ObjectType) rt;
198 }
199 constraintViolated(o, "expecting ObjectType but got " + rt);
200 return null;
201 }
202
203
204
205
206
207
208
209
210
211
212
213 private void indexValid(final Instruction i, final int idx) {
214 if (idx < 0 || idx >= constantPoolGen.getSize()) {
215 constraintViolated(i, "Illegal constant pool index '" + idx + "'.");
216 }
217 }
218
219
220
221
222 private int maxLocals() {
223 try {
224 return Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getCode().getMaxLocals();
225 } catch (final ClassNotFoundException e) {
226
227 throw new AssertionViolatedException("Missing class: " + e, e);
228 }
229 }
230
231
232 @Override
233 public void visitALOAD(final ALOAD o) {
234 final int idx = o.getIndex();
235 if (idx < 0) {
236 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
237 } else {
238 final int maxminus1 = maxLocals() - 1;
239 if (idx > maxminus1) {
240 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
241 }
242 }
243 }
244
245
246 @Override
247 public void visitANEWARRAY(final ANEWARRAY o) {
248 indexValid(o, o.getIndex());
249 final Constant c = constantPoolGen.getConstant(o.getIndex());
250 if (!(c instanceof ConstantClass)) {
251 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
252 }
253 final Type t = o.getType(constantPoolGen);
254 if (t instanceof ArrayType) {
255 final int dimensions = ((ArrayType) t).getDimensions();
256 if (dimensions > Const.MAX_ARRAY_DIMENSIONS) {
257 constraintViolated(o,
258 "Not allowed to create an array with more than " + Const.MAX_ARRAY_DIMENSIONS + " dimensions; actual: " + dimensions);
259 }
260 }
261 }
262
263
264 @Override
265 public void visitASTORE(final ASTORE o) {
266 final int idx = o.getIndex();
267 if (idx < 0) {
268 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
269 } else {
270 final int maxminus1 = maxLocals() - 1;
271 if (idx > maxminus1) {
272 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
273 }
274 }
275 }
276
277
278 @Override
279 public void visitCHECKCAST(final CHECKCAST o) {
280 indexValid(o, o.getIndex());
281 final Constant c = constantPoolGen.getConstant(o.getIndex());
282 if (!(c instanceof ConstantClass)) {
283 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
284 }
285 }
286
287
288 @Override
289 public void visitDLOAD(final DLOAD o) {
290 final int idx = o.getIndex();
291 if (idx < 0) {
292 constraintViolated(o, "Index '" + idx + "' must be non-negative."
293 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
294 } else {
295 final int maxminus2 = maxLocals() - 2;
296 if (idx > maxminus2) {
297 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
298 }
299 }
300 }
301
302
303 @Override
304 public void visitDSTORE(final DSTORE o) {
305 final int idx = o.getIndex();
306 if (idx < 0) {
307 constraintViolated(o, "Index '" + idx + "' must be non-negative."
308 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
309 } else {
310 final int maxminus2 = maxLocals() - 2;
311 if (idx > maxminus2) {
312 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
313 }
314 }
315 }
316
317
318
319 @Override
320 public void visitFieldInstruction(final FieldInstruction o) {
321 try {
322 indexValid(o, o.getIndex());
323 final Constant c = constantPoolGen.getConstant(o.getIndex());
324 if (!(c instanceof ConstantFieldref)) {
325 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + tostring(c) + "'.");
326 }
327
328 final String fieldName = o.getFieldName(constantPoolGen);
329
330 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
331 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
332 if (f == null) {
333 constraintViolated(o, "Referenced field '" + fieldName + "' does not exist in class '" + jc.getClassName() + "'.");
334 }
335 } catch (final ClassNotFoundException e) {
336
337 throw new AssertionViolatedException("Missing class: " + e, e);
338 }
339 }
340
341
342 @Override
343 public void visitFLOAD(final FLOAD o) {
344 final int idx = o.getIndex();
345 if (idx < 0) {
346 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
347 } else {
348 final int maxminus1 = maxLocals() - 1;
349 if (idx > maxminus1) {
350 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
351 }
352 }
353 }
354
355
356 @Override
357 public void visitFSTORE(final FSTORE o) {
358 final int idx = o.getIndex();
359 if (idx < 0) {
360 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
361 } else {
362 final int maxminus1 = maxLocals() - 1;
363 if (idx > maxminus1) {
364 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
365 }
366 }
367 }
368
369
370 @Override
371 public void visitGETSTATIC(final GETSTATIC o) {
372 try {
373 final String fieldName = o.getFieldName(constantPoolGen);
374 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
375 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
376 if (f == null) {
377 throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
378 }
379
380 if (!f.isStatic()) {
381 constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
382 }
383 } catch (final ClassNotFoundException e) {
384
385 throw new AssertionViolatedException("Missing class: " + e, e);
386 }
387 }
388
389
390 @Override
391 public void visitIINC(final IINC o) {
392 final int idx = o.getIndex();
393 if (idx < 0) {
394 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
395 } else {
396 final int maxminus1 = maxLocals() - 1;
397 if (idx > maxminus1) {
398 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
399 }
400 }
401 }
402
403
404 @Override
405 public void visitILOAD(final ILOAD o) {
406 final int idx = o.getIndex();
407 if (idx < 0) {
408 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
409 } else {
410 final int maxminus1 = maxLocals() - 1;
411 if (idx > maxminus1) {
412 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
413 }
414 }
415 }
416
417
418 @Override
419 public void visitINSTANCEOF(final INSTANCEOF o) {
420 indexValid(o, o.getIndex());
421 final Constant c = constantPoolGen.getConstant(o.getIndex());
422 if (!(c instanceof ConstantClass)) {
423 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
424 }
425 }
426
427
428 @Override
429 public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC o) {
430 throw new UnsupportedOperationException("INVOKEDYNAMIC instruction is not supported at this time");
431 }
432
433
434 @Override
435 public void visitInvokeInstruction(final InvokeInstruction o) {
436 indexValid(o, o.getIndex());
437 if (o instanceof INVOKEVIRTUAL || o instanceof INVOKESPECIAL || o instanceof INVOKESTATIC) {
438 final Constant c = constantPoolGen.getConstant(o.getIndex());
439 if (!(c instanceof ConstantMethodref)) {
440 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '" + tostring(c) + "'.");
441 } else {
442
443 final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantMethodref) c).getNameAndTypeIndex());
444 final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex());
445 if (cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && !(o instanceof INVOKESPECIAL)) {
446 constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
447 }
448 if (!cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && cutf8.getBytes().startsWith("<")) {
449 constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods"
450 + " may be called by the method invocation instructions.");
451 }
452 }
453 } else {
454 final Constant c = constantPoolGen.getConstant(o.getIndex());
455 if (!(c instanceof ConstantInterfaceMethodref) && !(c instanceof ConstantInvokeDynamic)) {
456 constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref/InvokeDynamic but a '" + tostring(c) + "'.");
457 }
458
459
460
461
462
463
464 final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantCP) c).getNameAndTypeIndex());
465 final String name = ((ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex())).getBytes();
466 if (name.equals(Const.CONSTRUCTOR_NAME)) {
467 constraintViolated(o, "Method to invoke must not be '" + Const.CONSTRUCTOR_NAME + "'.");
468 }
469 if (name.equals(Const.STATIC_INITIALIZER_NAME)) {
470 constraintViolated(o, "Method to invoke must not be '" + Const.STATIC_INITIALIZER_NAME + "'.");
471 }
472 }
473
474
475
476 Type t = o.getReturnType(constantPoolGen);
477 if (t instanceof ArrayType) {
478 t = ((ArrayType) t).getBasicType();
479 }
480 if (t instanceof ObjectType) {
481 final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
482 final VerificationResult vr = v.doPass2();
483 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
484 constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
485 }
486 }
487
488 final Type[] ts = o.getArgumentTypes(constantPoolGen);
489 for (final Type element : ts) {
490 t = element;
491 if (t instanceof ArrayType) {
492 t = ((ArrayType) t).getBasicType();
493 }
494 if (t instanceof ObjectType) {
495 final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
496 final VerificationResult vr = v.doPass2();
497 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
498 constraintViolated(o, "Argument type class/interface could not be verified successfully: '" + vr.getMessage() + "'.");
499 }
500 }
501 }
502
503 }
504
505
506 @Override
507 public void visitINVOKEINTERFACE(final INVOKEINTERFACE o) {
508 try {
509
510
511
512
513 final String className = o.getClassName(constantPoolGen);
514 final JavaClass jc = Repository.lookupClass(className);
515 final Method m = getMethodRecursive(jc, o);
516 if (m == null) {
517 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
518 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
519 }
520 if (jc.isClass()) {
521 constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is a class, but not an interface as expected.");
522 }
523 } catch (final ClassNotFoundException e) {
524
525 throw new AssertionViolatedException("Missing class: " + e, e);
526 }
527 }
528
529
530 @Override
531 public void visitINVOKESPECIAL(final INVOKESPECIAL o) {
532 try {
533
534
535
536
537 final String className = o.getClassName(constantPoolGen);
538 final JavaClass jc = Repository.lookupClass(className);
539 final Method m = getMethodRecursive(jc, o);
540 if (m == null) {
541 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
542 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
543 }
544
545 JavaClass current = Repository.lookupClass(verifier.getClassName());
546 if (current.isSuper() && Repository.instanceOf(current, jc) && !current.equals(jc)
547 && !o.getMethodName(constantPoolGen).equals(Const.CONSTRUCTOR_NAME)) {
548
549
550 int supidx = -1;
551
552 Method meth = null;
553 while (supidx != 0) {
554 supidx = current.getSuperclassNameIndex();
555 current = Repository.lookupClass(current.getSuperclassName());
556
557 final Method[] meths = current.getMethods();
558 for (final Method meth2 : meths) {
559 if (meth2.getName().equals(o.getMethodName(constantPoolGen))
560 && Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(constantPoolGen))
561 && Arrays.equals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(constantPoolGen))) {
562 meth = meth2;
563 break;
564 }
565 }
566 if (meth != null) {
567 break;
568 }
569 }
570 if (meth == null) {
571 constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '" + o.getMethodName(constantPoolGen)
572 + "' with proper signature not declared in superclass hierarchy.");
573 }
574 }
575
576 } catch (final ClassNotFoundException e) {
577
578 throw new AssertionViolatedException("Missing class: " + e, e);
579 }
580
581 }
582
583
584 @Override
585 public void visitINVOKESTATIC(final INVOKESTATIC o) {
586 try {
587
588
589
590
591 final String className = o.getClassName(constantPoolGen);
592 final JavaClass jc = Repository.lookupClass(className);
593 final Method m = getMethodRecursive(jc, o);
594 if (m == null) {
595 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
596 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
597 } else if (!m.isStatic()) {
598 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' has ACC_STATIC unset.");
599 }
600
601 } catch (final ClassNotFoundException e) {
602
603 throw new AssertionViolatedException("Missing class: " + e, e);
604 }
605 }
606
607
608 @Override
609 public void visitINVOKEVIRTUAL(final INVOKEVIRTUAL o) {
610 try {
611
612
613
614
615 final String className = o.getClassName(constantPoolGen);
616 final JavaClass jc;
617 if (className.charAt(0) == '[') {
618 jc = Repository.lookupClass("java.lang.Object");
619 } else {
620 jc = Repository.lookupClass(className);
621 }
622 final Method m = getMethodRecursive(jc, o);
623 if (m == null) {
624 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '"
625 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'.");
626 }
627 if (!jc.isClass()) {
628 constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is an interface, but not a class as expected.");
629 }
630
631 } catch (final ClassNotFoundException e) {
632
633
634 addMessage("Unable to verify INVOKEVITUAL as cannot load target class: " + e.getCause());
635 }
636 }
637
638
639 @Override
640 public void visitISTORE(final ISTORE o) {
641 final int idx = o.getIndex();
642 if (idx < 0) {
643 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
644 } else {
645 final int maxminus1 = maxLocals() - 1;
646 if (idx > maxminus1) {
647 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
648 }
649 }
650 }
651
652
653
654 @Override
655 public void visitLDC(final LDC ldc) {
656 indexValid(ldc, ldc.getIndex());
657 final Constant c = constantPoolGen.getConstant(ldc.getIndex());
658 if (c instanceof ConstantClass) {
659 addMessage("Operand of LDC or LDC_W is CONSTANT_Class '" + tostring(c) + "' - this is only supported in JDK 1.5 and higher.");
660 } else if (!(c instanceof ConstantInteger || c instanceof ConstantFloat || c instanceof ConstantString || c instanceof ConstantDynamic)) {
661 constraintViolated(ldc,
662 "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float, CONSTANT_String or CONSTANT_Dynamic but is '"
663 + tostring(c) + "'.");
664 }
665 }
666
667
668
669 @Override
670 public void visitLDC2_W(final LDC2_W o) {
671 indexValid(o, o.getIndex());
672 final Constant c = constantPoolGen.getConstant(o.getIndex());
673 if (!(c instanceof ConstantLong || c instanceof ConstantDouble)) {
674 constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '" + tostring(c) + "'.");
675 }
676 try {
677 indexValid(o, o.getIndex() + 1);
678 } catch (final StaticCodeInstructionOperandConstraintException e) {
679 throw new AssertionViolatedException("Does not BCEL handle that? LDC2_W operand has a problem.", e);
680 }
681 }
682
683
684 @Override
685 public void visitLLOAD(final LLOAD o) {
686 final int idx = o.getIndex();
687 if (idx < 0) {
688 constraintViolated(o, "Index '" + idx + "' must be non-negative."
689 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
690 } else {
691 final int maxminus2 = maxLocals() - 2;
692 if (idx > maxminus2) {
693 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
694 }
695 }
696 }
697
698
699
700
701
702
703
704
705 @Override
706 public void visitLoadClass(final LoadClass loadClass) {
707 final ObjectType t = loadClass.getLoadClassType(constantPoolGen);
708 if (t != null) {
709 final Verifier v = VerifierFactory.getVerifier(t.getClassName());
710 final VerificationResult vr = v.doPass1();
711 if (vr.getStatus() != VerificationResult.VERIFIED_OK) {
712 constraintViolated((Instruction) loadClass,
713 "Class '" + loadClass.getLoadClassType(constantPoolGen).getClassName() + "' is referenced, but cannot be loaded: '" + vr + "'.");
714 }
715 }
716 }
717
718
719
720
721
722
723
724
725
726
727
728
729 @Override
730 public void visitLOOKUPSWITCH(final LOOKUPSWITCH o) {
731 final int[] matchs = o.getMatchs();
732 int max = Integer.MIN_VALUE;
733 for (int i = 0; i < matchs.length; i++) {
734 if (matchs[i] == max && i != 0) {
735 constraintViolated(o, "Match '" + matchs[i] + "' occurs more than once.");
736 }
737 if (matchs[i] < max) {
738 constraintViolated(o, "Lookup table must be sorted but isn't.");
739 } else {
740 max = matchs[i];
741 }
742 }
743 }
744
745
746 @Override
747 public void visitLSTORE(final LSTORE o) {
748 final int idx = o.getIndex();
749 if (idx < 0) {
750 constraintViolated(o, "Index '" + idx + "' must be non-negative."
751 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
752 } else {
753 final int maxminus2 = maxLocals() - 2;
754 if (idx > maxminus2) {
755 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'.");
756 }
757 }
758 }
759
760
761 @Override
762 public void visitMULTIANEWARRAY(final MULTIANEWARRAY o) {
763 indexValid(o, o.getIndex());
764 final Constant c = constantPoolGen.getConstant(o.getIndex());
765 if (!(c instanceof ConstantClass)) {
766 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
767 }
768 final int dimensions2create = o.getDimensions();
769 if (dimensions2create < 1) {
770 constraintViolated(o, "Number of dimensions to create must be greater than zero.");
771 }
772 final Type t = o.getType(constantPoolGen);
773 if (t instanceof ArrayType) {
774 final int dimensions = ((ArrayType) t).getDimensions();
775 if (dimensions < dimensions2create) {
776 constraintViolated(o, "Not allowed to create array with more dimensions ('" + dimensions2create
777 + "') than the one referenced by the CONSTANT_Class '" + t + "'.");
778 }
779 } else {
780 constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type."
781 + " [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]");
782 }
783 }
784
785
786 @Override
787 public void visitNEW(final NEW o) {
788 indexValid(o, o.getIndex());
789 final Constant c = constantPoolGen.getConstant(o.getIndex());
790 if (!(c instanceof ConstantClass)) {
791 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'.");
792 } else {
793 final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(((ConstantClass) c).getNameIndex());
794 final Type t = Type.getType("L" + cutf8.getBytes() + ";");
795 if (t instanceof ArrayType) {
796 constraintViolated(o, "NEW must not be used to create an array.");
797 }
798 }
799
800 }
801
802
803 @Override
804 public void visitNEWARRAY(final NEWARRAY o) {
805 final byte t = o.getTypecode();
806 if (!(t == Const.T_BOOLEAN || t == Const.T_CHAR || t == Const.T_FLOAT || t == Const.T_DOUBLE || t == Const.T_BYTE || t == Const.T_SHORT
807 || t == Const.T_INT || t == Const.T_LONG)) {
808 constraintViolated(o, "Illegal type code '" + tostring(t) + "' for 'atype' operand.");
809 }
810 }
811
812
813 @Override
814 public void visitPUTSTATIC(final PUTSTATIC o) {
815 try {
816 final String fieldName = o.getFieldName(constantPoolGen);
817 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName());
818 final Field f = jc.findField(fieldName, o.getType(constantPoolGen));
819 if (f == null) {
820 throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName());
821 }
822
823 if (f.isFinal() && !verifier.getClassName().equals(getObjectType(o).getClassName())) {
824 constraintViolated(o, "Referenced field '" + f + "' is final and must therefore be declared in the current class '"
825 + verifier.getClassName() + "' which is not the case: it is declared in '" + o.getReferenceType(constantPoolGen) + "'.");
826 }
827
828 if (!f.isStatic()) {
829 constraintViolated(o, "Referenced field '" + f + "' is not static which it should be.");
830 }
831
832 final String methName = Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getName();
833
834
835 if (!jc.isClass() && !methName.equals(Const.STATIC_INITIALIZER_NAME)) {
836 constraintViolated(o, "Interface field '" + f + "' must be set in a '" + Const.STATIC_INITIALIZER_NAME + "' method.");
837 }
838 } catch (final ClassNotFoundException e) {
839
840 throw new AssertionViolatedException("Missing class: " + e, e);
841 }
842 }
843
844
845 @Override
846 public void visitRET(final RET o) {
847 final int idx = o.getIndex();
848 if (idx < 0) {
849 constraintViolated(o, "Index '" + idx + "' must be non-negative.");
850 } else {
851 final int maxminus1 = maxLocals() - 1;
852 if (idx > maxminus1) {
853 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'.");
854 }
855 }
856 }
857
858
859
860
861 @Override
862 public void visitTABLESWITCH(final TABLESWITCH o) {
863
864
865 }
866 }
867
868
869 private static boolean contains(final int[] ints, final int i) {
870 for (final int k : ints) {
871 if (k == i) {
872 return true;
873 }
874 }
875 return false;
876 }
877
878
879 private final Verifier verifier;
880
881
882
883
884 private final int methodNo;
885
886
887
888
889
890 private InstructionList instructionList;
891
892
893
894
895
896 private Code code;
897
898
899 public Pass3aVerifier(final Verifier verifier, final int methodNo) {
900 this.verifier = verifier;
901 this.methodNo = methodNo;
902 }
903
904
905
906
907
908
909
910
911
912 private void delayedPass2Checks() {
913
914 final int[] instructionPositions = instructionList.getInstructionPositions();
915 final int codeLength = code.getCode().length;
916
917
918
919
920 final LineNumberTable lnt = code.getLineNumberTable();
921 if (lnt != null) {
922 final LineNumber[] lineNumbers = lnt.getLineNumberTable();
923 final IntList offsets = new IntList();
924 lineNumberLoop: for (final LineNumber lineNumber : lineNumbers) {
925 for (final int instructionPosition : instructionPositions) {
926
927 final int offset = lineNumber.getStartPC();
928 if (instructionPosition == offset) {
929 if (offsets.contains(offset)) {
930 addMessage("LineNumberTable attribute '" + code.getLineNumberTable() + "' refers to the same code offset ('" + offset
931 + "') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler].");
932 } else {
933 offsets.add(offset);
934 }
935 continue lineNumberLoop;
936 }
937 }
938 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LineNumberTable attribute '" + code.getLineNumberTable()
939 + "' referring to a code offset ('" + lineNumber.getStartPC() + "') that does not exist.");
940 }
941 }
942
943
944
945
946
947
948
949 final Attribute[] atts = code.getAttributes();
950 for (final Attribute att : atts) {
951 if (att instanceof LocalVariableTable) {
952 ((LocalVariableTable) att).forEach(localVariable -> {
953 final int startpc = localVariable.getStartPC();
954 final int length = localVariable.getLength();
955
956 if (!contains(instructionPositions, startpc)) {
957 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '"
958 + code.getLocalVariableTable() + "' referring to a code offset ('" + startpc + "') that does not exist.");
959 }
960 if (!contains(instructionPositions, startpc + length) && startpc + length != codeLength) {
961 throw new ClassConstraintException(
962 "Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '" + code.getLocalVariableTable()
963 + "' referring to a code offset start_pc+length ('" + (startpc + length) + "') that does not exist.");
964 }
965 });
966 }
967 }
968
969
970
971
972
973
974
975 final CodeException[] exceptionTable = code.getExceptionTable();
976 for (final CodeException element : exceptionTable) {
977 final int startpc = element.getStartPC();
978 final int endpc = element.getEndPC();
979 final int handlerpc = element.getHandlerPC();
980 if (startpc >= endpc) {
981 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
982 + "' that has its start_pc ('" + startpc + "') not smaller than its end_pc ('" + endpc + "').");
983 }
984 if (!contains(instructionPositions, startpc)) {
985 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
986 + "' that has a non-existant bytecode offset as its start_pc ('" + startpc + "').");
987 }
988 if (!contains(instructionPositions, endpc) && endpc != codeLength) {
989 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
990 + "' that has a non-existant bytecode offset as its end_pc ('" + startpc + "') [that is also not equal to code_length ('" + codeLength
991 + "')].");
992 }
993 if (!contains(instructionPositions, handlerpc)) {
994 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element
995 + "' that has a non-existant bytecode offset as its handler_pc ('" + handlerpc + "').");
996 }
997 }
998 }
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 @Override
1011 public VerificationResult do_verify() {
1012 try {
1013 if (verifier.doPass2().equals(VerificationResult.VR_OK)) {
1014
1015
1016 final JavaClass jc = Repository.lookupClass(verifier.getClassName());
1017 final Method[] methods = jc.getMethods();
1018 if (methodNo >= methods.length) {
1019 throw new InvalidMethodException("METHOD DOES NOT EXIST!");
1020 }
1021 final Method method = methods[methodNo];
1022 code = method.getCode();
1023
1024
1025 if (method.isAbstract() || method.isNative()) {
1026 return VerificationResult.VR_OK;
1027 }
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038 try {
1039 instructionList = new InstructionList(method.getCode().getCode());
1040 } catch (final RuntimeException re) {
1041 return new VerificationResult(VerificationResult.VERIFIED_REJECTED,
1042 "Bad bytecode in the code array of the Code attribute of method '" + tostring(method) + "'.");
1043 }
1044
1045 instructionList.setPositions(true);
1046
1047
1048 VerificationResult vr = VerificationResult.VR_OK;
1049 try {
1050 delayedPass2Checks();
1051 } catch (final ClassConstraintException | ClassFormatException cce) {
1052 return new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
1053 }
1054 try {
1055 pass3StaticInstructionChecks();
1056 pass3StaticInstructionOperandsChecks();
1057 } catch (final StaticCodeConstraintException | ClassFormatException scce) {
1058 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage());
1059 } catch (final ClassCastException cce) {
1060 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Class Cast Exception: " + cce.getMessage());
1061 }
1062 return vr;
1063 }
1064
1065 return VerificationResult.VR_NOTYET;
1066 } catch (final ClassNotFoundException e) {
1067
1068 throw new AssertionViolatedException("Missing class: " + e, e);
1069 }
1070 }
1071
1072
1073 public int getMethodNo() {
1074 return methodNo;
1075 }
1076
1077
1078
1079
1080
1081
1082
1083 private void pass3StaticInstructionChecks() {
1084
1085
1086
1087
1088
1089
1090 if (code.getCode().length >= Const.MAX_CODE_SIZE) {
1091 throw new StaticCodeInstructionConstraintException(
1092 "Code array in code attribute '" + tostring(code) + "' too big: must be smaller than " + Const.MAX_CODE_SIZE + "65536 bytes.");
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110 InstructionHandle ih = instructionList.getStart();
1111 while (ih != null) {
1112 final Instruction i = ih.getInstruction();
1113 if (i instanceof IMPDEP1) {
1114 throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
1115 }
1116 if (i instanceof IMPDEP2) {
1117 throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
1118 }
1119 if (i instanceof BREAKPOINT) {
1120 throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!");
1121 }
1122 ih = ih.getNext();
1123 }
1124
1125
1126
1127
1128
1129 final Instruction last = instructionList.getEnd().getInstruction();
1130 if (!(last instanceof ReturnInstruction || last instanceof RET || last instanceof GotoInstruction || last instanceof ATHROW)) {
1131 throw new StaticCodeInstructionConstraintException(
1132 "Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do"
1133 + " - so it may be a false alarm if the last instruction is not reachable.");
1134 }
1135 }
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145 private void pass3StaticInstructionOperandsChecks() {
1146 try {
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158 final ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(verifier.getClassName()).getConstantPool());
1159 final InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg);
1160
1161
1162 InstructionHandle ih = instructionList.getStart();
1163 while (ih != null) {
1164 final Instruction i = ih.getInstruction();
1165
1166
1167 if (i instanceof JsrInstruction) {
1168 final InstructionHandle target = ((JsrInstruction) i).getTarget();
1169 if (target == instructionList.getStart()) {
1170 throw new StaticCodeInstructionOperandConstraintException(
1171 "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction"
1172 + " (such as the very first instruction, which is targeted by instruction '" + tostring(ih) + "' as its target.");
1173 }
1174 if (!(target.getInstruction() instanceof ASTORE)) {
1175 throw new StaticCodeInstructionOperandConstraintException(
1176 "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else"
1177 + " than an ASTORE instruction. Instruction '" + tostring(ih) + "' targets '" + tostring(target) + "'.");
1178 }
1179 }
1180
1181
1182 ih.accept(v);
1183
1184 ih = ih.getNext();
1185 }
1186
1187 } catch (final ClassNotFoundException e) {
1188
1189 throw new AssertionViolatedException("Missing class: " + e, e);
1190 }
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200 protected String tostring(final Object obj) {
1201 String ret;
1202 try {
1203 ret = obj.toString();
1204 } catch (final RuntimeException e) {
1205
1206
1207 String s = obj.getClass().getName();
1208 s = s.substring(s.lastIndexOf(".") + 1);
1209 ret = "<<" + s + ">>";
1210 }
1211 return ret;
1212 }
1213 }