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