1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
|
/*
* Copyright 2000-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.bcel.verifier.structurals;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.GETFIELD;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.JsrInstruction;
import org.apache.bcel.generic.LoadInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.RET;
import org.apache.bcel.generic.ReturnInstruction;
import org.apache.bcel.generic.ReturnaddressType;
import org.apache.bcel.generic.Type;
import org.apache.bcel.verifier.PassVerifier;
import org.apache.bcel.verifier.VerificationResult;
import org.apache.bcel.verifier.Verifier;
import org.apache.bcel.verifier.exc.AssertionViolatedException;
import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
import org.apache.bcel.verifier.exc.VerifierConstraintViolatedException;
/**
* This PassVerifier verifies a method of class file according to pass 3,
* so-called structural verification as described in The Java Virtual Machine
* Specification, 2nd edition.
* More detailed information is to be found at the do_verify() method's
* documentation.
*
* @version $Id: ModifiedPass3bVerifier.java,v 1.3 2007-11-08 17:22:56 ebruneton Exp $
* @author Enver Haase
* @see #do_verify()
*/
public final class ModifiedPass3bVerifier extends PassVerifier{
/* TODO: Throughout pass 3b, upper halves of LONG and DOUBLE
are represented by Type.UNKNOWN. This should be changed
in favour of LONG_Upper and DOUBLE_Upper as in pass 2. */
/**
* An InstructionContextQueue is a utility class that holds
* (InstructionContext, ArrayList) pairs in a Queue data structure.
* This is used to hold information about InstructionContext objects
* externally --- i.e. that information is not saved inside the
* InstructionContext object itself. This is useful to save the
* execution path of the symbolic execution of the
* Pass3bVerifier - this is not information
* that belongs into the InstructionContext object itself.
* Only at "execute()"ing
* time, an InstructionContext object will get the current information
* we have about its symbolic execution predecessors.
*/
private static final class InstructionContextQueue{
private List ics = new Vector(); // Type: InstructionContext
private List ecs = new Vector(); // Type: ArrayList (of InstructionContext)
public void add(InstructionContext ic, ArrayList executionChain){
ics.add(ic);
ecs.add(executionChain);
}
public boolean isEmpty(){
return ics.isEmpty();
}
public void remove(){
this.remove(0);
}
public void remove(int i){
ics.remove(i);
ecs.remove(i);
}
public InstructionContext getIC(int i){
return (InstructionContext) ics.get(i);
}
public ArrayList getEC(int i){
return (ArrayList) ecs.get(i);
}
public int size(){
return ics.size();
}
} // end Inner Class InstructionContextQueue
/** In DEBUG mode, the verification algorithm is not randomized. */
private static final boolean DEBUG = true;
private JavaClass jc;
/** The method number to verify. */
private int method_no;
/**
* This class should only be instantiated by a Verifier.
*
* @see org.apache.bcel.verifier.Verifier
*/
public ModifiedPass3bVerifier(JavaClass jc, int method_no){
this.jc = jc;
this.method_no = method_no;
}
/**
* Whenever the outgoing frame
* situation of an InstructionContext changes, all its successors are
* put [back] into the queue [as if they were unvisited].
* The proof of termination is about the existence of a
* fix point of frame merging.
*/
private void circulationPump(MethodGen m,ControlFlowGraph cfg, InstructionContext start, Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev){
final Random random = new Random();
InstructionContextQueue icq = new InstructionContextQueue();
start.execute(vanillaFrame, new ArrayList(), icv, ev); // new ArrayList() <=> no Instruction was executed before
// => Top-Level routine (no jsr call before)
icq.add(start, new ArrayList());
// LOOP!
while (!icq.isEmpty()){
InstructionContext u;
ArrayList ec;
if (!DEBUG){
int r = random.nextInt(icq.size());
u = icq.getIC(r);
ec = icq.getEC(r);
icq.remove(r);
}
else{
u = icq.getIC(0);
ec = icq.getEC(0);
icq.remove(0);
}
ArrayList oldchain = (ArrayList) (ec.clone());
ArrayList newchain = (ArrayList) (ec.clone());
newchain.add(u);
if ((u.getInstruction().getInstruction()) instanceof RET){
//System.err.println(u);
// We can only follow _one_ successor, the one after the
// JSR that was recently executed.
RET ret = (RET) (u.getInstruction().getInstruction());
ReturnaddressType t = (ReturnaddressType) u.getOutFrame(oldchain).getLocals().get(ret.getIndex());
InstructionContext theSuccessor = cfg.contextOf(t.getTarget());
// Sanity check
InstructionContext lastJSR = null;
int skip_jsr = 0;
for (int ss=oldchain.size()-1; ss >= 0; ss--){
if (skip_jsr < 0){
throw new AssertionViolatedException("More RET than JSR in execution chain?!");
}
//System.err.println("+"+oldchain.get(ss));
if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof JsrInstruction){
if (skip_jsr == 0){
lastJSR = (InstructionContext) oldchain.get(ss);
break;
}
else{
skip_jsr--;
}
}
if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof RET){
skip_jsr++;
}
}
if (lastJSR == null){
throw new AssertionViolatedException("RET without a JSR before in ExecutionChain?! EC: '"+oldchain+"'.");
}
JsrInstruction jsr = (JsrInstruction) (lastJSR.getInstruction().getInstruction());
if ( theSuccessor != (cfg.contextOf(jsr.physicalSuccessor())) ){
throw new AssertionViolatedException("RET '"+u.getInstruction()+"' info inconsistent: jump back to '"+theSuccessor+"' or '"+cfg.contextOf(jsr.physicalSuccessor())+"'?");
}
if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)){
icq.add(theSuccessor, (ArrayList) newchain.clone());
}
}
else{// "not a ret"
// Normal successors. Add them to the queue of successors.
InstructionContext[] succs = u.getSuccessors();
for (int s=0; s<succs.length; s++){
InstructionContext v = succs[s];
if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)){
icq.add(v, (ArrayList) newchain.clone());
}
}
}// end "not a ret"
// Exception Handlers. Add them to the queue of successors.
// [subroutines are never protected; mandated by JustIce]
ExceptionHandler[] exc_hds = u.getExceptionHandlers();
for (int s=0; s<exc_hds.length; s++){
InstructionContext v = cfg.contextOf(exc_hds[s].getHandlerStart());
// TODO: the "oldchain" and "newchain" is used to determine the subroutine
// we're in (by searching for the last JSR) by the InstructionContext
// implementation. Therefore, we should not use this chain mechanism
// when dealing with exception handlers.
// Example: a JSR with an exception handler as its successor does not
// mean we're in a subroutine if we go to the exception handler.
// We should address this problem later; by now we simply "cut" the chain
// by using an empty chain for the exception handlers.
//if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(), new OperandStack (u.getOutFrame().getStack().maxStack(), (exc_hds[s].getExceptionType()==null? Type.THROWABLE : exc_hds[s].getExceptionType())) ), newchain), icv, ev){
//icq.add(v, (ArrayList) newchain.clone());
if (v.execute(new Frame(u.getOutFrame(oldchain).getLocals(), new OperandStack (u.getOutFrame(oldchain).getStack().maxStack(), (exc_hds[s].getExceptionType()==null? Type.THROWABLE : exc_hds[s].getExceptionType())) ), new ArrayList(), icv, ev)){
icq.add(v, new ArrayList());
}
}
}// while (!icq.isEmpty()) END
InstructionHandle ih = start.getInstruction();
do{
if ((ih.getInstruction() instanceof ReturnInstruction) && (!(cfg.isDead(ih)))) {
InstructionContext ic = cfg.contextOf(ih);
Frame f = ic.getOutFrame(new ArrayList()); // TODO: This is buggy, we check only the top-level return instructions this way. Maybe some maniac returns from a method when in a subroutine?
LocalVariables lvs = f.getLocals();
for (int i=0; i<lvs.maxLocals(); i++){
if (lvs.get(i) instanceof UninitializedObjectType){
this.addMessage("Warning: ReturnInstruction '"+ic+"' may leave method with an uninitialized object in the local variables array '"+lvs+"'.");
}
}
OperandStack os = f.getStack();
for (int i=0; i<os.size(); i++){
if (os.peek(i) instanceof UninitializedObjectType){
this.addMessage("Warning: ReturnInstruction '"+ic+"' may leave method with an uninitialized object on the operand stack '"+os+"'.");
}
}
//see JVM $4.8.2
//TODO implement all based on stack
Type returnedType = null;
if( ih.getPrev().getInstruction() instanceof InvokeInstruction )
{
returnedType = ((InvokeInstruction)ih.getPrev().getInstruction()).getType(m.getConstantPool());
}
if( ih.getPrev().getInstruction() instanceof LoadInstruction )
{
int index = ((LoadInstruction)ih.getPrev().getInstruction()).getIndex();
returnedType = lvs.get(index);
}
if( ih.getPrev().getInstruction() instanceof GETFIELD )
{
returnedType = ((GETFIELD)ih.getPrev().getInstruction()).getType(m.getConstantPool());
}
if( returnedType != null )
{
if( returnedType instanceof ObjectType )
{
try
{
if( !((ObjectType)returnedType).isAssignmentCompatibleWith(m.getReturnType()) )
{
throw new StructuralCodeConstraintException("Returned type "+returnedType+" does not match Method's return type "+m.getReturnType());
}
}
catch (ClassNotFoundException e)
{
//dont know what do do now, so raise RuntimeException
throw new RuntimeException(e);
}
}
else if( !returnedType.equals(m.getReturnType()) )
{
throw new StructuralCodeConstraintException("Returned type "+returnedType+" does not match Method's return type "+m.getReturnType());
}
}
}
}while ((ih = ih.getNext()) != null);
}
/**
* Pass 3b implements the data flow analysis as described in the Java Virtual
* Machine Specification, Second Edition.
* Later versions will use LocalVariablesInfo objects to verify if the
* verifier-inferred types and the class file's debug information (LocalVariables
* attributes) match [TODO].
*
* @see org.apache.bcel.verifier.statics.LocalVariablesInfo
* @see org.apache.bcel.verifier.statics.Pass2Verifier#getLocalVariablesInfo(int)
*/
public VerificationResult do_verify(){
/*if (! myOwner.doPass3a(method_no).equals(VerificationResult.VR_OK)){
return VerificationResult.VR_NOTYET;
}
// Pass 3a ran before, so it's safe to assume the JavaClass object is
// in the BCEL repository.
JavaClass jc;
try {
jc = Repository.lookupClass(myOwner.getClassName());
} catch (ClassNotFoundException e) {
// FIXME: maybe not the best way to handle this
throw new AssertionViolatedException("Missing class: " + e.toString());
}*/
ConstantPoolGen constantPoolGen = new ConstantPoolGen(jc.getConstantPool());
// Init Visitors
InstConstraintVisitor icv = new InstConstraintVisitor();
icv.setConstantPoolGen(constantPoolGen);
ExecutionVisitor ev = new ExecutionVisitor();
ev.setConstantPoolGen(constantPoolGen);
Method[] methods = jc.getMethods(); // Method no "method_no" exists, we ran Pass3a before on it!
try{
MethodGen mg = new MethodGen(methods[method_no], jc.getClassName(), constantPoolGen);
icv.setMethodGen(mg);
////////////// DFA BEGINS HERE ////////////////
if (! (mg.isAbstract() || mg.isNative()) ){ // IF mg HAS CODE (See pass 2)
ControlFlowGraph cfg = new ControlFlowGraph(mg);
// Build the initial frame situation for this method.
Frame f = new Frame(mg.getMaxLocals(),mg.getMaxStack());
if ( !mg.isStatic() ){
if (mg.getName().equals(Constants.CONSTRUCTOR_NAME)){
Frame._this = new UninitializedObjectType(new ObjectType(jc.getClassName()));
f.getLocals().set(0, Frame._this);
}
else{
Frame._this = null;
f.getLocals().set(0, new ObjectType(jc.getClassName()));
}
}
Type[] argtypes = mg.getArgumentTypes();
int twoslotoffset = 0;
for (int j=0; j<argtypes.length; j++){
if (argtypes[j] == Type.SHORT || argtypes[j] == Type.BYTE || argtypes[j] == Type.CHAR || argtypes[j] == Type.BOOLEAN){
argtypes[j] = Type.INT;
}
f.getLocals().set(twoslotoffset + j + (mg.isStatic()?0:1), argtypes[j]);
if (argtypes[j].getSize() == 2){
twoslotoffset++;
f.getLocals().set(twoslotoffset + j + (mg.isStatic()?0:1), Type.UNKNOWN);
}
}
circulationPump(mg,cfg, cfg.contextOf(mg.getInstructionList().getStart()), f, icv, ev);
}
}
catch (VerifierConstraintViolatedException ce){
ce.extendMessage("Constraint violated in method '"+methods[method_no]+"':\n","");
return new VerificationResult(VerificationResult.VERIFIED_REJECTED, ce.getMessage());
}
catch (RuntimeException re){
// These are internal errors
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
re.printStackTrace(pw);
throw new AssertionViolatedException("Some RuntimeException occured while verify()ing class '"+jc.getClassName()+"', method '"+methods[method_no]+"'. Original RuntimeException's stack trace:\n---\n"+sw+"---\n");
}
return VerificationResult.VR_OK;
}
/** Returns the method number as supplied when instantiating. */
public int getMethodNo(){
return method_no;
}
}
|