1 19 20 package edu.umd.cs.findbugs.detect; 21 22 23 import edu.umd.cs.findbugs.*; 24 import edu.umd.cs.findbugs.ba.*; 25 import edu.umd.cs.findbugs.ba.npe.*; 26 import edu.umd.cs.findbugs.ba.vna.*; 27 28 import java.util.BitSet ; 29 import org.apache.bcel.Constants; 30 import org.apache.bcel.classfile.*; 31 import org.apache.bcel.generic.*; 32 33 class Lock extends ResourceCreationPoint { 34 private ValueNumber lockValue; 35 36 public Lock(Location location, String lockClass, ValueNumber lockValue) { 37 super(location, lockClass); 38 this.lockValue = lockValue; 39 } 40 41 public ValueNumber getLockValue() { 42 return lockValue; 43 } 44 } 45 46 public class FindUnreleasedLock extends ResourceTrackingDetector<Lock, FindUnreleasedLock.LockResourceTracker> { 47 private static final boolean DEBUG = SystemProperties.getBoolean("ful.debug"); 48 private int numAcquires = 0; 49 50 private static final int JDK15_MAJOR = 48; 51 private static final int JDK15_MINOR = 0; 52 53 56 57 private static class LockFrameModelingVisitor extends ResourceValueFrameModelingVisitor { 58 private LockResourceTracker resourceTracker; 59 private Lock lock; 60 private ValueNumberDataflow vnaDataflow; 61 63 public LockFrameModelingVisitor( 64 ConstantPoolGen cpg, 65 LockResourceTracker resourceTracker, 66 Lock lock, 67 ValueNumberDataflow vnaDataflow, 68 IsNullValueDataflow isNullDataflow) { 69 super(cpg); 70 this.resourceTracker = resourceTracker; 71 this.lock = lock; 72 this.vnaDataflow = vnaDataflow; 73 } 75 76 @Override 77 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock) throws DataflowAnalysisException { 78 final Instruction ins = handle.getInstruction(); 79 final ConstantPoolGen cpg = getCPG(); 80 final ResourceValueFrame frame = getFrame(); 81 82 int status = -1; 83 84 if (DEBUG) System.out.println("PC : " + handle.getPosition() + " " + ins); 85 if (DEBUG && ins instanceof InvokeInstruction) { 86 InvokeInstruction iins = (InvokeInstruction) ins; 87 System.out.println(" " + ins.toString(cpg.getConstantPool())); 88 } 89 if (DEBUG) System.out.println("resource frame before instruction: " + frame.toString()); 90 91 Location creationPoint = lock.getLocation(); 93 if (handle == creationPoint.getHandle() && basicBlock == creationPoint.getBasicBlock()) { 94 status = ResourceValueFrame.OPEN; 95 if (DEBUG) System.out.println("OPEN"); 96 } else if (resourceTracker.isResourceClose(basicBlock, handle, cpg, lock, frame)) { 97 status = ResourceValueFrame.CLOSED; 98 if (DEBUG) System.out.println("CLOSE"); 99 } 100 101 analyzeInstruction(ins); 103 104 final int updatedNumSlots = frame.getNumSlots(); 105 106 ValueNumberFrame vnaFrame = vnaDataflow.getFactAfterLocation(new Location(handle, basicBlock)); 108 if (DEBUG) { 109 System.out.println("vna frame after instruction: " + vnaFrame.toString()); 110 System.out.println("Lock value number: " + lock.getLockValue()); 111 if (lock.getLockValue().hasFlag(ValueNumber.RETURN_VALUE) ) 112 System.out.println("is return value"); 113 } 114 115 for (int i = 0; i < updatedNumSlots; ++i) { 116 if (DEBUG) { 117 System.out.println("Slot " + i); 118 System.out.println(" Lock value number: " + vnaFrame.getValue(i)); 119 if (vnaFrame.getValue(i).hasFlag(ValueNumber.RETURN_VALUE) ) 120 System.out.println(" is return value"); 121 } 122 if (vnaFrame.fuzzyMatch(lock.getLockValue(), vnaFrame.getValue(i)) ) { 123 if (DEBUG) System.out.println("Saw lock value!"); 124 frame.setValue(i, ResourceValue.instance()); 125 } 126 } 127 128 if (status != -1) { 130 frame.setStatus(status); 131 } 132 if (DEBUG) System.out.println("resource frame after instruction: " + frame.toString()); 133 134 135 } 136 137 @Override 138 protected boolean instanceEscapes(InvokeInstruction inv, int instanceArgNum) { 139 return false; 140 } 141 } 142 143 class LockResourceTracker implements ResourceTracker<Lock> { 144 private RepositoryLookupFailureCallback lookupFailureCallback; 145 private CFG cfg; 146 private ValueNumberDataflow vnaDataflow; 147 private IsNullValueDataflow isNullDataflow; 148 149 public LockResourceTracker( 150 RepositoryLookupFailureCallback lookupFailureCallback, 151 CFG cfg, 152 ValueNumberDataflow vnaDataflow, 153 IsNullValueDataflow isNullDataflow) { 154 this.lookupFailureCallback = lookupFailureCallback; 155 this.cfg = cfg; 156 this.vnaDataflow = vnaDataflow; 157 this.isNullDataflow = isNullDataflow; 158 } 159 160 public Lock isResourceCreation(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg) 161 throws DataflowAnalysisException { 162 163 InvokeInstruction inv = toInvokeInstruction(handle.getInstruction()); 164 if (inv == null) 165 return null; 166 167 String className = inv.getClassName(cpg); 168 String methodName = inv.getName(cpg); 169 String methodSig = inv.getSignature(cpg); 170 171 try { 172 if (methodName.equals("lock") && 173 methodSig.equals("()V") && 174 Hierarchy.isSubtype(className, "java.util.concurrent.locks.Lock")) { 175 176 Location location = new Location(handle, basicBlock); 177 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location); 178 ValueNumber lockValue = frame.getTopValue(); 179 if (DEBUG) System.out.println("Lock value is " + lockValue.getNumber() + ", frame=" + frame.toString()); 180 if (DEBUG) ++numAcquires; 181 return new Lock(location, className, lockValue); 182 } 183 } catch (ClassNotFoundException e) { 184 lookupFailureCallback.reportMissingClass(e); 185 } 186 return null; 187 } 188 189 public boolean isResourceClose(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, Lock resource, 190 ResourceValueFrame frame) throws DataflowAnalysisException { 191 192 InvokeInstruction inv = toInvokeInstruction(handle.getInstruction()); 193 if (inv == null) 194 return false; 195 196 String className = inv.getClassName(cpg); 197 String methodName = inv.getName(cpg); 198 String methodSig = inv.getSignature(cpg); 199 200 ResourceValue topValue = frame.getTopValue(); 201 if (!topValue.isInstance()) 202 return false; 203 204 try { 205 if (methodName.equals("unlock") && 206 methodSig.equals("()V") && 207 Hierarchy.isSubtype(className, "java.util.concurrent.locks.Lock")) { 208 209 return true; 210 } 211 } catch (ClassNotFoundException e) { 212 lookupFailureCallback.reportMissingClass(e); 213 } 214 215 return false; 216 } 217 218 public ResourceValueFrameModelingVisitor createVisitor(Lock resource, ConstantPoolGen cpg) { 219 return new LockFrameModelingVisitor(cpg, this, resource, vnaDataflow, isNullDataflow); 220 } 221 222 public boolean ignoreImplicitExceptions(Lock resource) { 223 return false; 226 } 227 228 public boolean ignoreExceptionEdge(Edge edge, Lock resource, ConstantPoolGen cpg) { 229 230 try { 231 Location location = cfg.getExceptionThrowerLocation(edge); 232 if (DEBUG) { 233 System.out.println("Exception thrower location: " + location); 234 } 235 Instruction ins = location.getHandle().getInstruction(); 236 237 if (ins instanceof GETFIELD) { 238 GETFIELD insGetfield = (GETFIELD)ins; 239 String fieldName = insGetfield.getFieldName(cpg); 240 if (DEBUG) { 241 System.out.println("Inspecting GETFIELD of " + fieldName + " at " + location); 242 } 243 if (fieldName.equals("lock")) return true; 246 IsNullValueFrame frame = isNullDataflow.getFactAtLocation(location); 247 if (!frame.isValid()) 248 return false; 249 IsNullValue receiver = frame.getInstance(ins, cpg); 250 boolean notNull = receiver.isDefinitelyNotNull(); 251 if (DEBUG && notNull) { 252 System.out.println("Ignoring exception from non-null GETFIELD"); 253 } 254 return notNull; 255 } else if (ins instanceof InvokeInstruction) { 256 InvokeInstruction iins = (InvokeInstruction) ins; 257 String methodName = iins.getMethodName(cpg); 258 if (methodName.startsWith("access$")) return true; 260 if (methodName.equals("readLock") || methodName.equals("writeLock")) return true; 261 if (methodName.equals("lock") || methodName.equals("unlock")) return true; 262 } 263 if (DEBUG) { 264 System.out.println("FOUND Exception thrower at: " + location); 265 } 266 } catch (DataflowAnalysisException e) { 267 } 269 270 return false; 271 } 272 273 public boolean isParamInstance(Lock resource, int slot) { 274 return false; 277 } 278 279 private InvokeInstruction toInvokeInstruction(Instruction ins) { 280 short opcode = ins.getOpcode(); 281 if (opcode != Constants.INVOKEVIRTUAL && opcode != Constants.INVOKEINTERFACE) 282 return null; 283 return (InvokeInstruction) ins; 284 } 285 } 286 287 290 291 public FindUnreleasedLock(BugReporter bugReporter) { 292 super(bugReporter); 293 } 294 295 298 public static boolean preTiger(JavaClass jclass) { 299 return jclass.getMajor() < JDK15_MAJOR || 300 (jclass.getMajor() == JDK15_MAJOR && jclass.getMinor() < JDK15_MINOR); 301 302 } 303 306 @Override 307 public void visitClassContext(ClassContext classContext) { 308 JavaClass jclass = classContext.getJavaClass(); 309 310 if (preTiger(jclass)) return; 314 315 boolean sawUtilConcurrentLocks = false; 316 for(Constant c : jclass.getConstantPool().getConstantPool()) 317 if (c instanceof ConstantMethodref) { 318 ConstantMethodref m = (ConstantMethodref) c; 319 ConstantClass cl = (ConstantClass) jclass.getConstantPool().getConstant(m.getClassIndex()); 320 ConstantUtf8 name = (ConstantUtf8) jclass.getConstantPool().getConstant(cl.getNameIndex()); 321 String nameAsString = name.getBytes(); 322 if (nameAsString.startsWith("java/util/concurrent/locks")) sawUtilConcurrentLocks = true; 323 324 } 325 if (sawUtilConcurrentLocks) super.visitClassContext(classContext); 326 } 327 328 329 @Override 330 public boolean prescreen(ClassContext classContext, Method method) { 331 BitSet bytecodeSet = classContext.getBytecodeSet(method); 332 if (bytecodeSet == null) return false; 333 MethodGen methodGen = classContext.getMethodGen(method); 334 return methodGen != null && methodGen.getName().toLowerCase().indexOf("lock") == -1 335 && (bytecodeSet.get(Constants.INVOKEVIRTUAL) 336 || bytecodeSet.get(Constants.INVOKEINTERFACE)); 337 } 338 339 @Override 340 public LockResourceTracker getResourceTracker(ClassContext classContext, Method method) 341 throws CFGBuilderException, DataflowAnalysisException { 342 return new LockResourceTracker( 343 bugReporter, 344 classContext.getCFG(method), 345 classContext.getValueNumberDataflow(method), 346 classContext.getIsNullValueDataflow(method)); 347 } 348 349 @Override 350 public void inspectResult(ClassContext classContext, MethodGen methodGen, CFG cfg, 351 Dataflow<ResourceValueFrame, ResourceValueAnalysis<Lock>> dataflow, Lock resource) { 352 353 JavaClass javaClass = classContext.getJavaClass(); 354 355 ResourceValueFrame exitFrame = dataflow.getResultFact(cfg.getExit()); 356 if (DEBUG) { 357 System.out.println("Resource value at exit: " + exitFrame); 358 } 359 int exitStatus = exitFrame.getStatus(); 360 361 if (exitStatus == ResourceValueFrame.OPEN || exitStatus == ResourceValueFrame.OPEN_ON_EXCEPTION_PATH) { 362 String bugType; 363 int priority; 364 if (exitStatus == ResourceValueFrame.OPEN) { 365 bugType = "UL_UNRELEASED_LOCK"; 366 priority = HIGH_PRIORITY; 367 } else { 368 bugType = "UL_UNRELEASED_LOCK_EXCEPTION_PATH"; 369 priority = NORMAL_PRIORITY; 370 } 371 372 String sourceFile = javaClass.getSourceFileName(); 373 bugReporter.reportBug(new BugInstance(this, bugType, priority) 374 .addClassAndMethod(methodGen, sourceFile) 375 .addSourceLine(classContext, methodGen, sourceFile, resource.getLocation().getHandle())); 376 } 377 } 378 379 @Override 380 public void report() { 381 if (DEBUG) System.out.println("numAcquires=" + numAcquires); 382 } 383 384 387 388 public static void main(String [] argv) throws Exception { 389 if (argv.length != 3) { 390 System.err.println("Usage: " + FindUnreleasedLock.class.getName() + " <class file> <method name> <bytecode offset>"); 391 System.exit(1); 392 } 393 394 String classFile = argv[0]; 395 String methodName = argv[1]; 396 int offset = Integer.parseInt(argv[2]); 397 final FindUnreleasedLock detector = new FindUnreleasedLock(null); 398 399 ResourceValueAnalysisTestDriver<Lock, LockResourceTracker> driver = 400 new ResourceValueAnalysisTestDriver<Lock, LockResourceTracker>() { 401 @Override 402 public LockResourceTracker createResourceTracker(ClassContext classContext, Method method) 403 throws CFGBuilderException, DataflowAnalysisException { 404 405 RepositoryLookupFailureCallback lookupFailureCallback = classContext.getLookupFailureCallback(); 406 407 return detector.new LockResourceTracker( 408 lookupFailureCallback, 409 classContext.getCFG(method), 410 classContext.getValueNumberDataflow(method), 411 classContext.getIsNullValueDataflow(method)); 412 } 413 }; 414 415 driver.execute(classFile, methodName, offset); 416 } 417 } 418 419 | Popular Tags |