/** * 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.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.eclipse.jdt.core.IJavaProject; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAMonitorInstruction; 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.LCK03JBugPattern; import edu.illinois.keshmesh.detector.bugs.LCK03JFixInformation; import edu.illinois.keshmesh.detector.util.AnalysisUtils; import edu.illinois.keshmesh.util.Logger; /** * * @author Samira Tasharofi * */ public class LCK03JBugDetector extends BugPatternDetector { @Override public IntermediateResults getIntermediateResults() { return null; } @Override public BugInstances doPerformAnalysis(IJavaProject javaProject, BasicAnalysisData basicAnalysisData) { BugInstances bugInstances = new BugInstances(); Collection<InstructionInfo> unsafeSynchronizedBlocks = new HashSet<InstructionInfo>(); Iterator<CGNode> cgNodesIterator = basicAnalysisData.callGraph.iterator(); while (cgNodesIterator.hasNext()) { final CGNode cgNode = cgNodesIterator.next(); IMethod method = cgNode.getMethod(); if (!isIgnoredClass(method.getDeclaringClass())) { populateBugInstances(unsafeSynchronizedBlocks, cgNode, bugInstances); } } return bugInstances; } private Set<String> getJavaNames(Set<IClass> monitorExpressionTypes) { Set<String> monitorExpressionTypeNames = new HashSet<String>(); for (IClass type : monitorExpressionTypes) { monitorExpressionTypeNames.add(AnalysisUtils.walaTypeNameToJavaName(type.getName())); } return monitorExpressionTypeNames; } private void populateBugInstances(Collection<InstructionInfo> synchronizedBlocks, final CGNode cgNode, final BugInstances bugInstances) { AnalysisUtils.collect(javaProject, synchronizedBlocks, cgNode, new InstructionFilter() { @Override public boolean accept(InstructionInfo instructionInfo) { SSAInstruction instruction = instructionInfo.getInstruction(); if (AnalysisUtils.isMonitorEnter(instruction)) { SSAMonitorInstruction monitorEnterInstruction = (SSAMonitorInstruction) instruction; Set<IClass> monitorExpressionTypes = getMonitorExpressionTypes(cgNode, monitorEnterInstruction); boolean isLock = isLock(monitorExpressionTypes); boolean isCondition = isCondition(monitorExpressionTypes); if (isLock || isCondition) { CodePosition instructionPosition = instructionInfo.getPosition(); Logger.log("Detected an instance of LCK03-J in class " + instructionPosition.getFullyQualifiedClassName() + ", line number=" + instructionPosition.getFirstLine() + ", instructionIndex= " + instructionInfo.getInstructionIndex()); bugInstances.add(new BugInstance(BugPatterns.LCK03J, instructionPosition, new LCK03JFixInformation(getJavaNames(monitorExpressionTypes), isLock))); } } return false; } }); } boolean isLock(Set<IClass> instanceTypes) { return anyClassImplementsInterface(instanceTypes, LCK03JBugPattern.LOCK); } boolean isCondition(Set<IClass> instanceTypes) { return anyClassImplementsInterface(instanceTypes, LCK03JBugPattern.CONDITION); } private boolean anyClassImplementsInterface(Set<IClass> instanceTypes, String interfaceName) { for (IClass instanceType : instanceTypes) { Collection<IClass> implementedInterfaces = instanceType.getAllImplementedInterfaces(); for (IClass implementedInterface : implementedInterfaces) { String interfaceType = AnalysisUtils.walaTypeNameToJavaName(implementedInterface.getName()); if (interfaceType.equals(interfaceName)) { return true; } } } return false; } Set<IClass> getMonitorExpressionTypes(CGNode cgNode, SSAMonitorInstruction monitorInstruction) { if (!monitorInstruction.isMonitorEnter()) { throw new AssertionError("Expected a monitor enter instruction."); } PointerKey lockPointer = getPointerForValueNumber(cgNode, monitorInstruction.getRef()); Collection<InstanceKey> lockPointedInstances = getPointedInstances(lockPointer); Set<IClass> instancesTypes = new HashSet<IClass>(); for (InstanceKey instanceKey : lockPointedInstances) { instancesTypes.add(instanceKey.getConcreteType()); } return instancesTypes; } private boolean isIgnoredClass(IClass klass) { //TODO: Should we look for bugs in JDK usage as well? //TODO: !!!What about other bytecodes, e.g. from the libraries, which will not allow to get the source position? return AnalysisUtils.isJDKClass(klass); } }