package fr.lifl.stc.stan.analyser;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import java.util.Hashtable;
import java.util.Vector;
import java.util.HashMap;
import java.util.TreeMap;
import org.apache.bcel.SignatureConstants;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.ProofAttribute;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.FieldInstruction;
import fr.lifl.stc.stan.annotation.*;
import fr.lifl.stc.stan.execution.Frame;
import fr.lifl.stc.stan.execution.InstructionType;
import fr.lifl.stc.stan.execution.Interpreter;
import fr.lifl.stc.stan.execution.MethodInfo;
import fr.lifl.stc.stan.implicitFlow.ImplicitDependences;
import fr.lifl.stc.stan.implicitFlow.data.BooleanExpression;
import fr.lifl.stc.stan.implicitFlow.data.BooleanVariable;
import fr.lifl.stc.stan.implicitFlow.execution.InterpreterIF;
import fr.lifl.stc.stan.implicitFlow.execution.MethodInfoIF;
import fr.lifl.stc.stan.policy.Policy;
import fr.lifl.stc.stan.samples.escape.EscapeAnnotation;
import fr.lifl.stc.stan.samples.flow.FlowAnnotation;
import fr.lifl.stc.stan.signature.*;
import fr.lifl.stc.stan.util.Printer;
import fr.lifl.stc.stan.dsl.FlowChecker;
import fr.lifl.stc.stan.dsl.FlowCheckerException;
import fr.lifl.stc.stan.dsl.Flow;
import fr.lifl.stc.stan.samples.flow.FlowPartialSignature;
import fr.lifl.stc.stan.samples.flow.FlowPartialSignatureData;
import fr.lifl.stc.stan.link.TableLink;
import fr.lifl.stc.stan.link.Link;
import fr.lifl.stc.stan.link.defaultLink.DefaultLink;
import org.apache.bcel.generic.ReturnInstruction;
/**
* @author Dorina
*
* This class provides an algorithm to perform a static analysis a method of a '.class' file.
*
*/
public class MethodAnalyzer {
/**
* the Java class to wich the analysed belongs
*/
private JavaClass _class;
private ClassAnalyser classAnalyser;
/**
* contant pool of the class
*/
private ConstantPoolGen cpg;
/**
* The method to wich the analyse applies
*/
private Method _method;
/**
* Array of Annotations vector (one vector per instruction)
*/
private Annotation annotations[];
/**
* A global dictionary that contains all method signature.
*/
private SignatureDictionary dictionary;
private Policy policy;
private FlowChecker flowchecker; /** Checker for dsl policies */
private MethodInfo methodInfo;
private Interpreter interpreter;
private InterpreterIF interpreterIF;
private MethodInfoIF methodInfoIF;
private ImplicitDependences implicitDependences;
/**
* label bytecode (int) => Frame
*/
private TreeMap> labels = new TreeMap>();
/**
* stats for label bytecodes
* label(int) => number of passes (int)
*/
Hashtable statsLabels = new Hashtable();
//public static int stat = 0;
private int proof_index;
////////////////////////////////////////////////////////////////
//// Constructor
////////////////////////////////////////////////////////////////
/**
*
*/
/**
* Creates a new MethodAnalyser
instance.
*
* @param dict
* A global dictionary that contains all methods signatures.
* @param pol
* The security policy
* @param cpg
* The constant pool generator
* @param c
* The class to witch m belongs
* @param m
* The method to analyse
* @param proof_index
* The index for the proof
attribute, when adding annotations to .class
file
*
*/
public MethodAnalyzer(SignatureDictionary dict, Policy pol, FlowChecker fc,
ConstantPoolGen cpg, JavaClass c, Method m, ClassAnalyser ca, int proof_index) {
this.policy = pol;
this.flowchecker = fc;
this.setDictionary(dict);
this._class = c;
this._method = m;
this.setCpg(cpg);
this.classAnalyser = ca;
this.methodInfo = new MethodInfo(this);
this.interpreter = new Interpreter(this);
this.proof_index = proof_index;
// plop
//methodInfoIF = new MethodInfoIF(this);
//this.implicitDependences = new ImplicitDependences(_method,methodInfoIF.getConditions());
//interpreterIF = new InterpreterIF(this);
this.initAnnotations();
this.initLabels();
Stats.sizeOfProof += statsLabels.size()
* (interpreter.getMethodInfo().getReferences().length + interpreter
.getMethodInfo().getValues().length);
}
/**
* Initialization of the annotations vector array
*
*/
private void initAnnotations() {
//FIXME depends on options
annotations = new Annotation[2];
annotations[0] = new FlowAnnotation(interpreter.getRefValues(),
interpreter.getMethodInfo());
annotations[1] = new EscapeAnnotation(interpreter.getRefs(),
interpreter.getMethodInfo());
}
/**
* Initiates labels
with labels bytecode
*
*/
private void initLabels() {
InstructionList i_list;
i_list = new InstructionList(_method.getCode().getCode());
Iterator it = i_list.iterator();
while (it.hasNext()) {
InstructionHandle ih = it.next();
if (ih.hasTargeters()) {
labels.put(new Integer(ih.getPosition()), new Vector(0));
} else if (InstructionType.getType(ih.getInstruction()) == InstructionType.JSR_INSTRUCTION) {
labels.put(new Integer(ih.getPosition()), new Vector(0));
}
}
//Add exception handlers to labels table
CodeException[] handlers = _method.getCode().getExceptionTable();
for (int i = 0; i < handlers.length; i++) {
labels.put(new Integer(handlers[i].getHandlerPC()), new Vector(0));
}
Iterator it2 = labels.keySet().iterator();
while (it2.hasNext()){
Integer lab= it2.next();
statsLabels.put(lab, new Integer(0));
}
}
/**
* increments number of passes for label pos
* @param pos
*/
private void incStat(int pos) {
Integer posi = new Integer(pos);
int i = statsLabels.get(posi).intValue();
i++;
statsLabels.put(posi, new Integer(i));
}
/**
* Merges the frame for bytecote pos
with the frame f
* @param pos
* @param f
* @return
*/
private boolean mergeFrameAtLabel(int pos, Frame f) {
if (!labels.containsKey(new Integer(pos)))
return true;
Integer posi = new Integer(pos);
Vector v = labels.get(posi);
boolean updated = false;
if (v.size() == 0) {
System.out.println("new frame");
v.add((Frame) f.clone());
updated = true;
} else {
System.out.println("merge frame");
Frame f0 = v.elementAt(0);
updated = f0.merge(f);
}
return updated;
}
/**
*
* @param pos
* The bytecode position to be verified
* @return
* true
if the bytecode at position pos
is a target bytecode,
* false
otherwise
*/
private boolean isLabelOrHandler(int pos) {
return labels.containsKey(new Integer(pos));
}
/**
* Returns the frame corresponding to bytecode at position pos
pos. If pos
* is not a label bytecode, it returns null
*/
private Frame getFrameForPos(int pos) {
Vector v = labels.get(new Integer(pos));
if (v != null && v.size() > 0)
return v.elementAt(0);
return null;
}
////////////////////////////////////////////////////////////////
//// Analyse
////////////////////////////////////////////////////////////////
/**
*
* Follows the control flow of the method
* Algorithm:
* Initialization of the control stack
* do
* get the instruction to execute on top of stack
* get the instruction previously executed on top of stack
* update the annotation vector of the current instruction
* execute the instruction
* get the next possible instruction after the current one
*
* if some changes has been brougth to annotations, stack, static world
* or local variables (variable and parameter)
* for each of these instruction do
* push on top of stack the current instruction
* push on top of stack the next instruction
* done
* end if
* while the stack is not empty
*/
public void analyse() {
if (_method.isNative()) {
System.err.println("[Warning] " + _method + " is native");
return;
}
Stats.aproxMemory(labels.size(), this.methodInfo.getJvmValues().length);
Stack control = new Stack();
control.push(interpreter.getStart());
Frame currentFrame = null;
if (isLabelOrHandler(interpreter.getStart().getPosition())) {
this.mergeFrameAtLabel(interpreter.getStart().getPosition(),
this.interpreter.getDefaultFrame());
} else {
currentFrame = this.interpreter.getDefaultFrame();
}
TableLink checkedlinks = new TableLink(30, 30); //FIXME
Frame prev_frame= (Frame)currentFrame.clone();
InstructionHandle currentHandle = null;
while (!control.isEmpty()) {
currentHandle = control.pop();
/**
* get current frame
*/
if (isLabelOrHandler(currentHandle.getPosition())) {
System.out.println("label or handler spotted");
prev_frame = (Frame)currentFrame.clone();
currentFrame = null;
currentFrame = (Frame) (this.getFrameForPos(currentHandle.getPosition()).clone());
incStat(currentHandle.getPosition());
}
/**
* Mod starts here
*/
// System.out.println(">> " + currentHandle + " <<");
// System.out.println(currentFrame);
TableLink newtbl = ((FlowAnnotation)annotations[0]).link(currentFrame);
for(int i=0; i it;
ArrayList list = interpreter.getNextExceptionHandlers(currentHandle);
if (list.size() > 0) {
Frame ex = (Frame) currentFrame.clone();
ex.getStack().clear();
ex.getStack().push(ex.getException());
//ex.makePointTo(ex.getStaticWorld(),ex.getException());
it = list.iterator();
while (it.hasNext()) {
InstructionHandle next = it.next();
boolean modif = this.mergeFrameAtLabel(next.getPosition(),
ex);
if ((updated || modif) && !control.contains(next)) {
control.push(next);
}
}
}
/**
* process next instructions
*/
it = interpreter.getNextInstructions(currentHandle, currentFrame).iterator();
while (it.hasNext()) {
InstructionHandle next = it.next();
boolean modif = this.mergeFrameAtLabel(next.getPosition(),
currentFrame);
if ((updated || modif) && !control.contains(next)) {
control.push(next);
}
}
}
if (Config.statisticsEnabled()) {
Stats.stats(this);
String st = Stats.statsToString(this);
if (!st.equals("")){
//Stats.println(st);
}
}
Signature sign = new Signature();
boolean updated = false;
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].getPartialSignature() != null) {
sign.add(annotations[i].getPartialSignature());
//System.out.println(">>" + annotations[i] + "<<");
updated = true;
}
}
if (updated) {
//System.out.println("coin : " + sign);
String key = SignatureDictionary.key(_class, _method);
dictionary.putSignature(key, sign, interpreter.isFinalSignature());
}
}
/**
* Computes the proof annotation to be embedded with the code , in order to ease the online verification.
* The proof associates to each label bytecode its corresponding frame.
*
* format
* codification length_of_jvmType_tablearray_of_proofs_length array_of_proofs
*
* array_of_proof contains elements of form:
* label size_of_proof(in bytes) proof
*
* where proof is the codification of a frame
*
* The labels are order, to facilitate the oncard algorithm
* @return
* The proof annotation to be embedded with the code , in order to ease the online verification
*/
public byte[] getProof() {
byte[] output = null;
//no proof if there are no labels
/* if (labels.size() == 0)
return null;
*/
int length = 0;
Frame fr;
Vector v;
Integer key;
byte codification = this.getProofCodification();
//add length at the begining?
//compute length
length += 1;
length += SignatureConstants.LABEL_BYTECODE_SIZE;
length += SignatureConstants.LABEL_BYTECODE_SIZE;
Iterator keys = labels.keySet().iterator();
Frame tmp = null;
for (; keys.hasNext();) {
key = keys.next();
v = labels.get(key);
length += SignatureConstants.LABEL_BYTECODE_SIZE;
if (v.size() > 0) {
fr = v.elementAt(0);
byte[] arr = fr.toBytes(codification, tmp);
length += arr.length;
tmp = fr;
}
else {
length += SignatureConstants.TOTAL_LENGTH_SIZE;
tmp = null;
}
}
//construct output
output = new byte[length];
int index = 0;
output[index++] = codification;
byte[] arrSize = SignatureConstants.toByteArray( this.interpreter.refValuesLength ,
SignatureConstants.LABEL_BYTECODE_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
arrSize = SignatureConstants.toByteArray(labels.size(),
SignatureConstants.LABEL_BYTECODE_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
keys = labels.keySet().iterator();
tmp = null;
for (; keys.hasNext();) {
key = keys.next();
v = labels.get(key);
//set label
arrSize = SignatureConstants.toByteArray(key.intValue(),
SignatureConstants.LABEL_BYTECODE_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
byte[] arr = null;
if (v.size() > 0) {
fr = v.elementAt(0);
//compute frame
arr = fr.toBytes(codification, tmp);
tmp = fr;
}
else {
arr = new byte[SignatureConstants.TOTAL_LENGTH_SIZE];
tmp = null;
}
System.arraycopy(arr, 0, output, index, arr.length);
index += arr.length;
}
return output;
}
/**
* Returns to codification to be used in order to codify the proof
* @return
*
*/
private byte getProofCodification() {
int length = this.interpreter.getRefValues().length
+ this.interpreter.getAddresses().length;
if (length <= 63)
return SignatureConstants.CODIF_6B;
if (length <= 255)
return SignatureConstants.CODIF_8B;
if (length <= 16384)
return SignatureConstants.CODIF_14B;
return SignatureConstants.ERROR_CODIFICATION;
}
/**
*
* annotates the .class with the proof (the frame of each jump target)
*
*/
public void annotateProof() {
byte[] byteStream = this.getProof();
if (byteStream == null) {
return;
}
Attribute[] orig = this._method.getAttributes();
if (byteStream.length > 0) {
ProofAttribute linkAttr = new ProofAttribute(proof_index,
byteStream.length, byteStream, _class.getConstantPool());
int j = 0;
while (j < orig.length) {
if (orig[j] instanceof ProofAttribute)
break;
j++;
}
if (j < orig.length) {
orig[j] = linkAttr;
_method.setAttributes(orig);
} else {
Attribute[] new_attr = new Attribute[orig.length + 1];
new_attr[0] = linkAttr;
System.arraycopy(orig, 0, new_attr, 1, orig.length);
_method.setAttributes(new_attr);
}
} else {
//suppress the attribute
int j = 0;
while (j < orig.length) {
if (orig[j] instanceof ProofAttribute) {
break;
}
j++;
}
if (j < orig.length) {
Attribute[] new_attr = new Attribute[orig.length - 1];
if (j > 0)
System.arraycopy(orig, 0, new_attr, 0, j);
if (j < orig.length - 1)
System.arraycopy(orig, j + 1, new_attr, j, orig.length - j
- 1);
_method.setAttributes(new_attr);
}
}
}
public void addExternalMethod(InvokeInstruction i, int signType) {
this.classAnalyser.addExternalMethod(i, signType);
}
public void addExternalField(FieldInstruction i) {
this.classAnalyser.addExternalField(i);
}
////////////////////////////////////////////////////////////////
//// toString
////////////////////////////////////////////////////////////////
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "MethodAnalyser.toString() not yet implemented\n";
}
/////////////////////////////////////////////////////////////
//IMPLICIT FLOW
/////////////////////////////////////////////////////////////
public void analyseImplicitFlow() {
if (_method.isNative()) {
System.err.println("Warning: " + _method + " is native");
return;
}
Printer.println("Analyzing method " + _method);
Stack control = new Stack();
control.push(interpreterIF.getStart());
this.implicitDependences.set(interpreterIF.getStart().getPosition(),
new BooleanExpression(BooleanVariable.condition_true));
InstructionHandle currentHandle = null;
while (!control.isEmpty()) {
currentHandle = control.pop();
//Printer.println("pop from control stack : "+currentHandle);
/*
ArrayList list = interpreterIF.getNextExceptionHandlers(currentHandle);
if (list.size() > 0) {
Frame ex = (Frame)currentFrame.clone();
ex.getStack().clear();
ex.getStack().push(ex.getException());
//ex.makePointTo(ex.getStaticWorld(),ex.getException());
it = list.iterator();
while (it.hasNext()) {
InstructionHandle next = (InstructionHandle) it.next();
boolean modif = this.mergeFrameAtLabel(next.getPosition(), ex);
if( (updated || modif) && !control.contains(next)){
//Printer.println("push exception "+next);
control.push(next);
}
}
}
*/
HashMap succ = interpreterIF.getNextInstructions(currentHandle);
Iterator it = succ.keySet().iterator();
while (it.hasNext()) {
InstructionHandle next = it.next();
BooleanExpression condition = succ
.get(next);
boolean modif = interpreterIF.abstractExecutionStep(
currentHandle, next, condition);
if (modif && !control.contains(next)) {
control.push(next);
}
}
}
Printer.println(this.implicitDependences.toString());
}
public MethodInfo getMethodInfo() {
return this.methodInfo;
}
public MethodInfoIF getMethodInfoIF() {
return this.methodInfoIF;
}
public ImplicitDependences getImplicitDeps() {
return this.implicitDependences;
}
////////////////////////////////////////
//// IMPLICIT FLOW - END
////////////////////////////////////////
/**
* @param cpg The cpg to set.
*/
private void setCpg(ConstantPoolGen cpg) {
this.cpg = cpg;
}
/**
* @return Returns the cpg.
*/
public ConstantPoolGen getCpg() {
return cpg;
}
/**
* @param dictionary The dictionary to set.
*/
private void setDictionary(SignatureDictionary dictionary) {
this.dictionary = dictionary;
}
/**
* @return the dictionary.
*/
public SignatureDictionary getDictionary() {
return dictionary;
}
/**
* @return the policy
*/
public Policy getPolicy() {
return policy;
}
/**
* @return the flow checker
*/
public FlowChecker getFlowChecker() {
return flowchecker;
}
/**
*
* @return the bcel Method
object
*/
public Method getMethod() {
return _method;
}
/**
*
* @return the bcel JavaClass
object associated to analyzed method
*/
public JavaClass getJavaClass() {
return this._class;
}
/**
*
* @return the class name
*/
public String getClassName() {
return _class.getClassName();
}
/**
*
* @return the annotations of the method
*/
public Annotation[] getAnnotations(){
return this.annotations;
}
}