/**
* This file is licensed under the University of Illinois/NCSA Open Source License. See LICENSE.TXT for details.
*/
package edu.illinois.keshmesh.detector;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.jdt.core.IJavaProject;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.AbstractTypeInNode;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.NormalAllocationInNode;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.util.intset.OrdinalSet;
import edu.illinois.keshmesh.detector.bugs.BugInstance;
import edu.illinois.keshmesh.detector.bugs.BugInstances;
import edu.illinois.keshmesh.detector.bugs.BugPatterns;
import edu.illinois.keshmesh.detector.bugs.CodePosition;
import edu.illinois.keshmesh.detector.bugs.LCK02JFixInformation;
import edu.illinois.keshmesh.detector.util.AnalysisUtils;
import edu.illinois.keshmesh.util.Logger;
import edu.illinois.keshmesh.walaconfig.ReceiverStringContext;
/**
*
* @author Mohsen Vakilian
* @author Stas Negara
*
*/
public class LCK02JBugDetector extends BugPatternDetector {
private static final String JAVA_LANG_CLASS = "Ljava/lang/Class"; //$NON-NLS-1$
@Override
public IntermediateResults getIntermediateResults() {
return null;
}
@Override
public BugInstances doPerformAnalysis(IJavaProject javaProject, BasicAnalysisData analysisData) {
final BugInstances bugInstances = new BugInstances();
Iterator<CGNode> cgNodesIterator = basicAnalysisData.callGraph.iterator();
while (cgNodesIterator.hasNext()) {
final CGNode cgNode = cgNodesIterator.next();
IMethod method = cgNode.getMethod();
if (AnalysisUtils.isJDKClass(method.getDeclaringClass()))
continue;
IR ir = cgNode.getIR();
if (ir != null) {
AnalysisUtils.collect(javaProject, new HashSet<InstructionInfo>(), cgNode, new InstructionFilter() {
@Override
public boolean accept(InstructionInfo instructionInfo) {
SSAInstruction instruction = instructionInfo.getInstruction();
if (AnalysisUtils.isMonitorEnter(instruction)) {
Set<String> synchronizedClassTypeNames = getSynchronizedClassTypeNames((SSAMonitorInstruction) instruction, cgNode);
if (!synchronizedClassTypeNames.isEmpty()) {
CodePosition instructionPosition = instructionInfo.getPosition();
Logger.log("Detected an instance of LCK02-J in class " + instructionPosition.getFullyQualifiedClassName() + ", line number=" + instructionPosition.getFirstLine()
+ ", instructionIndex= " + instructionInfo.getInstructionIndex());
bugInstances.add(new BugInstance(BugPatterns.LCK02J, instructionPosition, new LCK02JFixInformation(synchronizedClassTypeNames)));
}
}
return false;
}
});
}
}
return bugInstances;
}
private static boolean isReturnedByGetClass(NormalAllocationInNode normalAllocationInNode) {
return normalAllocationInNode.getSite().getDeclaredType().getName().toString().equals(JAVA_LANG_CLASS) && AnalysisUtils.isObjectGetClass(normalAllocationInNode.getNode().getMethod());
}
private Set<String> getSynchronizedClassTypeNames(SSAMonitorInstruction monitorInstruction, CGNode cgNode) {
Set<String> result = new HashSet<String>();
int lockValueNumber = monitorInstruction.getRef();
PointerKey lockPointer = getPointerForValueNumber(cgNode, lockValueNumber);
OrdinalSet<InstanceKey> lockObjects = basicAnalysisData.pointerAnalysis.getPointsToSet(lockPointer);
for (InstanceKey instanceKey : lockObjects) {
Logger.log("An InstanceKey pointed to by the lock of the synchronized block:" + instanceKey);
if (instanceKey instanceof NormalAllocationInNode) {
NormalAllocationInNode normalAllocationInNode = (NormalAllocationInNode) instanceKey;
if (isReturnedByGetClass(normalAllocationInNode)) {
// addSynchronizedClassTypeNames(result, normalAllocationInNode);
result.add(getReceiverTypeName(normalAllocationInNode));
}
}
}
return result;
}
/*
* The method Object.getClass() has a normal allocation instruction. This
* method look for the predecessors of the CGNode containing the normal
* allocation instruction. These predecessors make calls to
* Object.getClass(). This method iterates over all such invocations and
* returns the type names of the receivers of all such method invocations.
* This method is an attempt to make the analysis independent of receiver
* instance context. But, we encountered several problems while using this
* method on contexts lighter than the receiver instance context. For
* example, cheaper contexts are less precise and thus report too many
* predecessors for a the CGNode of Object.getClass(). And, too many
* predecessors result in too many type names to be reported as potential
* receivers of a call to getClass. Specifically, several exception classes
* get reported as the type names of the receivers of the call to
* Object.getClass.
*/
@Deprecated
private void addSynchronizedClassTypeNames(Set<String> result, NormalAllocationInNode normalAllocationInNode) {
{
CGNode normalAllocationCGNode = normalAllocationInNode.getNode();
Iterator<CGNode> predNodesIterator = basicAnalysisData.callGraph.getPredNodes(normalAllocationCGNode);
while (predNodesIterator.hasNext()) {
CGNode predNode = predNodesIterator.next();
Iterator<CallSiteReference> possibleSitesIterator = basicAnalysisData.callGraph.getPossibleSites(predNode, normalAllocationCGNode);
while (possibleSitesIterator.hasNext()) {
CallSiteReference possibleSite = possibleSitesIterator.next();
SSAAbstractInvokeInstruction[] calls = predNode.getIR().getCalls(possibleSite);
for (SSAAbstractInvokeInstruction invokeInstruction : calls) {
int invocationReceiverValueNumber = invokeInstruction.getReceiver();
PointerKey pointerKeyForReceiver = getPointerForValueNumber(predNode, invocationReceiverValueNumber);
OrdinalSet<InstanceKey> receiverObjects = basicAnalysisData.pointerAnalysis.getPointsToSet(pointerKeyForReceiver);
for (InstanceKey receiverInstanceKey : receiverObjects) {
result.add(getJavaClassName(receiverInstanceKey.getConcreteType().getName()));
}
}
}
}
}
}
private static String getReceiverTypeName(AbstractTypeInNode node) {
TypeName typeName = ((ReceiverStringContext) (node.getNode().getContext())).getReceiver().getConcreteType().getName();
return getJavaClassName(typeName);
}
private static String getJavaClassName(TypeName typeName) {
return AnalysisUtils.walaTypeNameToJavaName(typeName) + ".class";
}
}