package fr.lifl.stc.stan.analyser;
import java.util.Hashtable;
import java.util.Enumeration;
import org.apache.bcel.SignatureConstants;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.ExternalMethodsAttribute;
import org.apache.bcel.classfile.ExternalFieldsAttribute;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.FieldInstruction;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.Constants;
import fr.lifl.stc.stan.analyser.GlobalAnalyser;
import fr.lifl.stc.stan.execution.stack.JvmType;
import fr.lifl.stc.stan.link.Link;
import fr.lifl.stc.stan.link.TableLink;
import fr.lifl.stc.stan.policy.Policy;
import fr.lifl.stc.stan.samples.flow.FlowAnnotation;
import fr.lifl.stc.stan.samples.flow.FlowPartialSignature;
import fr.lifl.stc.stan.samples.flow.FlowPartialSignatureData;
import fr.lifl.stc.stan.signature.Signature;
import fr.lifl.stc.stan.signature.SignatureDictionary;
import fr.lifl.stc.stan.annotation.Annotation;
import fr.lifl.stc.stan.tools.HTMLOutput;
import fr.lifl.stc.stan.util.Printer;
import fr.lifl.stc.stan.dsl.FlowChecker;
/**
* @author Julien Graziano
*
*/
public class ClassAnalyser {
public static long memory = 0L;
/**
* dictionary containing signatures
*/
private SignatureDictionary dictionary;
private Policy policy;
private FlowChecker flowchecker; /** Checker for dsl policies */
private JavaClass _class;
private Method methods[];
private ConstantPoolGen cpg;
public int proof_index;
private int externalMethods_index;
private int externalFields_index;
/**
* list the external methods invoked in the class
*
* key => method type
*
* where
* key = the key of the method in the dictionary
* method type = a combination between
* SignatureDictionary.GLOBAL_SIGN_IDX and
* SignatureDictionary.EXACT_SIGN_IDX, specifing which of global/exact signatures are nedeed
*/
private Hashtable externalMethods;
/**
* key => security level
* Where key is = className.fieldName
* the entry in the constant pool corresponding to the field
*/
private Hashtable externalFields;
/**
* Creates a new ClassAnalyser
instance.
* @param dict
* @param pol
* @param c
*/
public ClassAnalyser(SignatureDictionary dict, Policy pol, FlowChecker fc, JavaClass c) {
dictionary = dict;
policy = pol;
flowchecker = fc;
_class = c;
methods = c.getMethods();
externalMethods = new Hashtable();
externalFields = new Hashtable();
cpg = new ConstantPoolGen(c.getConstantPool());
prepareConstantPool();
}
/**
*
*
*/
private void prepareConstantPool() {
proof_index = cpg.addUtf8("ProofMap");
externalMethods_index = cpg.addUtf8("ExternalMethods");
externalFields_index = cpg.addUtf8("ExternalFields");
_class.setConstantPool(cpg.getFinalConstantPool());
}
/**
*
*/
public void analyse() {
int i;
boolean printed = false;
MethodAnalyzer manalyser;
for (i = 0; i < methods.length; i++) {
HTMLOutput.newMethod(methods[i].getName(), methods[i].getSignature());
printed = false;
if (methods[i].isNative()) {
//Printer.println("[run] Native method " + SignatureDictionary.key(_class,methods[i]));
String msg = "native ";
if (methods[i].isStatic())
msg += "static\t";
else
msg += "\t";
String key = SignatureDictionary.key(_class, methods[i]);
if (dictionary.contains(key))
continue;
System.err.println("[run] Native method "+msg + this._class.getClassName() + "."
+ methods[i].getName() + methods[i].getSignature()+" - signature not found: worst signature considered ");
TableLink b = computeWorstSignature(methods[i]);
FlowPartialSignature lps = new FlowPartialSignature(FlowAnnotation.class.getName(), b);
Signature s = new Signature();
s.add(lps);
// HTMLOutput.newMethod(methods[i].getName(), methods[i].getSignature());
//HTMLOutput.appendln(b);
dictionary.putSignature(SignatureDictionary.key(_class, methods[i]), s, true);
} else if (methods[i].isAbstract()) {
if (dictionary.contains(SignatureDictionary.key(_class,
methods[i])))
continue;
TableLink b = null;
if (Config.worstSignatureEnabled() && methods[i].isPublic()) {
b = computeWorstSignature(methods[i]);
} else {
int n = methods[i].getArgumentTypes().length;
int dec;
if (methods[i].isStatic())
dec = 4;
else
dec = 5;
b = new TableLink(n + dec, n + dec);
}
FlowPartialSignature lps = new FlowPartialSignature(FlowAnnotation.class.getName(), b);
Signature s = new Signature();
s.add(lps);
dictionary.putSignature(SignatureDictionary.key(_class, methods[i]), s , false);
} else {
// String key = SignatureDictionary.key(_class, methods[i]);
// Signature sgn = dictionary.findExactSignature(key);
{
manalyser = null;
Annotation[] annotations = null;
Printer.println("[run] Analysing method "
+ methods[i].getName() + methods[i].getSignature());
manalyser = new MethodAnalyzer(dictionary, policy, flowchecker, cpg, _class,
methods[i], this, proof_index);
//if (Config.implicitFlowEnabled())
// manalyser.analyseImplicitFlow();
/**
* analyses the method
*/
manalyser.analyse();
// System.gc();
// System.gc();
// long memoryEnd = Runtime.getRuntime().freeMemory();
// Stats.addMemory(memoryStart - memoryEnd);
/**
* prints friendly-html
*/
annotations = manalyser.getAnnotations();
for (int j = 0; j < annotations.length; j++) {
HTMLOutput.appendln(annotations[j].toHTML());
annotations[1].addResults();
}
printed = true;
/**
* annotates LinkMap and ProofMap
*/
if (Config.annotateEnabled())
manalyser.annotateProof();
Stats.incAnalyzedMethod();
}
memory = Math.max(memoryUsed(), memory);
}
if(!printed) {
Signature aux = dictionary.findGlobalSignature(SignatureDictionary.key(_class, methods[i]));
HTMLOutput.appendln("Information Flow
");
if(aux!=null) {
HTMLOutput.appendln("global
");
HTMLOutput.appendln(aux.toHTMLString());
}
aux = dictionary.findExactSignature(SignatureDictionary.key(_class, methods[i]));
if(aux!=null) {
HTMLOutput.appendln("exact
");
HTMLOutput.appendln(aux.toHTMLString());
}
}
}
if (Config.annotateEnabled()){
this.annotateFields();
this.annotateExternalMethods();
this.annotateExternalFields();
}
}
/**
* computes the worst signature possible for method method
* @param method the method for which the signature will be computed
* @return the worst possible signture for method method
*/
TableLink computeWorstSignature(Method method) {
int n = method.getArgumentTypes().length;
int dec;
if (method.isStatic())
dec = 4;
else
dec = 5;
TableLink b = new TableLink(n + dec, n + dec);
for (int ii = 0; ii < n + dec; ii++) {
int fromType = 0;
int fromPart = 0;
if ((ii > 0 && ii < dec)
|| (ii == 0 && method.getReturnType().getSignature().charAt(0) == '[')
|| (ii >= dec && method.getArgumentTypes()[ii - dec] instanceof ReferenceType && method
.getArgumentTypes()[ii - dec].getSignature().charAt(0) == '[')) {
fromPart = JvmType.PUBLIC_PART;
fromType = Link.REFERENCE_LINK;
// REFERENCE PUBLIC PART
} else if ((ii == 0 && method.getReturnType() instanceof BasicType && method.getReturnType().getSignature().charAt(0)!='V')
|| (ii >= dec && method.getArgumentTypes()[ii - dec] instanceof BasicType)) {
fromPart = JvmType.PUBLIC_PART;
fromType = Link.VALUE_LINK;
// VALUE PUBLIC PART
} else {
fromPart = JvmType.WHOLE_PART;
fromType = Link.REFERENCE_LINK;
// WHOLE REFERENCE
}
for (int jj = 0; jj < n + dec; jj++) {
int toType = 0;
int toPart = 0;
if ((jj > 0 && jj < dec)
|| (jj == 0 && method.getReturnType().getSignature()
.charAt(0) == '[')
|| (jj >= dec
&& method.getArgumentTypes()[jj - dec] instanceof ReferenceType && method
.getArgumentTypes()[jj - dec].getSignature()
.charAt(0) == '[')) {
toPart = JvmType.PUBLIC_PART;
toType = Link.REFERENCE_LINK;
// REFERENCE PUBLIC PART
} else if ((jj == 0 && method.getReturnType() instanceof BasicType && method.getReturnType().getSignature().charAt(0)!='V')
|| (jj >= dec && method.getArgumentTypes()[jj - dec] instanceof BasicType)) {
toPart = JvmType.PUBLIC_PART;
toType = Link.VALUE_LINK;
// VALUE PUBLIC PART
} else {
toPart = JvmType.WHOLE_PART;
toType = Link.REFERENCE_LINK;
// WHOLE REFERENCE
}
b.set(ii, jj, GlobalAnalyser.linkFactory.createLink(fromPart,
fromType, toPart, toType));
}
}
if (method.getReturnType().getSignature().charAt(0) == 'V')
for (int ii = 0; ii < n + dec; ii++) {
b.put(ii, 0, null);
b.put(0, ii, null);
}
return b;
}
/**
* annotates the .class
file with security policies associated to class attributes.
*
*/
private void annotateFields() {
Field[] field = this._class.getFields();
for (int i = 0; i < field.length; i++) {
if (policy.findAttribute(this._class.getClassName(), field[i]
.getName()) == Policy.SECRET_ATTRIBUTE) // secret field
{
field[i].setAccessFlags(field[i].getAccessFlags()
| Constants.ACC_SECRET);
}
}
}
/**
*
*
*/
private void annotateExternalFields(){
byte[] byteStream = this.getExternalFieldsAsBytes();
if (byteStream == null) {
return;
}
Attribute[] orig = this._class.getAttributes();
if (byteStream.length > 0) {
ExternalFieldsAttribute linkAttr = new ExternalFieldsAttribute(externalFields_index,
byteStream.length, byteStream, _class.getConstantPool());
int j = 0;
while (j < orig.length) {
if (orig[j] instanceof ExternalFieldsAttribute)
break;
j++;
}
if (j < orig.length) {
orig[j] = linkAttr;
_class.setAttributes(orig);
} else {
Attribute[] new_attr = new Attribute[orig.length + 1];
new_attr[0] = linkAttr;
System.arraycopy(orig, 0, new_attr, 1, orig.length);
_class.setAttributes(new_attr);
}
} else {
//suppress the attribute
int j = 0;
while (j < orig.length) {
if (orig[j] instanceof ExternalFieldsAttribute) {
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);
_class.setAttributes(new_attr);
}
}
}
/**
* returns the EXTERNAL_FIELDS attibute
*
* length array_of_external_fields
*
* where the array has the form:
*
* index security_level
*
* where
* index [2bytes] is the constant pool index
* scurity_level [1 byte] the policy of the field : one of the values #Policy.SECRET_ATTRIBUTE or #Policy.PUBLIC_ATTRIBUTE
*
*
*
* @return
*/
byte[] getExternalFieldsAsBytes() {
byte[] output = null;
if(this.externalFields.size() < 1)
return null;
int length = SignatureConstants.TOTAL_LENGTH_SIZE;
//compute length = noOfFields * ( siezOfIndex (2) + sizeOfPolicy (1) )
length += this.externalFields.size() * ( SignatureConstants.CSTPOOL_SIZE + 1);
output = new byte[length];
int index = 0;
byte[] arrSize = SignatureConstants.toByteArray(this.externalFields.size(),
SignatureConstants.TOTAL_LENGTH_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
//total number of distinct signatures to be embedded
Enumeration it = this.externalFields.keys();
for(; it.hasMoreElements();) {
String description = it.nextElement();
String className = SignatureDictionary.keyClassName(description);
String fieldName = SignatureDictionary.keyFieldName(description);
int cstPoolIdx = this.externalFields.get(description).intValue();
int securityLevel = policy.findAttribute( className, fieldName);
//add constant pool index
arrSize = SignatureConstants.toByteArray(cstPoolIdx, SignatureConstants.CSTPOOL_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
//add security level
output[index++] = (byte)securityLevel;
}
return output;
}
/**
*
*
*/
private void annotateExternalMethods(){
byte[] byteStream = this.getExternalMethodsAsBytes();
if (byteStream == null) {
return;
}
Attribute[] orig = this._class.getAttributes();
if (byteStream.length > 0) {
ExternalMethodsAttribute linkAttr = new ExternalMethodsAttribute(externalMethods_index,
byteStream.length, byteStream, _class.getConstantPool());
int j = 0;
while (j < orig.length) {
if (orig[j] instanceof ExternalMethodsAttribute)
break;
j++;
}
if (j < orig.length) {
orig[j] = linkAttr;
_class.setAttributes(orig);
} else {
Attribute[] new_attr = new Attribute[orig.length + 1];
new_attr[0] = linkAttr;
System.arraycopy(orig, 0, new_attr, 1, orig.length);
_class.setAttributes(new_attr);
}
} else {
//suppress the attribute
int j = 0;
while (j < orig.length) {
if (orig[j] instanceof ExternalMethodsAttribute) {
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);
_class.setAttributes(new_attr);
}
}
}
/**
* returns the EXTERNAL_METHODS attibute
*
* length array_of_external_methods
*
* where an entry in the array has the form:
*
* entry_size(in bytes) index type signature
*
* where
* entry_size [TOTAL_LENGHT_SIZE] is the number of bytes to follow for the entry (in bytes)
* index [2bytes] is the constant pool entry for the method
* type [1 byte] the type of signature (global or local)
* signature the method signature
*
* @return
*/
byte[] getExternalMethodsAsBytes() {
byte[] output = null;
if(this.externalMethods.size() < 1)
return null;
int length = SignatureConstants.TOTAL_LENGTH_SIZE;
//compute length
//total number of distinct signatures to be embedded
int nSign = 0;
Enumeration it = this.externalMethods.keys();
for(; it.hasMoreElements();) {
String key = it.nextElement();
int value = this.externalMethods.get(key).intValue();
if((value & SignatureDictionary.EXACT_SIGN_IDX) != 0 ) {
//we have the exact signature
Signature sign = this.dictionary.findExactSignature(key);
if(sign!=null) {
nSign ++;
FlowPartialSignature link = (FlowPartialSignature) sign
.getPartialSignature(FlowAnnotation.class.getName());
//System.out.println("FLW for :"+key+" = "+link);
byte[] byteStream = ((FlowPartialSignatureData) link.getComponent())
.getDataAsBytes();
length += SignatureConstants.TOTAL_LENGTH_SIZE;
length += SignatureConstants.CSTPOOL_SIZE;
length += byteStream.length;
//1 byte for type of signature (exact or global)
length += 1;
}
else
Printer.println("Exact signature for "+key+" not found in the dictionary!");
}
if((value & SignatureDictionary.GLOBAL_SIGN_IDX) != 0 ) {
//we have the global signature
Signature sign = this.dictionary.findGlobalSignature(key);
if(sign!=null){
nSign++;
FlowPartialSignature link = (FlowPartialSignature) sign
.getPartialSignature(FlowAnnotation.class.getName());
byte[] byteStream = ((FlowPartialSignatureData) link.getComponent())
.getDataAsBytes();
length += SignatureConstants.TOTAL_LENGTH_SIZE;
length += SignatureConstants.CSTPOOL_SIZE;
length += byteStream.length;
length += 1;
}
else
Printer.println("Global signature for "+key+" not found in the dictionary!");
}
}
output = new byte[length];
int index = 0;
byte[] arrSize = SignatureConstants.toByteArray(nSign,
SignatureConstants.TOTAL_LENGTH_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
//add data to
it = this.externalMethods.keys();
for(; it.hasMoreElements();) {
String key = it.nextElement();
int value = this.externalMethods.get(key).intValue();
String className = SignatureDictionary.keyClassName(key);
int cstPoolIdx = cpg.lookupMethodref( className,
SignatureDictionary.keyOnlyMethodName(key),
SignatureDictionary.keyDescription(key));
if(cstPoolIdx == -1) {
cstPoolIdx = cpg.lookupInterfaceMethodref( className,
SignatureDictionary.keyOnlyMethodName(key),
SignatureDictionary.keyDescription(key));
if(cstPoolIdx == -1) {
className = SignatureDictionary.keyJits(className);
cstPoolIdx = cpg.lookupMethodref( className,
SignatureDictionary.keyOnlyMethodName(key),
SignatureDictionary.keyDescription(key));
if(cstPoolIdx == -1)
cstPoolIdx = cpg.lookupInterfaceMethodref( className,
SignatureDictionary.keyOnlyMethodName(key),
SignatureDictionary.keyDescription(key));
}
}
if((value & SignatureDictionary.EXACT_SIGN_IDX) != 0 ) {
//we have the exact signature
Signature sign = this.dictionary.findExactSignature(key);
if(sign!=null) {
FlowPartialSignature link = (FlowPartialSignature) sign
.getPartialSignature(FlowAnnotation.class.getName());
byte[] byteStream = ((FlowPartialSignatureData) link.getComponent())
.getDataAsBytes();
arrSize = SignatureConstants.toByteArray(cstPoolIdx,
SignatureConstants.CSTPOOL_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
arrSize = SignatureConstants.toByteArray(byteStream.length,
SignatureConstants.TOTAL_LENGTH_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
output[index++] = SignatureDictionary.EXACT_SIGN_IDX;
System.arraycopy(byteStream, 0, output, index, byteStream.length);
index += byteStream.length;
}
}
if((value & SignatureDictionary.GLOBAL_SIGN_IDX) != 0 ) {
//we have the global signature
Signature sign = this.dictionary.findGlobalSignature(key);
if(sign!=null){
FlowPartialSignature link = (FlowPartialSignature) sign
.getPartialSignature(FlowAnnotation.class.getName());
byte[] byteStream = ((FlowPartialSignatureData) link.getComponent())
.getDataAsBytes();
arrSize = SignatureConstants.toByteArray(cstPoolIdx,
SignatureConstants.CSTPOOL_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
arrSize = SignatureConstants.toByteArray(byteStream.length,
SignatureConstants.TOTAL_LENGTH_SIZE);
System.arraycopy(arrSize, 0, output, index, arrSize.length);
index += arrSize.length;
output[index++] = SignatureDictionary.GLOBAL_SIGN_IDX;
System.arraycopy(byteStream, 0, output, index, byteStream.length);
index += byteStream.length;
}
}
}
return output;
}
/**
* adds an external method invoked by one of the class' methods
* @param i
* @param typeSignature the type of signature of invoked method (global/exact)
*/
void addExternalMethod(InvokeInstruction i, int typeSignature) {
int newVal;
if( i.getClassName(cpg).equals(this._class.getClassName()))
return;
String key = SignatureDictionary.key(i, this.cpg);
if(this.externalMethods.containsKey(key)){
newVal = this.externalMethods.get(key).intValue() | typeSignature;
}
else
newVal = typeSignature;
this.externalMethods.put(key, new Integer(newVal));
}
/**
*
* @param i
*/
void addExternalField(FieldInstruction i) {
if( i.getClassName(cpg).equals(this._class.getClassName()))
return;
String key = SignatureDictionary.key(i.getClassName(this.cpg), i.getFieldName(this.cpg), "");
this.externalFields.put(key, new Integer(i.getIndex()));
}
/**
*
* @return
*/
public long memoryUsed() {
return Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory();
}
}