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