001: package edu.umd.cs.findbugs.detect;
002:
003: import java.util.BitSet;
004: import java.util.Iterator;
005:
006: import org.apache.bcel.Constants;
007: import org.apache.bcel.classfile.Method;
008: import org.apache.bcel.generic.ConstantPoolGen;
009: import org.apache.bcel.generic.Instruction;
010: import org.apache.bcel.generic.InstructionHandle;
011: import org.apache.bcel.generic.InvokeInstruction;
012: import org.apache.bcel.generic.MethodGen;
013: import org.apache.bcel.generic.ReferenceType;
014: import org.apache.bcel.generic.Type;
015:
016: import edu.umd.cs.findbugs.BugInstance;
017: import edu.umd.cs.findbugs.BugReporter;
018: import edu.umd.cs.findbugs.DeepSubtypeAnalysis;
019: import edu.umd.cs.findbugs.Detector;
020: import edu.umd.cs.findbugs.SourceLineAnnotation;
021: import edu.umd.cs.findbugs.ba.CFG;
022: import edu.umd.cs.findbugs.ba.CFGBuilderException;
023: import edu.umd.cs.findbugs.ba.ClassContext;
024: import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
025: import edu.umd.cs.findbugs.ba.Location;
026: import edu.umd.cs.findbugs.ba.type.NullType;
027: import edu.umd.cs.findbugs.ba.type.TopType;
028: import edu.umd.cs.findbugs.ba.type.TypeDataflow;
029: import edu.umd.cs.findbugs.ba.type.TypeFrame;
030:
031: public class FindNonSerializableValuePassedToWriteObject implements
032: Detector {
033:
034: private BugReporter bugReporter;
035:
036: private static final boolean DEBUG = false;
037:
038: public FindNonSerializableValuePassedToWriteObject(
039: BugReporter bugReporter) {
040: this .bugReporter = bugReporter;
041: }
042:
043: public void visitClassContext(ClassContext classContext) {
044: Method[] methodList = classContext.getJavaClass().getMethods();
045:
046: for (Method method : methodList) {
047: if (method.getCode() == null)
048: continue;
049:
050: try {
051: analyzeMethod(classContext, method);
052: } catch (CFGBuilderException e) {
053: bugReporter.logError("Detector "
054: + this .getClass().getName()
055: + " caught exception", e);
056: } catch (DataflowAnalysisException e) {
057: // bugReporter.logError("Detector " + this.getClass().getName() + " caught exception", e);
058: }
059: }
060: }
061:
062: private void analyzeMethod(ClassContext classContext, Method method)
063: throws CFGBuilderException, DataflowAnalysisException {
064: MethodGen methodGen = classContext.getMethodGen(method);
065: if (methodGen == null)
066: return;
067: BitSet bytecodeSet = classContext.getBytecodeSet(method);
068: if (bytecodeSet == null)
069: return;
070: // We don't adequately model instanceof interfaces yet
071: if (bytecodeSet.get(Constants.INSTANCEOF)
072: || bytecodeSet.get(Constants.CHECKCAST))
073: return;
074: CFG cfg = classContext.getCFG(method);
075: TypeDataflow typeDataflow = classContext
076: .getTypeDataflow(method);
077: ConstantPoolGen cpg = classContext.getConstantPoolGen();
078:
079: String sourceFile = classContext.getJavaClass()
080: .getSourceFileName();
081: if (DEBUG) {
082: String methodName = methodGen.getClassName() + "."
083: + methodGen.getName();
084: System.out.println("Checking " + methodName);
085: }
086:
087: for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
088: Location location = i.next();
089: InstructionHandle handle = location.getHandle();
090: int pc = handle.getPosition();
091: Instruction ins = handle.getInstruction();
092:
093: if (!(ins instanceof InvokeInstruction))
094: continue;
095:
096: InvokeInstruction invoke = (InvokeInstruction) ins;
097: String mName = invoke.getMethodName(cpg);
098: if (!mName.equals("writeObject"))
099: continue;
100: String cName = invoke.getClassName(cpg);
101: if (!cName.equals("java.io.ObjectOutput")
102: && !cName.equals("java.io.ObjectOutputStream"))
103: continue;
104:
105: TypeFrame frame = typeDataflow.getFactAtLocation(location);
106: if (!frame.isValid()) {
107: // This basic block is probably dead
108: continue;
109: }
110: Type operandType = frame.getTopValue();
111:
112: if (operandType.equals(TopType.instance())) {
113: // unreachable
114: continue;
115: }
116: if (!(operandType instanceof ReferenceType)) {
117: // Shouldn't happen - illegal bytecode
118: continue;
119: }
120: ReferenceType refType = (ReferenceType) operandType;
121:
122: if (refType.equals(NullType.instance())) {
123: continue;
124: }
125: String refSig = refType.getSignature();
126:
127: try {
128:
129: double isSerializable = DeepSubtypeAnalysis
130: .isDeepSerializable(refSig);
131:
132: if (isSerializable < 0.9) {
133: double isRemote = DeepSubtypeAnalysis
134: .isDeepRemote(refSig);
135: if (isSerializable < isRemote)
136: isSerializable = isRemote;
137: }
138:
139: if (isSerializable < 0.9) {
140: SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
141: .fromVisitedInstruction(classContext,
142: methodGen, sourceFile, handle);
143:
144: bugReporter
145: .reportBug(new BugInstance(
146: this ,
147: "DMI_NONSERIALIZABLE_OBJECT_WRITTEN",
148: isSerializable < 0.15 ? HIGH_PRIORITY
149: : isSerializable > 0.5 ? LOW_PRIORITY
150: : NORMAL_PRIORITY)
151: .addClassAndMethod(methodGen,
152: sourceFile)
153: .addClass(
154: DeepSubtypeAnalysis
155: .getComponentClass(refSig))
156: .addSourceLine(sourceLineAnnotation));
157: }
158: } catch (ClassNotFoundException e) {
159: // ignore
160: }
161: }
162: }
163:
164: public void report() {
165: }
166:
167: }
|