1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.verifier.structurals;
18
19
20 import java.io.PrintWriter;
21 import java.io.StringWriter;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Random;
25 import java.util.Vector;
26 import org.apache.bcel.Constants;
27 import org.apache.bcel.Repository;
28 import org.apache.bcel.classfile.JavaClass;
29 import org.apache.bcel.classfile.Method;
30 import org.apache.bcel.generic.ConstantPoolGen;
31 import org.apache.bcel.generic.InstructionHandle;
32 import org.apache.bcel.generic.JsrInstruction;
33 import org.apache.bcel.generic.MethodGen;
34 import org.apache.bcel.generic.ObjectType;
35 import org.apache.bcel.generic.RET;
36 import org.apache.bcel.generic.ReturnInstruction;
37 import org.apache.bcel.generic.ReturnaddressType;
38 import org.apache.bcel.generic.Type;
39 import org.apache.bcel.verifier.PassVerifier;
40 import org.apache.bcel.verifier.VerificationResult;
41 import org.apache.bcel.verifier.Verifier;
42 import org.apache.bcel.verifier.exc.AssertionViolatedException;
43 import org.apache.bcel.verifier.exc.VerifierConstraintViolatedException;
44
45 /***
46 * This PassVerifier verifies a method of class file according to pass 3,
47 * so-called structural verification as described in The Java Virtual Machine
48 * Specification, 2nd edition.
49 * More detailed information is to be found at the do_verify() method's
50 * documentation.
51 *
52 * @version $Id: Pass3bVerifier.java 384759 2006-03-10 10:38:59Z tcurdt $
53 * @author Enver Haase
54 * @see #do_verify()
55 */
56
57 public final class Pass3bVerifier extends PassVerifier{
58
59
60
61
62 /***
63 * An InstructionContextQueue is a utility class that holds
64 * (InstructionContext, ArrayList) pairs in a Queue data structure.
65 * This is used to hold information about InstructionContext objects
66 * externally --- i.e. that information is not saved inside the
67 * InstructionContext object itself. This is useful to save the
68 * execution path of the symbolic execution of the
69 * Pass3bVerifier - this is not information
70 * that belongs into the InstructionContext object itself.
71 * Only at "execute()"ing
72 * time, an InstructionContext object will get the current information
73 * we have about its symbolic execution predecessors.
74 */
75 private static final class InstructionContextQueue{
76 private List ics = new Vector();
77 private List ecs = new Vector();
78 public void add(InstructionContext ic, ArrayList executionChain){
79 ics.add(ic);
80 ecs.add(executionChain);
81 }
82 public boolean isEmpty(){
83 return ics.isEmpty();
84 }
85 public void remove(){
86 this.remove(0);
87 }
88 public void remove(int i){
89 ics.remove(i);
90 ecs.remove(i);
91 }
92 public InstructionContext getIC(int i){
93 return (InstructionContext) ics.get(i);
94 }
95 public ArrayList getEC(int i){
96 return (ArrayList) ecs.get(i);
97 }
98 public int size(){
99 return ics.size();
100 }
101 }
102
103 /*** In DEBUG mode, the verification algorithm is not randomized. */
104 private static final boolean DEBUG = true;
105
106 /*** The Verifier that created this. */
107 private Verifier myOwner;
108
109 /*** The method number to verify. */
110 private int method_no;
111
112 /***
113 * This class should only be instantiated by a Verifier.
114 *
115 * @see org.apache.bcel.verifier.Verifier
116 */
117 public Pass3bVerifier(Verifier owner, int method_no){
118 myOwner = owner;
119 this.method_no = method_no;
120 }
121
122 /***
123 * Whenever the outgoing frame
124 * situation of an InstructionContext changes, all its successors are
125 * put [back] into the queue [as if they were unvisited].
126 * The proof of termination is about the existence of a
127 * fix point of frame merging.
128 */
129 private void circulationPump(ControlFlowGraph cfg, InstructionContext start, Frame vanillaFrame, InstConstraintVisitor icv, ExecutionVisitor ev){
130 final Random random = new Random();
131 InstructionContextQueue icq = new InstructionContextQueue();
132
133 start.execute(vanillaFrame, new ArrayList(), icv, ev);
134
135 icq.add(start, new ArrayList());
136
137
138 while (!icq.isEmpty()){
139 InstructionContext u;
140 ArrayList ec;
141 if (!DEBUG){
142 int r = random.nextInt(icq.size());
143 u = icq.getIC(r);
144 ec = icq.getEC(r);
145 icq.remove(r);
146 }
147 else{
148 u = icq.getIC(0);
149 ec = icq.getEC(0);
150 icq.remove(0);
151 }
152
153 ArrayList oldchain = (ArrayList) (ec.clone());
154 ArrayList newchain = (ArrayList) (ec.clone());
155 newchain.add(u);
156
157 if ((u.getInstruction().getInstruction()) instanceof RET){
158
159
160
161 RET ret = (RET) (u.getInstruction().getInstruction());
162 ReturnaddressType t = (ReturnaddressType) u.getOutFrame(oldchain).getLocals().get(ret.getIndex());
163 InstructionContext theSuccessor = cfg.contextOf(t.getTarget());
164
165
166 InstructionContext lastJSR = null;
167 int skip_jsr = 0;
168 for (int ss=oldchain.size()-1; ss >= 0; ss--){
169 if (skip_jsr < 0){
170 throw new AssertionViolatedException("More RET than JSR in execution chain?!");
171 }
172
173 if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof JsrInstruction){
174 if (skip_jsr == 0){
175 lastJSR = (InstructionContext) oldchain.get(ss);
176 break;
177 }
178 else{
179 skip_jsr--;
180 }
181 }
182 if (((InstructionContext) oldchain.get(ss)).getInstruction().getInstruction() instanceof RET){
183 skip_jsr++;
184 }
185 }
186 if (lastJSR == null){
187 throw new AssertionViolatedException("RET without a JSR before in ExecutionChain?! EC: '"+oldchain+"'.");
188 }
189 JsrInstruction jsr = (JsrInstruction) (lastJSR.getInstruction().getInstruction());
190 if ( theSuccessor != (cfg.contextOf(jsr.physicalSuccessor())) ){
191 throw new AssertionViolatedException("RET '"+u.getInstruction()+"' info inconsistent: jump back to '"+theSuccessor+"' or '"+cfg.contextOf(jsr.physicalSuccessor())+"'?");
192 }
193
194 if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)){
195 icq.add(theSuccessor, (ArrayList) newchain.clone());
196 }
197 }
198 else{
199
200
201 InstructionContext[] succs = u.getSuccessors();
202 for (int s=0; s<succs.length; s++){
203 InstructionContext v = succs[s];
204 if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)){
205 icq.add(v, (ArrayList) newchain.clone());
206 }
207 }
208 }
209
210
211
212 ExceptionHandler[] exc_hds = u.getExceptionHandlers();
213 for (int s=0; s<exc_hds.length; s++){
214 InstructionContext v = cfg.contextOf(exc_hds[s].getHandlerStart());
215
216
217
218
219
220
221
222
223
224
225 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)){
226 icq.add(v, new ArrayList());
227 }
228 }
229
230 }
231
232 InstructionHandle ih = start.getInstruction();
233 do{
234 if ((ih.getInstruction() instanceof ReturnInstruction) && (!(cfg.isDead(ih)))) {
235 InstructionContext ic = cfg.contextOf(ih);
236 Frame f = ic.getOutFrame(new ArrayList());
237 LocalVariables lvs = f.getLocals();
238 for (int i=0; i<lvs.maxLocals(); i++){
239 if (lvs.get(i) instanceof UninitializedObjectType){
240 this.addMessage("Warning: ReturnInstruction '"+ic+"' may leave method with an uninitialized object in the local variables array '"+lvs+"'.");
241 }
242 }
243 OperandStack os = f.getStack();
244 for (int i=0; i<os.size(); i++){
245 if (os.peek(i) instanceof UninitializedObjectType){
246 this.addMessage("Warning: ReturnInstruction '"+ic+"' may leave method with an uninitialized object on the operand stack '"+os+"'.");
247 }
248 }
249 }
250 }while ((ih = ih.getNext()) != null);
251
252 }
253
254 /***
255 * Pass 3b implements the data flow analysis as described in the Java Virtual
256 * Machine Specification, Second Edition.
257 * Later versions will use LocalVariablesInfo objects to verify if the
258 * verifier-inferred types and the class file's debug information (LocalVariables
259 * attributes) match [TODO].
260 *
261 * @see org.apache.bcel.verifier.statics.LocalVariablesInfo
262 * @see org.apache.bcel.verifier.statics.Pass2Verifier#getLocalVariablesInfo(int)
263 */
264 public VerificationResult do_verify(){
265 if (! myOwner.doPass3a(method_no).equals(VerificationResult.VR_OK)){
266 return VerificationResult.VR_NOTYET;
267 }
268
269
270
271 JavaClass jc;
272 try {
273 jc = Repository.lookupClass(myOwner.getClassName());
274 } catch (ClassNotFoundException e) {
275
276 throw new AssertionViolatedException("Missing class: " + e.toString());
277 }
278
279 ConstantPoolGen constantPoolGen = new ConstantPoolGen(jc.getConstantPool());
280
281 InstConstraintVisitor icv = new InstConstraintVisitor();
282 icv.setConstantPoolGen(constantPoolGen);
283
284 ExecutionVisitor ev = new ExecutionVisitor();
285 ev.setConstantPoolGen(constantPoolGen);
286
287 Method[] methods = jc.getMethods();
288
289 try{
290
291 MethodGen mg = new MethodGen(methods[method_no], myOwner.getClassName(), constantPoolGen);
292
293 icv.setMethodGen(mg);
294
295
296 if (! (mg.isAbstract() || mg.isNative()) ){
297
298 ControlFlowGraph cfg = new ControlFlowGraph(mg);
299
300
301 Frame f = new Frame(mg.getMaxLocals(),mg.getMaxStack());
302 if ( !mg.isStatic() ){
303 if (mg.getName().equals(Constants.CONSTRUCTOR_NAME)){
304 Frame._this = new UninitializedObjectType(new ObjectType(jc.getClassName()));
305 f.getLocals().set(0, Frame._this);
306 }
307 else{
308 Frame._this = null;
309 f.getLocals().set(0, new ObjectType(jc.getClassName()));
310 }
311 }
312 Type[] argtypes = mg.getArgumentTypes();
313 int twoslotoffset = 0;
314 for (int j=0; j<argtypes.length; j++){
315 if (argtypes[j] == Type.SHORT || argtypes[j] == Type.BYTE || argtypes[j] == Type.CHAR || argtypes[j] == Type.BOOLEAN){
316 argtypes[j] = Type.INT;
317 }
318 f.getLocals().set(twoslotoffset + j + (mg.isStatic()?0:1), argtypes[j]);
319 if (argtypes[j].getSize() == 2){
320 twoslotoffset++;
321 f.getLocals().set(twoslotoffset + j + (mg.isStatic()?0:1), Type.UNKNOWN);
322 }
323 }
324 circulationPump(cfg, cfg.contextOf(mg.getInstructionList().getStart()), f, icv, ev);
325 }
326 }
327 catch (VerifierConstraintViolatedException ce){
328 ce.extendMessage("Constraint violated in method '"+methods[method_no]+"':\n","");
329 return new VerificationResult(VerificationResult.VERIFIED_REJECTED, ce.getMessage());
330 }
331 catch (RuntimeException re){
332
333
334 StringWriter sw = new StringWriter();
335 PrintWriter pw = new PrintWriter(sw);
336 re.printStackTrace(pw);
337
338 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");
339 }
340 return VerificationResult.VR_OK;
341 }
342
343 /*** Returns the method number as supplied when instantiating. */
344 public int getMethodNo(){
345 return method_no;
346 }
347 }