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 java.util.*; 26 import org.apache.bcel.Constants; 27 import org.apache.bcel.classfile.*; 28 import org.apache.bcel.generic.*; 29 30 39 public final class FindOpenStream extends ResourceTrackingDetector<Stream, StreamResourceTracker> implements StatelessDetector { 40 static final boolean DEBUG = SystemProperties.getBoolean("fos.debug"); 41 static final boolean IGNORE_WRAPPED_UNINTERESTING_STREAMS = !SystemProperties.getBoolean("fos.allowWUS"); 42 43 46 47 50 static final ObjectType[] streamBaseList = 51 {ObjectTypeFactory.getInstance("java.io.InputStream"), 52 ObjectTypeFactory.getInstance("java.io.OutputStream"), 53 ObjectTypeFactory.getInstance("java.io.Reader"), 54 ObjectTypeFactory.getInstance("java.io.Writer"), 55 ObjectTypeFactory.getInstance("java.sql.Connection"), 56 ObjectTypeFactory.getInstance("java.sql.Statement"), 57 ObjectTypeFactory.getInstance("java.sql.ResultSet")}; 58 59 63 static final StreamFactory[] streamFactoryList; 64 65 static { 66 ArrayList<StreamFactory> streamFactoryCollection = new ArrayList<StreamFactory>(); 67 68 streamFactoryCollection.add(new IOStreamFactory("java.io.InputStream", 71 new String []{"java.io.ByteArrayInputStream", "java.io.StringBufferInputStream", "java.io.PipedInputStream" 72 ,"java.io.ObjectInputStream" 73 }, 74 "OS_OPEN_STREAM")); 75 streamFactoryCollection.add(new IOStreamFactory("java.io.OutputStream", 76 new String []{"java.io.ByteArrayOutputStream", "java.io.PipedOutputStream" 77 , "java.io.ObjectOutputStream" 78 }, 79 "OS_OPEN_STREAM")); 80 streamFactoryCollection.add(new IOStreamFactory("java.io.Reader", 81 new String []{"java.io.StringReader", "java.io.CharArrayReader", "java.io.PipedReader"}, 82 "OS_OPEN_STREAM")); 83 streamFactoryCollection.add(new IOStreamFactory("java.io.Writer", 84 new String []{"java.io.StringWriter", "java.io.CharArrayWriter", "java.io.PipedWriter"}, 85 "OS_OPEN_STREAM")); 86 87 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.net.Socket", 89 "getInputStream", "()Ljava/io/InputStream;")); 90 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.net.Socket", 91 "getOutputStream", "()Ljava/io/OutputStream;")); 92 93 streamFactoryCollection.add(new StaticFieldLoadStreamFactory("java.io.InputStream", 95 "java.lang.System", "in", "Ljava/io/InputStream;")); 96 streamFactoryCollection.add(new StaticFieldLoadStreamFactory("java.io.OutputStream", 97 "java.lang.System", "out", "Ljava/io/PrintStream;")); 98 streamFactoryCollection.add(new StaticFieldLoadStreamFactory("java.io.OutputStream", 99 "java.lang.System", "err", "Ljava/io/PrintStream;")); 100 101 streamFactoryCollection.add(new InstanceFieldLoadStreamFactory("java.io.InputStream")); 103 streamFactoryCollection.add(new InstanceFieldLoadStreamFactory("java.io.Reader")); 104 105 streamFactoryCollection.add(new InstanceFieldLoadStreamFactory("java.io.OutputStream")); 112 streamFactoryCollection.add(new InstanceFieldLoadStreamFactory("java.io.Writer")); 113 114 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 116 "prepareStatement", "(Ljava/lang/String;)Ljava/sql/PreparedStatement;", 117 "ODR_OPEN_DATABASE_RESOURCE")); 118 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 119 "prepareStatement", "(Ljava/lang/String;I)Ljava/sql/PreparedStatement;", 120 "ODR_OPEN_DATABASE_RESOURCE")); 121 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 122 "prepareStatement", "(Ljava/lang/String;[I)Ljava/sql/PreparedStatement;", 123 "ODR_OPEN_DATABASE_RESOURCE")); 124 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 125 "prepareStatement", "(Ljava/lang/String;II)Ljava/sql/PreparedStatement;", 126 "ODR_OPEN_DATABASE_RESOURCE")); 127 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 128 "prepareStatement", "(Ljava/lang/String;III)Ljava/sql/PreparedStatement;", 129 "ODR_OPEN_DATABASE_RESOURCE")); 130 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 131 "prepareStatement", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/PreparedStatement;", 132 "ODR_OPEN_DATABASE_RESOURCE")); 133 134 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 135 "prepareCall", "(Ljava/lang/String;)Ljava/sql/CallableStatement;", 136 "ODR_OPEN_DATABASE_RESOURCE")); 137 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 138 "prepareCall", "(Ljava/lang/String;II)Ljava/sql/CallableStatement;", 139 "ODR_OPEN_DATABASE_RESOURCE")); 140 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 141 "prepareCall", "(Ljava/lang/String;III)Ljava/sql/CallableStatement;", 142 "ODR_OPEN_DATABASE_RESOURCE")); 143 144 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.DriverManager", 145 "getConnection", "(Ljava/lang/String;)Ljava/sql/Connection;", 146 "ODR_OPEN_DATABASE_RESOURCE")); 147 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.DriverManager", 148 "getConnection", "(Ljava/lang/String;Ljava/util/Properties;)Ljava/sql/Connection;", 149 "ODR_OPEN_DATABASE_RESOURCE")); 150 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.DriverManager", 151 "getConnection", 152 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;", 153 "ODR_OPEN_DATABASE_RESOURCE")); 154 streamFactoryCollection.add(new MethodReturnValueStreamFactory("javax.sql.DataSource", 155 "getConnection", 156 "()Ljava/sql/Connection;", 157 "ODR_OPEN_DATABASE_RESOURCE")); 158 streamFactoryCollection.add(new MethodReturnValueStreamFactory("javax.sql.DataSource", 159 "getConnection", 160 "(Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;", 161 "ODR_OPEN_DATABASE_RESOURCE")); 162 163 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 164 "createStatement", "()Ljava/sql/Statement;", 165 "ODR_OPEN_DATABASE_RESOURCE")); 166 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 167 "createStatement", "(II)Ljava/sql/Statement;", 168 "ODR_OPEN_DATABASE_RESOURCE")); 169 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 170 "createStatement", "(III)Ljava/sql/Statement;", 171 "ODR_OPEN_DATABASE_RESOURCE")); 172 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 173 "createStatement", "(Ljava/lang/String;I)Ljava/sql/PreparedStatement;", 174 "ODR_OPEN_DATABASE_RESOURCE")); 175 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 176 "createStatement", "(Ljava/lang/String;II)Ljava/sql/PreparedStatement;", 177 "ODR_OPEN_DATABASE_RESOURCE")); 178 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 179 "createStatement", "(Ljava/lang/String;III)Ljava/sql/PreparedStatement;", 180 "ODR_OPEN_DATABASE_RESOURCE")); 181 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 182 "createStatement", "(Ljava/lang/String;[I)Ljava/sql/PreparedStatement;", 183 "ODR_OPEN_DATABASE_RESOURCE")); 184 streamFactoryCollection.add(new MethodReturnValueStreamFactory("java.sql.Connection", 185 "createStatement", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/PreparedStatement;", 186 "ODR_OPEN_DATABASE_RESOURCE")); 187 188 streamFactoryList = streamFactoryCollection.toArray(new StreamFactory[streamFactoryCollection.size()]); 189 } 190 191 194 195 private static class PotentialOpenStream { 196 public final String bugType; 197 public final int priority; 198 public final Stream stream; 199 200 public PotentialOpenStream(String bugType, int priority, Stream stream) { 201 this.bugType = bugType; 202 this.priority = priority; 203 this.stream = stream; 204 } 205 } 206 207 210 211 private List<PotentialOpenStream> potentialOpenStreamList; 212 213 216 217 public FindOpenStream(BugReporter bugReporter) { 218 super(bugReporter); 219 this.potentialOpenStreamList = new LinkedList<PotentialOpenStream>(); 220 } 221 222 @Override 223 public Object clone() { 224 try { 225 return super.clone(); 226 } catch (CloneNotSupportedException e) { 227 throw new AssertionError (e); 228 } 229 } 230 231 private static final String [] PRESCREEN_CLASS_LIST = 236 { "Stream", "Reader", "Writer", "DriverManager", "Connection" }; 237 238 241 @Override 242 public void visitClassContext(ClassContext classContext) { 243 JavaClass jclass = classContext.getJavaClass(); 244 245 boolean sawResourceClass = false; 250 for (int i = 0; i < jclass.getConstantPool().getLength(); ++i) { 251 Constant constant = jclass.getConstantPool().getConstant(i); 252 253 if (constant instanceof ConstantMethodref) { 254 ConstantMethodref cmr = (ConstantMethodref) constant; 255 256 int classIndex = cmr.getClassIndex(); 257 String className = jclass.getConstantPool().getConstantString( 258 classIndex, Constants.CONSTANT_Class); 259 260 if (DEBUG) System.out.println("FindOpenStream: saw class " + className); 261 262 if (className != null) { 263 for (String aPRESCREEN_CLASS_LIST : PRESCREEN_CLASS_LIST) { 264 if (className.indexOf(aPRESCREEN_CLASS_LIST) >= 0) { 265 sawResourceClass = true; 266 break; 267 } 268 } 269 } 270 } 271 } 272 273 if (sawResourceClass) { 274 super.visitClassContext(classContext); 275 } 276 } 277 278 @Override 279 public boolean prescreen(ClassContext classContext, Method method) { 280 BitSet bytecodeSet = classContext.getBytecodeSet(method); 281 if (bytecodeSet == null) return false; 282 return bytecodeSet.get(Constants.NEW) 283 || bytecodeSet.get(Constants.INVOKEINTERFACE) 284 || bytecodeSet.get(Constants.INVOKESPECIAL) 285 || bytecodeSet.get(Constants.INVOKESTATIC) 286 || bytecodeSet.get(Constants.INVOKEVIRTUAL); 287 } 288 289 @Override 290 public StreamResourceTracker getResourceTracker(ClassContext classContext, Method method) { 291 return new StreamResourceTracker(streamFactoryList, bugReporter); 292 } 293 294 public static boolean isMainMethod(Method method) { 295 return method.isStatic() 296 && method.getName().equals("main") 297 && method.getSignature().equals("([Ljava/lang/String;)V"); 298 } 299 300 @Override 301 public void analyzeMethod(ClassContext classContext, Method method, 302 StreamResourceTracker resourceTracker, 303 ResourceCollection<Stream> resourceCollection) 304 throws CFGBuilderException, DataflowAnalysisException { 305 306 if (isMainMethod(method)) return; 307 308 potentialOpenStreamList.clear(); 309 310 JavaClass javaClass = classContext.getJavaClass(); 311 MethodGen methodGen = classContext.getMethodGen(method); 312 if (methodGen == null) return; 313 CFG cfg = classContext.getCFG(method); 314 315 try { 319 Type[] parameterTypeList = Type.getArgumentTypes(methodGen.getSignature()); 320 Location firstLocation = new Location(cfg.getEntry().getFirstInstruction(), cfg.getEntry()); 321 322 int local = methodGen.isStatic() ? 0 : 1; 323 324 for (Type type : parameterTypeList) { 325 if (type instanceof ObjectType) { 326 ObjectType objectType = (ObjectType) type; 327 for (ObjectType streamBase : streamBaseList) { 328 if (Hierarchy.isSubtype(objectType, streamBase)) { 329 334 Stream paramStream = 335 new Stream(firstLocation, objectType.getClassName(), streamBase.getClassName()); 336 paramStream.setIsOpenOnCreation(true); 337 paramStream.setOpenLocation(firstLocation); 338 paramStream.setInstanceParam(local); 339 resourceCollection.addPreexistingResource(paramStream); 340 341 break; 342 } 343 } 344 } 345 346 switch (type.getType()) { 347 case Constants.T_LONG: 348 case Constants.T_DOUBLE: 349 local += 2; 350 break; 351 default: 352 local += 1; 353 break; 354 } 355 } 356 } catch (ClassNotFoundException e) { 357 bugReporter.reportMissingClass(e); 358 } 359 360 resourceTracker.setResourceCollection(resourceCollection); 364 365 super.analyzeMethod(classContext, method, resourceTracker, resourceCollection); 366 367 resourceTracker.markTransitiveUninterestingStreamEscapes(); 376 377 for (Iterator<Stream> i = resourceCollection.resourceIterator(); i.hasNext();) { 380 Stream stream = i.next(); 381 StreamEquivalenceClass equivalenceClass = resourceTracker.getStreamEquivalenceClass(stream); 382 if (stream.isClosed()) 383 equivalenceClass.setClosed(); 384 } 385 386 for (PotentialOpenStream pos : potentialOpenStreamList) { 391 Stream stream = pos.stream; 392 if (stream.isClosed()) 393 continue; 396 397 if (stream.isUninteresting()) 398 continue; 399 400 Location openLocation = stream.getOpenLocation(); 401 if (openLocation == null) 402 continue; 403 404 if (IGNORE_WRAPPED_UNINTERESTING_STREAMS 405 && resourceTracker.isUninterestingStreamEscape(stream)) 406 continue; 407 408 String sourceFile = javaClass.getSourceFileName(); 409 bugReporter.reportBug(new BugInstance(this, pos.bugType, pos.priority) 410 .addClassAndMethod(methodGen, sourceFile) 411 .addSourceLine(classContext, methodGen, sourceFile, stream.getLocation().getHandle())); 412 } 413 } 414 415 @Override 416 public void inspectResult(ClassContext classContext, MethodGen methodGen, CFG cfg, 417 Dataflow<ResourceValueFrame, ResourceValueAnalysis<Stream>> dataflow, Stream stream) { 418 419 ResourceValueFrame exitFrame = dataflow.getResultFact(cfg.getExit()); 420 421 int exitStatus = exitFrame.getStatus(); 422 if (exitStatus == ResourceValueFrame.OPEN 423 || exitStatus == ResourceValueFrame.OPEN_ON_EXCEPTION_PATH) { 424 425 428 String bugType = stream.getBugType(); 429 int priority = NORMAL_PRIORITY; 430 if (exitStatus == ResourceValueFrame.OPEN_ON_EXCEPTION_PATH) { 431 bugType += "_EXCEPTION_PATH"; 432 priority = LOW_PRIORITY; 433 } 434 435 potentialOpenStreamList.add(new PotentialOpenStream(bugType, priority, stream)); 436 } else if (exitStatus == ResourceValueFrame.CLOSED) { 437 stream.setClosed(); 441 } 442 } 443 444 public static void main(String [] argv) throws Exception { 445 if (argv.length != 3) { 446 System.err.println("Usage: " + FindOpenStream.class.getName() + 447 " <class file> <method name> <bytecode offset>"); 448 System.exit(1); 449 } 450 451 String classFile = argv[0]; 452 String methodName = argv[1]; 453 int offset = Integer.parseInt(argv[2]); 454 455 ResourceValueAnalysisTestDriver<Stream, StreamResourceTracker> driver = 456 new ResourceValueAnalysisTestDriver<Stream, StreamResourceTracker>() { 457 @Override 458 public StreamResourceTracker createResourceTracker(ClassContext classContext, Method method) { 459 return new StreamResourceTracker(streamFactoryList, classContext.getLookupFailureCallback()); 460 } 461 }; 462 463 driver.execute(classFile, methodName, offset); 464 } 465 466 } 467 468 | Popular Tags |