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