1 package org.apache.bcel.verifier.statics;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache BCEL" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache BCEL", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import org.apache.bcel.*;
58 import org.apache.bcel.generic.*;
59 import org.apache.bcel.classfile.*;
60 import org.apache.bcel.verifier.*;
61 import org.apache.bcel.verifier.exc.*;
62
63 /***
64 * This PassVerifier verifies a class file according to
65 * pass 3, static part as described in The Java Virtual
66 * Machine Specification, 2nd edition.
67 * More detailed information is to be found at the do_verify()
68 * method's documentation.
69 *
70 * @version $Id: Pass3aVerifier.java,v 1.3 2002/06/13 09:32:50 enver Exp $
71 * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A>
72 * @see #do_verify()
73 */
74 public final class Pass3aVerifier extends PassVerifier{
75
76 /*** The Verifier that created this. */
77 private Verifier myOwner;
78
79 /***
80 * The method number to verify.
81 * This is the index in the array returned
82 * by JavaClass.getMethods().
83 */
84 private int method_no;
85
86 /*** The one and only InstructionList object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */
87 InstructionList instructionList;
88 /*** The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */
89 Code code;
90
91 /*** Should only be instantiated by a Verifier. */
92 public Pass3aVerifier(Verifier owner, int method_no){
93 myOwner = owner;
94 this.method_no = method_no;
95 }
96
97 /***
98 * Pass 3a is the verification of static constraints of
99 * JVM code (such as legal targets of branch instructions).
100 * This is the part of pass 3 where you do not need data
101 * flow analysis.
102 * JustIce also delays the checks for a correct exception
103 * table of a Code attribute and correct line number entries
104 * in a LineNumberTable attribute of a Code attribute (which
105 * conceptually belong to pass 2) to this pass. Also, most
106 * of the check for valid local variable entries in a
107 * LocalVariableTable attribute of a Code attribute is
108 * delayed until this pass.
109 * All these checks need access to the code array of the
110 * Code attribute.
111 *
112 * @throws InvalidMethodException if the method to verify does not exist.
113 */
114 public VerificationResult do_verify(){
115 if (myOwner.doPass2().equals(VerificationResult.VR_OK)){
116 // Okay, class file was loaded correctly by Pass 1
117 // and satisfies static constraints of Pass 2.
118 JavaClass jc = Repository.lookupClass(myOwner.getClassName());
119 Method[] methods = jc.getMethods();
120 if (method_no >= methods.length){
121 throw new InvalidMethodException("METHOD DOES NOT EXIST!");
122 }
123 Method method = methods[method_no];
124 code = method.getCode();
125
126 // No Code? Nothing to verify!
127 if ( method.isAbstract() || method.isNative() ){ // IF mg HAS NO CODE (static constraint of Pass 2)
128 return VerificationResult.VR_OK;
129 }
130
131 // TODO:
132 // We want a very sophisticated code examination here with good explanations
133 // on where to look for an illegal instruction or such.
134 // Only after that we should try to build an InstructionList and throw an
135 // AssertionViolatedException if after our examination InstructionList building
136 // still fails.
137 // That examination should be implemented in a byte-oriented way, i.e. look for
138 // an instruction, make sure its validity, count its length, find the next
139 // instruction and so on.
140 try{
141 instructionList = new InstructionList(method.getCode().getCode());
142 }
143 catch(RuntimeException re){
144 return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Bad bytecode in the code array of the Code attribute of method '"+method+"'.");
145 }
146
147 instructionList.setPositions(true);
148
149 // Start verification.
150 VerificationResult vr = VerificationResult.VR_OK; //default
151 try{
152 delayedPass2Checks();
153 }
154 catch(ClassConstraintException cce){
155 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage());
156 return vr;
157 }
158 try{
159 pass3StaticInstructionChecks();
160 pass3StaticInstructionOperandsChecks();
161 }
162 catch(StaticCodeConstraintException scce){
163 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage());
164 }
165 return vr;
166 }
167 else{ //did not pass Pass 2.
168 return VerificationResult.VR_NOTYET;
169 }
170 }
171
172 /***
173 * These are the checks that could be done in pass 2 but are delayed to pass 3
174 * for performance reasons. Also, these checks need access to the code array
175 * of the Code attribute of a Method so it's okay to perform them here.
176 * Also see the description of the do_verify() method.
177 *
178 * @throws ClassConstraintException if the verification fails.
179 * @see #do_verify()
180 */
181 private void delayedPass2Checks(){
182
183 int[] instructionPositions = instructionList.getInstructionPositions();
184 int codeLength = code.getCode().length;
185
186 /////////////////////
187 // LineNumberTable //
188 /////////////////////
189 LineNumberTable lnt = code.getLineNumberTable();
190 if (lnt != null){
191 LineNumber[] lineNumbers = lnt.getLineNumberTable();
192 IntList offsets = new IntList();
193 lineNumber_loop: for (int i=0; i < lineNumbers.length; i++){ // may appear in any order.
194 for (int j=0; j < instructionPositions.length; j++){
195 // TODO: Make this a binary search! The instructionPositions array is naturally ordered!
196 int offset = lineNumbers[i].getStartPC();
197 if (instructionPositions[j] == offset){
198 if (offsets.contains(offset)){
199 addMessage("LineNumberTable attribute '"+code.getLineNumberTable()+"' refers to the same code offset ('"+offset+"') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler].");
200 }
201 else{
202 offsets.add(offset);
203 }
204 continue lineNumber_loop;
205 }
206 }
207 throw new ClassConstraintException("Code attribute '"+code+"' has a LineNumberTable attribute '"+code.getLineNumberTable()+"' referring to a code offset ('"+lineNumbers[i].getStartPC()+"') that does not exist.");
208 }
209 }
210
211 ///////////////////////////
212 // LocalVariableTable(s) //
213 ///////////////////////////
214 /* We cannot use code.getLocalVariableTable() because there could be more
215 than only one. This is a bug in BCEL. */
216 Attribute[] atts = code.getAttributes();
217 for (int a=0; a<atts.length; a++){
218 if (atts[a] instanceof LocalVariableTable){
219 LocalVariableTable lvt = (LocalVariableTable) atts[a];
220 if (lvt != null){
221 LocalVariable[] localVariables = lvt.getLocalVariableTable();
222 for (int i=0; i<localVariables.length; i++){
223 int startpc = localVariables[i].getStartPC();
224 int length = localVariables[i].getLength();
225
226 if (!contains(instructionPositions, startpc)){
227 throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset ('"+startpc+"') that does not exist.");
228 }
229 if ( (!contains(instructionPositions, startpc+length)) && (startpc+length != codeLength) ){
230 throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset start_pc+length ('"+(startpc+length)+"') that does not exist.");
231 }
232 }
233 }
234 }
235 }
236
237 ////////////////////
238 // ExceptionTable //
239 ////////////////////
240 // In BCEL's "classfile" API, the startPC/endPC-notation is
241 // inclusive/exclusive as in the Java Virtual Machine Specification.
242 // WARNING: This is not true for BCEL's "generic" API.
243 CodeException[] exceptionTable = code.getExceptionTable();
244 for (int i=0; i<exceptionTable.length; i++){
245 int startpc = exceptionTable[i].getStartPC();
246 int endpc = exceptionTable[i].getEndPC();
247 int handlerpc = exceptionTable[i].getHandlerPC();
248 if (startpc >= endpc){
249 throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has its start_pc ('"+startpc+"') not smaller than its end_pc ('"+endpc+"').");
250 }
251 if (!contains(instructionPositions, startpc)){
252 throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its start_pc ('"+startpc+"').");
253 }
254 if ( (!contains(instructionPositions, endpc)) && (endpc != codeLength)){
255 throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its end_pc ('"+startpc+"') [that is also not equal to code_length ('"+codeLength+"')].");
256 }
257 if (!contains(instructionPositions, handlerpc)){
258 throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its handler_pc ('"+handlerpc+"').");
259 }
260 }
261 }
262
263 /***
264 * These are the checks if constraints are satisfied which are described in the
265 * Java Virtual Machine Specification, Second Edition as Static Constraints on
266 * the instructions of Java Virtual Machine Code (chapter 4.8.1).
267 *
268 * @throws StaticCodeConstraintException if the verification fails.
269 */
270 private void pass3StaticInstructionChecks(){
271
272 // Code array must not be empty:
273 // Enforced in pass 2 (also stated in the static constraints of the Code
274 // array in vmspec2), together with pass 1 (reading code_length bytes and
275 // interpreting them as code[]). So this must not be checked again here.
276
277 if (! (code.getCode().length < 65536)){// contradicts vmspec2 page 152 ("Limitations"), but is on page 134.
278 throw new StaticCodeInstructionConstraintException("Code array in code attribute '"+code+"' too big: must be smaller than 65536 bytes.");
279 }
280
281 // First opcode at offset 0: okay, that's clear. Nothing to do.
282
283 // Only instances of the instructions documented in Section 6.4 may appear in
284 // the code array.
285
286 // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :)
287
288 // The last byte of the last instruction in the code array must be the byte at index
289 // code_length-1 : See the do_verify() comments. We actually don't iterate through the
290 // byte array, but use an InstructionList so we cannot check for this. But BCEL does
291 // things right, so it's implicitly okay.
292
293 // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2,
294 // BREAKPOINT... that BCEL knows about but which are illegal anyway.
295 // We currently go the safe way here.
296 InstructionHandle ih = instructionList.getStart();
297 while (ih != null){
298 Instruction i = ih.getInstruction();
299 if (i instanceof IMPDEP1){
300 throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
301 }
302 if (i instanceof IMPDEP2){
303 throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!");
304 }
305 if (i instanceof BREAKPOINT){
306 throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!");
307 }
308 ih = ih.getNext();
309 }
310
311 // The original verifier seems to do this check here, too.
312 // An unreachable last instruction may also not fall through the
313 // end of the code, which is stupid -- but with the original
314 // verifier's subroutine semantics one cannot predict reachability.
315 Instruction last = instructionList.getEnd().getInstruction();
316 if (! ((last instanceof ReturnInstruction) ||
317 (last instanceof RET) ||
318 (last instanceof GotoInstruction) ||
319 (last instanceof ATHROW) )) // JSR / JSR_W would possibly RETurn and then fall off the code!
320 throw new StaticCodeInstructionConstraintException("Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do - so it may be a false alarm if the last instruction is not reachable.");
321 }
322
323 /***
324 * These are the checks for the satisfaction of constraints which are described in the
325 * Java Virtual Machine Specification, Second Edition as Static Constraints on
326 * the operands of instructions of Java Virtual Machine Code (chapter 4.8.1).
327 * BCEL parses the code array to create an InstructionList and therefore has to check
328 * some of these constraints. Additional checks are also implemented here.
329 *
330 * @throws StaticCodeConstraintException if the verification fails.
331 */
332 private void pass3StaticInstructionOperandsChecks(){
333 // When building up the InstructionList, BCEL has already done all those checks
334 // mentioned in The Java Virtual Machine Specification, Second Edition, as
335 // "static constraints on the operands of instructions in the code array".
336 // TODO: see the do_verify() comments. Maybe we should really work on the
337 // byte array first to give more comprehensive messages.
338 // TODO: Review Exception API, possibly build in some "offending instruction" thing
339 // when we're ready to insulate the offending instruction by doing the
340 // above thing.
341
342 // TODO: Implement as much as possible here. BCEL does _not_ check everything.
343
344 ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(myOwner.getClassName()).getConstantPool());
345 InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg);
346
347 // Checks for the things BCEL does _not_ handle itself.
348 InstructionHandle ih = instructionList.getStart();
349 while (ih != null){
350 Instruction i = ih.getInstruction();
351
352 // An "own" constraint, due to JustIce's new definition of what "subroutine" means.
353 if (i instanceof JsrInstruction){
354 InstructionHandle target = ((JsrInstruction) i).getTarget();
355 if (target == instructionList.getStart()){
356 throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction (such as the very first instruction, which is targeted by instruction '"+ih+"' as its target.");
357 }
358 if (!(target.getInstruction() instanceof ASTORE)){
359 throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else than an ASTORE instruction. Instruction '"+ih+"' targets '"+target+"'.");
360 }
361 }
362
363 // vmspec2, page 134-137
364 ih.accept(v);
365
366 ih = ih.getNext();
367 }
368
369 }
370
371 /*** A small utility method returning if a given int i is in the given int[] ints. */
372 private static boolean contains(int[] ints, int i){
373 for (int j=0; j<ints.length; j++){
374 if (ints[j]==i) return true;
375 }
376 return false;
377 }
378
379 /*** Returns the method number as supplied when instantiating. */
380 public int getMethodNo(){
381 return method_no;
382 }
383
384 /***
385 * This visitor class does the actual checking for the instruction
386 * operand's constraints.
387 */
388 private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor{
389 /*** The ConstantPoolGen instance this Visitor operates on. */
390 private ConstantPoolGen cpg;
391
392 /*** The only Constructor. */
393 InstOperandConstraintVisitor(ConstantPoolGen cpg){
394 this.cpg = cpg;
395 }
396
397 /***
398 * Utility method to return the max_locals value of the method verified
399 * by the surrounding Pass3aVerifier instance.
400 */
401 private int max_locals(){
402 return Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getCode().getMaxLocals();
403 }
404
405 /***
406 * A utility method to always raise an exeption.
407 */
408 private void constraintViolated(Instruction i, String message) {
409 throw new StaticCodeInstructionOperandConstraintException("Instruction "+i+" constraint violated: "+message);
410 }
411
412 /***
413 * A utility method to raise an exception if the index is not
414 * a valid constant pool index.
415 */
416 private void indexValid(Instruction i, int idx){
417 if (idx < 0 || idx >= cpg.getSize()){
418 constraintViolated(i, "Illegal constant pool index '"+idx+"'.");
419 }
420 }
421
422 ///////////////////////////////////////////////////////////
423 // The Java Virtual Machine Specification, pages 134-137 //
424 ///////////////////////////////////////////////////////////
425 /***
426 * Assures the generic preconditions of a LoadClass instance.
427 * The referenced class is loaded and pass2-verified.
428 */
429 public void visitLoadClass(LoadClass o){
430 ObjectType t = o.getLoadClassType(cpg);
431 if (t != null){// null means "no class is loaded"
432 Verifier v = VerifierFactory.getVerifier(t.getClassName());
433 VerificationResult vr = v.doPass1();
434 if (vr.getStatus() != VerificationResult.VERIFIED_OK){
435 constraintViolated((Instruction) o, "Class '"+o.getLoadClassType(cpg).getClassName()+"' is referenced, but cannot be loaded: '"+vr+"'.");
436 }
437 }
438 }
439
440 // The target of each jump and branch instruction [...] must be the opcode [...]
441 // BCEL _DOES_ handle this.
442
443 // tableswitch: BCEL will do it, supposedly.
444
445 // lookupswitch: BCEL will do it, supposedly.
446
447 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
448 // LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model)
449 public void visitLDC(LDC o){
450 indexValid(o, o.getIndex());
451 Constant c = cpg.getConstant(o.getIndex());
452 if (! ( (c instanceof ConstantInteger) ||
453 (c instanceof ConstantFloat) ||
454 (c instanceof ConstantString) ) ){
455 constraintViolated(o, "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '"+c+"'.");
456 }
457 }
458
459 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
460 // LDC2_W
461 public void visitLDC2_W(LDC2_W o){
462 indexValid(o, o.getIndex());
463 Constant c = cpg.getConstant(o.getIndex());
464 if (! ( (c instanceof ConstantLong) ||
465 (c instanceof ConstantDouble) ) ){
466 constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '"+c+"'.");
467 }
468 try{
469 indexValid(o, o.getIndex()+1);
470 }
471 catch(StaticCodeInstructionOperandConstraintException e){
472 throw new AssertionViolatedException("OOPS: Does not BCEL handle that? LDC2_W operand has a problem.");
473 }
474 }
475
476 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
477 //getfield, putfield, getstatic, putstatic
478 public void visitFieldInstruction(FieldInstruction o){
479 indexValid(o, o.getIndex());
480 Constant c = cpg.getConstant(o.getIndex());
481 if (! (c instanceof ConstantFieldref)){
482 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '"+c+"'.");
483 }
484
485 String field_name = o.getFieldName(cpg);
486
487 JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
488 Field[] fields = jc.getFields();
489 Field f = null;
490 for (int i=0; i<fields.length; i++){
491 if (fields[i].getName().equals(field_name)){
492 f = fields[i];
493 break;
494 }
495 }
496 if (f == null){
497 /* TODO: also look up if the field is inherited! */
498 constraintViolated(o, "Referenced field '"+field_name+"' does not exist in class '"+jc.getClassName()+"'.");
499 }
500 else{
501 /* TODO: Check if assignment compatibility is sufficient.
502 What does Sun do? */
503 Type f_type = Type.getType(f.getSignature());
504 Type o_type = o.getType(cpg);
505
506 /* TODO: Is there a way to make BCEL tell us if a field
507 has a void method's signature, i.e. "()I" instead of "I"? */
508
509 if (! f_type.equals(o_type)){
510 constraintViolated(o, "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected.");
511 }
512 /* TODO: Check for access modifiers here. */
513 }
514 }
515
516 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
517 public void visitInvokeInstruction(InvokeInstruction o){
518 indexValid(o, o.getIndex());
519 if ( (o instanceof INVOKEVIRTUAL) ||
520 (o instanceof INVOKESPECIAL) ||
521 (o instanceof INVOKESTATIC) ){
522 Constant c = cpg.getConstant(o.getIndex());
523 if (! (c instanceof ConstantMethodref)){
524 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '"+c+"'.");
525 }
526 else{
527 // Constants are okay due to pass2.
528 ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantMethodref) c).getNameAndTypeIndex()));
529 ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()));
530 if (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME) && (!(o instanceof INVOKESPECIAL)) ){
531 constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
532 }
533 if ( (! (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME)) ) && (cutf8.getBytes().startsWith("<")) ){
534 constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods may be called by the method invocation instructions.");
535 }
536 }
537 }
538 else{ //if (o instanceof INVOKEINTERFACE){
539 Constant c = cpg.getConstant(o.getIndex());
540 if (! (c instanceof ConstantInterfaceMethodref)){
541 constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '"+c+"'.");
542 }
543 // TODO: From time to time check if BCEL allows to detect if the
544 // 'count' operand is consistent with the information in the
545 // CONSTANT_InterfaceMethodref and if the last operand is zero.
546 // By now, BCEL hides those two operands because they're superfluous.
547
548 // Invoked method must not be <init> or <clinit>
549 ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantInterfaceMethodref)c).getNameAndTypeIndex()));
550 String name = ((ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()))).getBytes();
551 if (name.equals(Constants.CONSTRUCTOR_NAME)){
552 constraintViolated(o, "Method to invoke must not be '"+Constants.CONSTRUCTOR_NAME+"'.");
553 }
554 if (name.equals(Constants.STATIC_INITIALIZER_NAME)){
555 constraintViolated(o, "Method to invoke must not be '"+Constants.STATIC_INITIALIZER_NAME+"'.");
556 }
557 }
558
559 // The LoadClassType is the method-declaring class, so we have to check the other types.
560
561 Type t = o.getReturnType(cpg);
562 if (t instanceof ArrayType){
563 t = ((ArrayType) t).getBasicType();
564 }
565 if (t instanceof ObjectType){
566 Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
567 VerificationResult vr = v.doPass2();
568 if (vr.getStatus() != VerificationResult.VERIFIED_OK){
569 constraintViolated(o, "Return type class/interface could not be verified successfully: '"+vr.getMessage()+"'.");
570 }
571 }
572
573 Type[] ts = o.getArgumentTypes(cpg);
574 for (int i=0; i<ts.length; i++){
575 t = ts[i];
576 if (t instanceof ArrayType){
577 t = ((ArrayType) t).getBasicType();
578 }
579 if (t instanceof ObjectType){
580 Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName());
581 VerificationResult vr = v.doPass2();
582 if (vr.getStatus() != VerificationResult.VERIFIED_OK){
583 constraintViolated(o, "Argument type class/interface could not be verified successfully: '"+vr.getMessage()+"'.");
584 }
585 }
586 }
587
588 }
589
590 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
591 public void visitINSTANCEOF(INSTANCEOF o){
592 indexValid(o, o.getIndex());
593 Constant c = cpg.getConstant(o.getIndex());
594 if (! (c instanceof ConstantClass)){
595 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
596 }
597 }
598
599 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
600 public void visitCHECKCAST(CHECKCAST o){
601 indexValid(o, o.getIndex());
602 Constant c = cpg.getConstant(o.getIndex());
603 if (! (c instanceof ConstantClass)){
604 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
605 }
606 }
607
608 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
609 public void visitNEW(NEW o){
610 indexValid(o, o.getIndex());
611 Constant c = cpg.getConstant(o.getIndex());
612 if (! (c instanceof ConstantClass)){
613 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
614 }
615 else{
616 ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant( ((ConstantClass) c).getNameIndex() ));
617 Type t = Type.getType("L"+cutf8.getBytes()+";");
618 if (t instanceof ArrayType){
619 constraintViolated(o, "NEW must not be used to create an array.");
620 }
621 }
622
623 }
624
625 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
626 public void visitMULTIANEWARRAY(MULTIANEWARRAY o){
627 indexValid(o, o.getIndex());
628 Constant c = cpg.getConstant(o.getIndex());
629 if (! (c instanceof ConstantClass)){
630 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
631 }
632 int dimensions2create = o.getDimensions();
633 if (dimensions2create < 1){
634 constraintViolated(o, "Number of dimensions to create must be greater than zero.");
635 }
636 Type t = o.getType(cpg);
637 if (t instanceof ArrayType){
638 int dimensions = ((ArrayType) t).getDimensions();
639 if (dimensions < dimensions2create){
640 constraintViolated(o, "Not allowed to create array with more dimensions ('+dimensions2create+') than the one referenced by the CONSTANT_Class '"+t+"'.");
641 }
642 }
643 else{
644 constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type. [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]");
645 }
646 }
647
648 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
649 public void visitANEWARRAY(ANEWARRAY o){
650 indexValid(o, o.getIndex());
651 Constant c = cpg.getConstant(o.getIndex());
652 if (! (c instanceof ConstantClass)){
653 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '"+c+"'.");
654 }
655 Type t = o.getType(cpg);
656 if (t instanceof ArrayType){
657 int dimensions = ((ArrayType) t).getDimensions();
658 if (dimensions >= 255){
659 constraintViolated(o, "Not allowed to create an array with more than 255 dimensions.");
660 }
661 }
662 }
663
664 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
665 public void visitNEWARRAY(NEWARRAY o){
666 byte t = o.getTypecode();
667 if (! ( (t == Constants.T_BOOLEAN) ||
668 (t == Constants.T_CHAR) ||
669 (t == Constants.T_FLOAT) ||
670 (t == Constants.T_DOUBLE) ||
671 (t == Constants.T_BYTE) ||
672 (t == Constants.T_SHORT) ||
673 (t == Constants.T_INT) ||
674 (t == Constants.T_LONG) ) ){
675 constraintViolated(o, "Illegal type code '+t+' for 'atype' operand.");
676 }
677 }
678
679 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
680 public void visitILOAD(ILOAD o){
681 int idx = o.getIndex();
682 if (idx < 0){
683 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
684 }
685 else{
686 int maxminus1 = max_locals()-1;
687 if (idx > maxminus1){
688 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
689 }
690 }
691 }
692
693 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
694 public void visitFLOAD(FLOAD o){
695 int idx = o.getIndex();
696 if (idx < 0){
697 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
698 }
699 else{
700 int maxminus1 = max_locals()-1;
701 if (idx > maxminus1){
702 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
703 }
704 }
705 }
706
707 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
708 public void visitALOAD(ALOAD o){
709 int idx = o.getIndex();
710 if (idx < 0){
711 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
712 }
713 else{
714 int maxminus1 = max_locals()-1;
715 if (idx > maxminus1){
716 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
717 }
718 }
719 }
720
721 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
722 public void visitISTORE(ISTORE o){
723 int idx = o.getIndex();
724 if (idx < 0){
725 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
726 }
727 else{
728 int maxminus1 = max_locals()-1;
729 if (idx > maxminus1){
730 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
731 }
732 }
733 }
734
735 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
736 public void visitFSTORE(FSTORE o){
737 int idx = o.getIndex();
738 if (idx < 0){
739 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
740 }
741 else{
742 int maxminus1 = max_locals()-1;
743 if (idx > maxminus1){
744 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
745 }
746 }
747 }
748
749 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
750 public void visitASTORE(ASTORE o){
751 int idx = o.getIndex();
752 if (idx < 0){
753 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
754 }
755 else{
756 int maxminus1 = max_locals()-1;
757 if (idx > maxminus1){
758 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
759 }
760 }
761 }
762
763 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
764 public void visitIINC(IINC o){
765 int idx = o.getIndex();
766 if (idx < 0){
767 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
768 }
769 else{
770 int maxminus1 = max_locals()-1;
771 if (idx > maxminus1){
772 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
773 }
774 }
775 }
776
777 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
778 public void visitRET(RET o){
779 int idx = o.getIndex();
780 if (idx < 0){
781 constraintViolated(o, "Index '"+idx+"' must be non-negative.");
782 }
783 else{
784 int maxminus1 = max_locals()-1;
785 if (idx > maxminus1){
786 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-1 '"+maxminus1+"'.");
787 }
788 }
789 }
790
791 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
792 public void visitLLOAD(LLOAD o){
793 int idx = o.getIndex();
794 if (idx < 0){
795 constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
796 }
797 else{
798 int maxminus2 = max_locals()-2;
799 if (idx > maxminus2){
800 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
801 }
802 }
803 }
804
805 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
806 public void visitDLOAD(DLOAD o){
807 int idx = o.getIndex();
808 if (idx < 0){
809 constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
810 }
811 else{
812 int maxminus2 = max_locals()-2;
813 if (idx > maxminus2){
814 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
815 }
816 }
817 }
818
819 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
820 public void visitLSTORE(LSTORE o){
821 int idx = o.getIndex();
822 if (idx < 0){
823 constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
824 }
825 else{
826 int maxminus2 = max_locals()-2;
827 if (idx > maxminus2){
828 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
829 }
830 }
831 }
832
833 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
834 public void visitDSTORE(DSTORE o){
835 int idx = o.getIndex();
836 if (idx < 0){
837 constraintViolated(o, "Index '"+idx+"' must be non-negative. [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]");
838 }
839 else{
840 int maxminus2 = max_locals()-2;
841 if (idx > maxminus2){
842 constraintViolated(o, "Index '"+idx+"' must not be greater than max_locals-2 '"+maxminus2+"'.");
843 }
844 }
845 }
846
847 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
848 public void visitLOOKUPSWITCH(LOOKUPSWITCH o){
849 int[] matchs = o.getMatchs();
850 int max = Integer.MIN_VALUE;
851 for (int i=0; i<matchs.length; i++){
852 if (matchs[i] == max && i != 0){
853 constraintViolated(o, "Match '"+matchs[i]+"' occurs more than once.");
854 }
855 if (matchs[i] < max){
856 constraintViolated(o, "Lookup table must be sorted but isn't.");
857 }
858 else{
859 max = matchs[i];
860 }
861 }
862 }
863
864 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
865 public void visitTABLESWITCH(TABLESWITCH o){
866 // "high" must be >= "low". We cannot check this, as BCEL hides
867 // it from us.
868 }
869
870 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
871 public void visitPUTSTATIC(PUTSTATIC o){
872 String field_name = o.getFieldName(cpg);
873 JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
874 Field[] fields = jc.getFields();
875 Field f = null;
876 for (int i=0; i<fields.length; i++){
877 if (fields[i].getName().equals(field_name)){
878 f = fields[i];
879 break;
880 }
881 }
882 if (f == null){
883 throw new AssertionViolatedException("Field not found?!?");
884 }
885
886 if (f.isFinal()){
887 if (!(myOwner.getClassName().equals(o.getClassType(cpg).getClassName()))){
888 constraintViolated(o, "Referenced field '"+f+"' is final and must therefore be declared in the current class '"+myOwner.getClassName()+"' which is not the case: it is declared in '"+o.getClassType(cpg).getClassName()+"'.");
889 }
890 }
891
892 if (! (f.isStatic())){
893 constraintViolated(o, "Referenced field '"+f+"' is not static which it should be.");
894 }
895
896 String meth_name = Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getName();
897
898 // If it's an interface, it can be set only in <clinit>.
899 if ((!(jc.isClass())) && (!(meth_name.equals(Constants.STATIC_INITIALIZER_NAME)))){
900 constraintViolated(o, "Interface field '"+f+"' must be set in a '"+Constants.STATIC_INITIALIZER_NAME+"' method.");
901 }
902 }
903
904 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
905 public void visitGETSTATIC(GETSTATIC o){
906 String field_name = o.getFieldName(cpg);
907 JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
908 Field[] fields = jc.getFields();
909 Field f = null;
910 for (int i=0; i<fields.length; i++){
911 if (fields[i].getName().equals(field_name)){
912 f = fields[i];
913 break;
914 }
915 }
916 if (f == null){
917 throw new AssertionViolatedException("Field not found?!?");
918 }
919
920 if (! (f.isStatic())){
921 constraintViolated(o, "Referenced field '"+f+"' is not static which it should be.");
922 }
923 }
924
925 /* Checks if the constraints of operands of the said instruction(s) are satisfied. */
926 //public void visitPUTFIELD(PUTFIELD o){
927 // for performance reasons done in Pass 3b
928 //}
929
930 /* Checks if the constraints of operands of the said instruction(s) are satisfied. */
931 //public void visitGETFIELD(GETFIELD o){
932 // for performance reasons done in Pass 3b
933 //}
934
935 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
936 public void visitINVOKEINTERFACE(INVOKEINTERFACE o){
937 // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in,
938 // is therefore resolved/verified.
939 // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified,
940 // too. So are the allowed method names.
941 String classname = o.getClassName(cpg);
942 JavaClass jc = Repository.lookupClass(classname);
943 Method[] ms = jc.getMethods();
944 Method m = null;
945 for (int i=0; i<ms.length; i++){
946 if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
947 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
948 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
949 m = ms[i];
950 break;
951 }
952 }
953 if (m == null){
954 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verfier does allow the method to be declared in some superinterface, which the Java Virtual Machine Specification, Second Edition does not.");
955 }
956 if (jc.isClass()){
957 constraintViolated(o, "Referenced class '"+jc.getClassName()+"' is a class, but not an interface as expected.");
958 }
959 }
960
961 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
962 public void visitINVOKESPECIAL(INVOKESPECIAL o){
963 // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in,
964 // is therefore resolved/verified.
965 // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified,
966 // too. So are the allowed method names.
967 String classname = o.getClassName(cpg);
968 JavaClass jc = Repository.lookupClass(classname);
969 Method[] ms = jc.getMethods();
970 Method m = null;
971 for (int i=0; i<ms.length; i++){
972 if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
973 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
974 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
975 m = ms[i];
976 break;
977 }
978 }
979 if (m == null){
980 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verfier does allow the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not.");
981 }
982
983 JavaClass current = Repository.lookupClass(myOwner.getClassName());
984 if (current.isSuper()){
985
986 if ((Repository.instanceOf( current, jc )) && (!current.equals(jc))){
987
988 if (! (o.getMethodName(cpg).equals(Constants.CONSTRUCTOR_NAME) )){
989 // Special lookup procedure for ACC_SUPER classes.
990
991 int supidx = -1;
992
993 Method meth = null;
994 while (supidx != 0){
995 supidx = current.getSuperclassNameIndex();
996 current = Repository.lookupClass(current.getSuperclassName());
997
998 Method[] meths = current.getMethods();
999 for (int i=0; i<meths.length; i++){
1000 if ( (meths[i].getName().equals(o.getMethodName(cpg))) &&
1001 (Type.getReturnType(meths[i].getSignature()).equals(o.getReturnType(cpg))) &&
1002 (objarrayequals(Type.getArgumentTypes(meths[i].getSignature()), o.getArgumentTypes(cpg))) ){
1003 meth = meths[i];
1004 break;
1005 }
1006 }
1007 if (meth != null) break;
1008 }
1009 if (meth == null){
1010 constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '"+o.getMethodName(cpg)+"' with proper signature not declared in superclass hierarchy.");
1011 }
1012 }
1013 }
1014 }
1015
1016
1017 }
1018
1019 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1020 public void visitINVOKESTATIC(INVOKESTATIC o){
1021 // INVOKESTATIC is a LoadClass; the Class where the referenced method is declared in,
1022 // is therefore resolved/verified.
1023 // INVOKESTATIC is an InvokeInstruction, the argument and return types are resolved/verified,
1024 // too. So are the allowed method names.
1025 String classname = o.getClassName(cpg);
1026 JavaClass jc = Repository.lookupClass(classname);
1027 Method[] ms = jc.getMethods();
1028 Method m = null;
1029 for (int i=0; i<ms.length; i++){
1030 if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
1031 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
1032 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
1033 m = ms[i];
1034 break;
1035 }
1036 }
1037 if (m == null){
1038 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verifier possibly allows the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not.");
1039 }
1040
1041 if (! (m.isStatic())){ // implies it's not abstract, verified in pass 2.
1042 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' has ACC_STATIC unset.");
1043 }
1044
1045 }
1046
1047
1048 /*** Checks if the constraints of operands of the said instruction(s) are satisfied. */
1049 public void visitINVOKEVIRTUAL(INVOKEVIRTUAL o){
1050 // INVOKEVIRTUAL is a LoadClass; the Class where the referenced method is declared in,
1051 // is therefore resolved/verified.
1052 // INVOKEVIRTUAL is an InvokeInstruction, the argument and return types are resolved/verified,
1053 // too. So are the allowed method names.
1054 String classname = o.getClassName(cpg);
1055 JavaClass jc = Repository.lookupClass(classname);
1056 Method[] ms = jc.getMethods();
1057 Method m = null;
1058 for (int i=0; i<ms.length; i++){
1059 if ( (ms[i].getName().equals(o.getMethodName(cpg))) &&
1060 (Type.getReturnType(ms[i].getSignature()).equals(o.getReturnType(cpg))) &&
1061 (objarrayequals(Type.getArgumentTypes(ms[i].getSignature()), o.getArgumentTypes(cpg))) ){
1062 m = ms[i];
1063 break;
1064 }
1065 }
1066 if (m == null){
1067 constraintViolated(o, "Referenced method '"+o.getMethodName(cpg)+"' with expected signature not found in class '"+jc.getClassName()+"'. The native verfier does allow the method to be declared in some superclass or implemented interface, which the Java Virtual Machine Specification, Second Edition does not.");
1068 }
1069 if (! (jc.isClass())){
1070 constraintViolated(o, "Referenced class '"+jc.getClassName()+"' is an interface, but not a class as expected.");
1071 }
1072
1073 }
1074
1075
1076 // WIDE stuff is BCEL-internal and cannot be checked here.
1077
1078 /***
1079 * A utility method like equals(Object) for arrays.
1080 * The equality of the elements is based on their equals(Object)
1081 * method instead of their object identity.
1082 */
1083 private boolean objarrayequals(Object[] o, Object[] p){
1084 if (o.length != p.length){
1085 return false;
1086 }
1087
1088 for (int i=0; i<o.length; i++){
1089 if (! (o[i].equals(p[i])) ){
1090 return false;
1091 }
1092 }
1093
1094 return true;
1095 }
1096
1097 }
1098 }
This page was automatically generated by Maven