1 19 20 package edu.umd.cs.findbugs.detect; 21 22 import java.util.BitSet ; 23 import java.util.HashSet ; 24 import java.util.Iterator ; 25 import java.util.LinkedList ; 26 import java.util.List ; 27 import java.util.Set ; 28 import java.util.SortedSet ; 29 import java.util.TreeSet ; 30 31 import org.apache.bcel.Constants; 32 import org.apache.bcel.classfile.JavaClass; 33 import org.apache.bcel.classfile.Method; 34 import org.apache.bcel.generic.ATHROW; 35 import org.apache.bcel.generic.ConstantPoolGen; 36 import org.apache.bcel.generic.Instruction; 37 import org.apache.bcel.generic.InstructionHandle; 38 import org.apache.bcel.generic.InstructionTargeter; 39 import org.apache.bcel.generic.InvokeInstruction; 40 import org.apache.bcel.generic.MethodGen; 41 import org.apache.bcel.generic.PUTFIELD; 42 import org.apache.bcel.generic.ReturnInstruction; 43 import org.apache.bcel.generic.Type; 44 45 import edu.umd.cs.findbugs.BugAnnotation; 46 import edu.umd.cs.findbugs.BugInstance; 47 import edu.umd.cs.findbugs.BugReporter; 48 import edu.umd.cs.findbugs.Detector; 49 import edu.umd.cs.findbugs.FieldAnnotation; 50 import edu.umd.cs.findbugs.FindBugsAnalysisFeatures; 51 import edu.umd.cs.findbugs.LocalVariableAnnotation; 52 import edu.umd.cs.findbugs.SourceLineAnnotation; 53 import edu.umd.cs.findbugs.SystemProperties; 54 import edu.umd.cs.findbugs.annotations.NonNull; 55 import edu.umd.cs.findbugs.ba.AnalysisContext; 56 import edu.umd.cs.findbugs.ba.BasicBlock; 57 import edu.umd.cs.findbugs.ba.CFG; 58 import edu.umd.cs.findbugs.ba.CFGBuilderException; 59 import edu.umd.cs.findbugs.ba.ClassContext; 60 import edu.umd.cs.findbugs.ba.DataflowAnalysisException; 61 import edu.umd.cs.findbugs.ba.DataflowValueChooser; 62 import edu.umd.cs.findbugs.ba.Edge; 63 import edu.umd.cs.findbugs.ba.Frame; 64 import edu.umd.cs.findbugs.ba.Hierarchy; 65 import edu.umd.cs.findbugs.ba.JavaClassAndMethod; 66 import edu.umd.cs.findbugs.ba.Location; 67 import edu.umd.cs.findbugs.ba.MissingClassException; 68 import edu.umd.cs.findbugs.ba.NullnessAnnotation; 69 import edu.umd.cs.findbugs.ba.NullnessAnnotationDatabase; 70 import edu.umd.cs.findbugs.ba.SignatureConverter; 71 import edu.umd.cs.findbugs.ba.XFactory; 72 import edu.umd.cs.findbugs.ba.XField; 73 import edu.umd.cs.findbugs.ba.XMethod; 74 import edu.umd.cs.findbugs.ba.interproc.PropertyDatabase; 75 import edu.umd.cs.findbugs.ba.npe.IsNullValue; 76 import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow; 77 import edu.umd.cs.findbugs.ba.npe.IsNullValueFrame; 78 import edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonCollector; 79 import edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonFinder; 80 import edu.umd.cs.findbugs.ba.npe.ParameterNullnessProperty; 81 import edu.umd.cs.findbugs.ba.npe.ParameterNullnessPropertyDatabase; 82 import edu.umd.cs.findbugs.ba.npe.RedundantBranch; 83 import edu.umd.cs.findbugs.ba.type.TypeDataflow; 84 import edu.umd.cs.findbugs.ba.type.TypeFrame; 85 import edu.umd.cs.findbugs.ba.vna.AvailableLoad; 86 import edu.umd.cs.findbugs.ba.vna.ValueNumber; 87 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow; 88 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame; 89 import edu.umd.cs.findbugs.classfile.FieldDescriptor; 90 import edu.umd.cs.findbugs.props.GeneralWarningProperty; 91 import edu.umd.cs.findbugs.props.WarningPropertySet; 92 import edu.umd.cs.findbugs.props.WarningPropertyUtil; 93 import edu.umd.cs.findbugs.visitclass.Util; 94 95 104 public class FindNullDeref 105 implements Detector, NullDerefAndRedundantComparisonCollector { 106 107 private static final boolean DEBUG = SystemProperties.getBoolean("fnd.debug"); 108 private static final boolean DEBUG_NULLARG = SystemProperties.getBoolean("fnd.debug.nullarg"); 109 private static final boolean DEBUG_NULLRETURN = SystemProperties.getBoolean("fnd.debug.nullreturn"); 110 private static final boolean REPORT_SAFE_METHOD_TARGETS = true; 111 112 private static final String METHOD = SystemProperties.getProperty("fnd.method"); 113 private static final String CLASS = SystemProperties.getProperty("fnd.class"); 114 115 private BugReporter bugReporter; 117 118 private ParameterNullnessPropertyDatabase unconditionalDerefParamDatabase; 120 private boolean checkedDatabases = false; 121 122 123 private ClassContext classContext; 125 private Method method; 126 private IsNullValueDataflow invDataflow; 127 private BitSet previouslyDeadBlocks; 128 private NullnessAnnotation methodAnnotation; 129 130 public FindNullDeref(BugReporter bugReporter) { 131 this.bugReporter = bugReporter; 132 } 133 134 public void visitClassContext(ClassContext classContext) { 135 this.classContext = classContext; 136 137 String currentMethod = null; 138 139 JavaClass jclass = classContext.getJavaClass(); 140 String className = jclass.getClassName(); 141 if (CLASS != null && !className.equals(CLASS)) return; 142 Method[] methodList = jclass.getMethods(); 143 for (Method method : methodList) { 144 try { 145 if (method.isAbstract() || method.isNative() 146 || method.getCode() == null) 147 continue; 148 149 MethodGen mg = classContext.getMethodGen(method); 150 if (mg == null) { 151 continue; 152 } 153 currentMethod = SignatureConverter.convertMethodSignature(mg); 154 155 156 if (METHOD != null && !method.getName().equals(METHOD)) 157 continue; 158 if (DEBUG) 159 System.out 160 .println("Checking for NP in " + currentMethod); 161 analyzeMethod(classContext, method); 162 } catch (MissingClassException e) { 163 bugReporter.reportMissingClass(e.getClassNotFoundException()); 164 } catch (DataflowAnalysisException e) { 165 bugReporter.logError("While analyzing " + currentMethod + ": FindNullDeref caught dae exception", e); 166 } catch (CFGBuilderException e) { 167 bugReporter.logError("While analyzing " + currentMethod + ": FindNullDeref caught cfgb exception", e); 168 } 169 170 } 171 } 172 173 private void analyzeMethod(ClassContext classContext, Method method) 174 throws CFGBuilderException, DataflowAnalysisException { 175 if (DEBUG || DEBUG_NULLARG) 176 System.out.println("Pre FND "); 177 178 MethodGen methodGen = classContext.getMethodGen(method); 179 if (methodGen == null) 180 return; 181 if (!checkedDatabases) { 182 checkDatabases(); 183 checkedDatabases = true; 184 } 185 186 this.method = method; 187 this.methodAnnotation = getMethodNullnessAnnotation(); 188 189 190 if (DEBUG || DEBUG_NULLARG) 191 System.out.println("FND: " + SignatureConverter.convertMethodSignature(methodGen)); 192 193 this.previouslyDeadBlocks = findPreviouslyDeadBlocks(); 194 195 invDataflow = classContext.getIsNullValueDataflow(method); 197 198 NullDerefAndRedundantComparisonFinder worker = new NullDerefAndRedundantComparisonFinder( 202 classContext, 203 method, 204 this); 205 worker.execute(); 206 207 208 checkCallSitesAndReturnInstructions(); 209 210 } 211 212 220 private BitSet findPreviouslyDeadBlocks() throws DataflowAnalysisException, CFGBuilderException { 221 BitSet deadBlocks = new BitSet (); 222 ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method); 223 for (Iterator <BasicBlock> i = vnaDataflow.getCFG().blockIterator(); i.hasNext();) { 224 BasicBlock block = i.next(); 225 ValueNumberFrame vnaFrame = vnaDataflow.getStartFact(block); 226 if (vnaFrame.isTop()) { 227 deadBlocks.set(block.getId()); 228 } 229 } 230 231 return deadBlocks; 232 } 233 234 238 private void checkDatabases() { 239 AnalysisContext analysisContext = AnalysisContext 240 .currentAnalysisContext(); 241 unconditionalDerefParamDatabase = analysisContext 242 .getUnconditionalDerefParamDatabase(); 243 if (false) { 244 XFactory xFactory = AnalysisContext.currentXFactory(); 245 246 for (XMethod xMethod : unconditionalDerefParamDatabase.getKeys()) { 247 XMethod m2 = xFactory.intern(xMethod); 248 if (!xMethod.equals(m2)) { 249 System.out.println("WWW: " + xMethod); 250 System.out.println(" ->: " + m2); 251 } 252 } 253 } 254 } 255 256 private< 257 DatabaseType extends PropertyDatabase<?,?>> boolean isDatabaseNonEmpty(DatabaseType database) { 258 return database != null && !database.isEmpty(); 259 } 260 261 265 private NullnessAnnotation getMethodNullnessAnnotation() { 266 267 if (method.getSignature().indexOf(")L") >= 0 || method.getSignature().indexOf(")[") >= 0 ) { 268 if (DEBUG_NULLRETURN) { 269 System.out.println("Checking return annotation for " + 270 SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method)); 271 } 272 273 XMethod m = XFactory.createXMethod(classContext.getJavaClass(), method); 274 return AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase() 275 .getResolvedAnnotation(m, false); 276 } 277 return NullnessAnnotation.UNKNOWN_NULLNESS; 278 } 279 280 private void checkCallSitesAndReturnInstructions() 281 throws CFGBuilderException, DataflowAnalysisException { 282 ConstantPoolGen cpg = classContext.getConstantPoolGen(); 283 TypeDataflow typeDataflow = classContext.getTypeDataflow(method); 284 285 for (Iterator <Location> i = classContext.getCFG(method).locationIterator(); i.hasNext();) { 286 Location location = i.next(); 287 Instruction ins = location.getHandle().getInstruction(); 288 try { 289 if (ins instanceof InvokeInstruction) { 290 examineCallSite(location, cpg, typeDataflow); 291 } else if (methodAnnotation == NullnessAnnotation.NONNULL && ins.getOpcode() == Constants.ARETURN) { 292 examineReturnInstruction(location); 293 } else if (ins instanceof PUTFIELD) { 294 examinePutfieldInstruction(location, (PUTFIELD) ins, cpg); 295 } 296 } catch (ClassNotFoundException e) { 297 bugReporter.reportMissingClass(e); 298 } 299 } 300 } 301 302 private void examineCallSite( 303 Location location, 304 ConstantPoolGen cpg, 305 TypeDataflow typeDataflow) 306 throws DataflowAnalysisException, CFGBuilderException, ClassNotFoundException { 307 308 InvokeInstruction invokeInstruction = (InvokeInstruction) 309 location.getHandle().getInstruction(); 310 311 312 String methodName = invokeInstruction.getName(cpg); 313 String signature = invokeInstruction.getSignature(cpg); 314 315 if (methodName.equals("equals") && signature.equals("(Ljava/lang/Object;)Z")) 319 return; 320 321 int returnTypeStart = signature.indexOf(')'); 322 if (returnTypeStart < 0) 323 return; 324 String paramList = signature.substring(0, returnTypeStart + 1); 325 326 if (paramList.equals("()") || 327 (paramList.indexOf("L") < 0 && paramList.indexOf('[') < 0)) 328 return; 330 331 IsNullValueFrame frame = 333 classContext.getIsNullValueDataflow(method).getFactAtLocation(location); 334 if (!frame.isValid()) 335 return; 336 BitSet nullArgSet = frame.getArgumentSet(invokeInstruction, cpg, new DataflowValueChooser<IsNullValue>() { 337 public boolean choose(IsNullValue value) { 338 return value.mightBeNull() && !value.isException() && !value.isReturnValue(); 342 } 343 }); 344 BitSet definitelyNullArgSet = frame.getArgumentSet(invokeInstruction, cpg, new DataflowValueChooser<IsNullValue>() { 345 public boolean choose(IsNullValue value) { 346 return value.isDefinitelyNull(); 347 } 348 }); 349 if (nullArgSet.isEmpty()) 350 return; 351 if (DEBUG_NULLARG) { 352 System.out.println("Null arguments passed: " + nullArgSet); 353 System.out.println("Frame is: " + frame); 354 System.out.println("# arguments: " + frame.getNumArguments(invokeInstruction, cpg)); 355 XMethod xm = XFactory.createXMethod(invokeInstruction, cpg); 356 System.out.print("Signature: " + xm.getSignature()); 357 } 358 359 if (unconditionalDerefParamDatabase != null) { 360 checkUnconditionallyDereferencedParam(location, cpg, typeDataflow, invokeInstruction, nullArgSet, definitelyNullArgSet); 361 } 362 363 364 if (DEBUG_NULLARG) { 365 System.out.println("Checking nonnull params"); 366 } 367 checkNonNullParam(location, cpg, typeDataflow, invokeInstruction, nullArgSet, definitelyNullArgSet); 368 369 } 370 371 private void examinePutfieldInstruction(Location location, PUTFIELD ins, ConstantPoolGen cpg) throws DataflowAnalysisException, CFGBuilderException { 372 373 IsNullValueDataflow invDataflow = classContext.getIsNullValueDataflow(method); 374 IsNullValueFrame frame = invDataflow.getFactAtLocation(location); 375 if (!frame.isValid()) 376 return; 377 IsNullValue tos = frame.getTopValue(); 378 if (tos.mightBeNull()) { 379 XField field = XFactory.createXField(ins, cpg); 380 NullnessAnnotation annotation = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase().getResolvedAnnotation(field, false); 381 if (annotation == NullnessAnnotation.NONNULL) { 382 383 MethodGen methodGen = classContext.getMethodGen(method); 384 String sourceFile = classContext.getJavaClass().getSourceFileName(); 385 386 BugInstance warning = new BugInstance("NP_STORE_INTO_NONNULL_FIELD", tos.isDefinitelyNull() ? 387 HIGH_PRIORITY : NORMAL_PRIORITY) 388 .addClassAndMethod(methodGen, sourceFile) 389 .addField(field) 390 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle()); 391 392 bugReporter.reportBug(warning); 393 } 394 } 395 } 396 private void examineReturnInstruction(Location location) throws DataflowAnalysisException, CFGBuilderException { 397 if (DEBUG_NULLRETURN) { 398 System.out.println("Checking null return at " + location); 399 } 400 401 IsNullValueDataflow invDataflow = classContext.getIsNullValueDataflow(method); 402 IsNullValueFrame frame = invDataflow.getFactAtLocation(location); 403 if (!frame.isValid()) 404 return; 405 IsNullValue tos = frame.getTopValue(); 406 if (tos.mightBeNull()) { 407 MethodGen methodGen = classContext.getMethodGen(method); 408 String sourceFile = classContext.getJavaClass().getSourceFileName(); 409 410 String bugPattern = "NP_NONNULL_RETURN_VIOLATION"; 411 String methodName = method.getName(); 412 if (methodName.equals("clone")) 413 bugPattern = "NP_CLONE_COULD_RETURN_NULL"; 414 else if (methodName.equals("toString")) 415 bugPattern = "NP_TOSTRING_COULD_RETURN_NULL"; 416 BugInstance warning = new BugInstance(bugPattern, tos.isDefinitelyNull() ? 417 HIGH_PRIORITY : NORMAL_PRIORITY) 418 .addClassAndMethod(methodGen, sourceFile) 419 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle()); 420 421 bugReporter.reportBug(warning); 422 } 423 } 424 425 private void checkUnconditionallyDereferencedParam( 426 Location location, 427 ConstantPoolGen cpg, 428 TypeDataflow typeDataflow, 429 InvokeInstruction invokeInstruction, 430 BitSet nullArgSet, BitSet definitelyNullArgSet) throws DataflowAnalysisException, ClassNotFoundException { 431 432 boolean caught = inCatchNullBlock(location); 433 if (caught && skipIfInsideCatchNull()) return; 434 435 TypeFrame typeFrame = typeDataflow.getFactAtLocation(location); 437 Set <JavaClassAndMethod> targetMethodSet = Hierarchy.resolveMethodCallTargets(invokeInstruction, typeFrame, cpg); 438 if (DEBUG_NULLARG) { 439 System.out.println("Possibly called methods: " + targetMethodSet); 440 } 441 442 BitSet unconditionallyDereferencedNullArgSet = new BitSet (); 444 List <JavaClassAndMethod> dangerousCallTargetList = new LinkedList <JavaClassAndMethod>(); 445 List <JavaClassAndMethod> veryDangerousCallTargetList = new LinkedList <JavaClassAndMethod>(); 446 for (JavaClassAndMethod targetMethod : targetMethodSet) { 447 if (DEBUG_NULLARG) { 448 System.out.println("For target method " + targetMethod); 449 } 450 451 ParameterNullnessProperty property = unconditionalDerefParamDatabase.getProperty(targetMethod.toXMethod()); 452 if (property == null) 453 continue; 454 if (DEBUG_NULLARG) { 455 System.out.println("\tUnconditionally dereferenced params: " + property); 456 } 457 458 BitSet targetUnconditionallyDereferencedNullArgSet = 459 property.getViolatedParamSet(nullArgSet); 460 461 if (targetUnconditionallyDereferencedNullArgSet.isEmpty()) 462 continue; 463 464 dangerousCallTargetList.add(targetMethod); 465 466 unconditionallyDereferencedNullArgSet.or(targetUnconditionallyDereferencedNullArgSet); 467 468 if (!property.getViolatedParamSet(definitelyNullArgSet).isEmpty()) 469 veryDangerousCallTargetList.add(targetMethod); 470 } 471 472 if (dangerousCallTargetList.isEmpty()) 473 return; 474 475 WarningPropertySet propertySet = new WarningPropertySet(); 476 477 Set <JavaClassAndMethod> safeCallTargetSet = new HashSet <JavaClassAndMethod>(); 479 safeCallTargetSet.addAll(targetMethodSet); 480 safeCallTargetSet.removeAll(dangerousCallTargetList); 481 if (safeCallTargetSet.isEmpty()) { 482 propertySet.addProperty(NullArgumentWarningProperty.ALL_DANGEROUS_TARGETS); 483 if (dangerousCallTargetList.size() == 1) { 484 propertySet.addProperty(NullArgumentWarningProperty.MONOMORPHIC_CALL_SITE); 485 } 486 } 487 488 boolean privateCall = 490 safeCallTargetSet.isEmpty() 491 && dangerousCallTargetList.size() == 1 492 && dangerousCallTargetList.get(0).getMethod().isPrivate(); 493 494 MethodGen methodGen = classContext.getMethodGen(method); 495 String sourceFile = classContext.getJavaClass().getSourceFileName(); 496 497 String bugType; 498 int priority; 499 if (privateCall 500 || invokeInstruction.getOpcode() == Constants.INVOKESTATIC 501 || invokeInstruction.getOpcode() == Constants.INVOKESPECIAL) { 502 bugType = "NP_NULL_PARAM_DEREF_NONVIRTUAL"; 503 priority = HIGH_PRIORITY; 504 } else if (safeCallTargetSet.isEmpty()) { 505 bugType = "NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS"; 506 priority = NORMAL_PRIORITY; 507 } else { 508 bugType = "NP_NULL_PARAM_DEREF"; 509 priority = LOW_PRIORITY; 510 } 511 512 if (caught) priority++; 513 if (dangerousCallTargetList.size() > veryDangerousCallTargetList.size()) 514 priority++; 515 else 516 propertySet.addProperty(NullArgumentWarningProperty.ACTUAL_PARAMETER_GUARANTEED_NULL); 517 518 BugInstance warning = new BugInstance(bugType, priority) 519 .addClassAndMethod(methodGen, sourceFile) 520 .addMethod(XFactory.createXMethod(invokeInstruction, cpg)).describe("METHOD_CALLED") 521 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle()) 522 ; 523 524 addParamAnnotations(definitelyNullArgSet, unconditionallyDereferencedNullArgSet, propertySet, warning); 526 527 for (JavaClassAndMethod dangerousCallTarget : veryDangerousCallTargetList) { 529 warning.addMethod(dangerousCallTarget).describe("METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL"); 530 } 531 dangerousCallTargetList.removeAll(veryDangerousCallTargetList); 532 for (JavaClassAndMethod dangerousCallTarget : dangerousCallTargetList) { 534 warning.addMethod(dangerousCallTarget).describe("METHOD_DANGEROUS_TARGET"); 535 } 536 537 for (JavaClassAndMethod safeMethod : safeCallTargetSet) { 541 warning.addMethod(safeMethod).describe("METHOD_SAFE_TARGET"); 542 } 543 544 decorateWarning(location, propertySet, warning); 545 bugReporter.reportBug(warning); 546 } 547 548 private void decorateWarning(Location location, WarningPropertySet propertySet, BugInstance warning) { 549 if (FindBugsAnalysisFeatures.isRelaxedMode()) { 550 WarningPropertyUtil.addPropertiesForLocation(propertySet, classContext, method, location); 551 } 552 propertySet.decorateBugInstance(warning); 553 } 554 555 private void addParamAnnotations( 556 BitSet definitelyNullArgSet, 557 BitSet violatedParamSet, 558 WarningPropertySet propertySet, 559 BugInstance warning) { 560 for (int i = 0; i < 32; ++i) { 561 if (violatedParamSet.get(i)) { 562 boolean definitelyNull = definitelyNullArgSet.get(i); 563 564 if (definitelyNull) { 565 propertySet.addProperty(NullArgumentWarningProperty.ARG_DEFINITELY_NULL); 566 } 567 568 warning.addInt(i + 1).describe( 570 definitelyNull ? "INT_NULL_ARG" : "INT_MAYBE_NULL_ARG"); 571 } 572 } 573 } 574 575 587 private void checkNonNullParam( 588 Location location, 589 ConstantPoolGen cpg, 590 TypeDataflow typeDataflow, 591 InvokeInstruction invokeInstruction, 592 BitSet nullArgSet, 593 BitSet definitelyNullArgSet) throws ClassNotFoundException { 594 595 boolean caught = inCatchNullBlock(location); 596 if (caught && skipIfInsideCatchNull()) return; 597 598 XMethod m = XFactory.createXMethod(invokeInstruction, cpg); 599 600 NullnessAnnotationDatabase db 601 = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase(); 602 for(int i=nullArgSet.nextSetBit(0); i>=0; i=nullArgSet.nextSetBit(i+1)) { 603 int paramNum = 0; 604 605 String signature = invokeInstruction.getSignature(cpg); 606 Type[] args = Type.getArgumentTypes(signature); 607 int words =0; 608 while (words < i) 609 words += args[paramNum++].getSize(); 610 611 if (db.parameterMustBeNonNull(m, paramNum)) { 612 boolean definitelyNull = definitelyNullArgSet.get(i); 613 if (DEBUG_NULLARG) { 614 System.out.println("QQQ2: " + i + " -- " + paramNum + " is null"); 615 System.out.println("QQQ nullArgSet: " + nullArgSet); 616 System.out.println("QQQ dnullArgSet: " + definitelyNullArgSet); 617 } 618 619 MethodGen methodGen = classContext.getMethodGen(method); 620 String sourceFile = classContext.getJavaClass().getSourceFileName(); 621 622 int priority = definitelyNull ? HIGH_PRIORITY : NORMAL_PRIORITY; 623 if (caught) priority++; 624 BugInstance warning = new BugInstance("NP_NONNULL_PARAM_VIOLATION", 625 priority) 626 .addClassAndMethod(methodGen, sourceFile) 627 .addMethod(m).describe("METHOD_CALLED") 628 .addInt(i).describe("INT_NONNULL_PARAM") 629 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle()); 630 631 bugReporter.reportBug(warning); 632 } 633 } 634 635 } 636 637 public void report() { 638 } 639 640 public boolean skipIfInsideCatchNull() { 641 return classContext.getJavaClass().getClassName().indexOf("Test") >= 0 642 || method.getName().indexOf("test") >= 0 || method.getName().indexOf("Test") >= 0; 643 } 644 public void foundNullDeref(ClassContext classContext, Location location, ValueNumber valueNumber, IsNullValue refValue, ValueNumberFrame vnaFrame) { 645 WarningPropertySet propertySet = new WarningPropertySet(); 646 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) return; 647 648 boolean onExceptionPath = refValue.isException(); 649 if (onExceptionPath) { 650 propertySet.addProperty(GeneralWarningProperty.ON_EXCEPTION_PATH); 651 } 652 int pc = location.getHandle().getPosition(); 653 BugAnnotation variable = findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame); 654 655 boolean duplicated = false; 656 try { 657 CFG cfg = classContext.getCFG(method); 658 duplicated = cfg.getLocationsContainingInstructionWithOffset(pc).size() > 1; 659 } catch (CFGBuilderException e) { 660 } 661 662 boolean caught = inCatchNullBlock(location); 663 if (caught && skipIfInsideCatchNull()) return; 664 665 if (!duplicated && refValue.isDefinitelyNull()) { 666 String type = onExceptionPath ? "NP_ALWAYS_NULL_EXCEPTION" : "NP_ALWAYS_NULL"; 667 int priority = onExceptionPath ? NORMAL_PRIORITY : HIGH_PRIORITY; 668 if (caught) priority++; 669 reportNullDeref(propertySet, classContext, method, location, type, priority, variable); 670 } else if (refValue.isNullOnSomePath() || duplicated && refValue.isDefinitelyNull()) { 671 String type = "NP_NULL_ON_SOME_PATH"; 672 int priority = NORMAL_PRIORITY; 673 if (caught) priority++; 674 if (onExceptionPath) type = "NP_NULL_ON_SOME_PATH_EXCEPTION"; 675 else if (refValue.isReturnValue()) 676 type = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"; 677 else if (refValue.isParamValue()) { 678 if (method.getName().equals("equals") 679 && method.getSignature().equals("(Ljava/lang/Object;)Z")) { 680 if (caught) return; 681 type = "NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT"; 682 683 } else 684 type = "NP_ARGUMENT_MIGHT_BE_NULL"; 685 } 686 687 if (DEBUG) System.out.println("Reporting null on some path: value=" + refValue); 688 reportNullDeref(propertySet, classContext, method, location, type, priority, variable); 689 } 690 } 691 692 699 public static BugAnnotation findAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) { 700 LocalVariableAnnotation ann = findLocalAnnotationFromValueNumber(method, location, valueNumber, vnaFrame); 701 if (ann != null && ann.isSignificant()) return ann; 702 FieldAnnotation field = findFieldAnnotationFromValueNumber(method, location, valueNumber, vnaFrame); 703 if (field != null) return field; 704 return ann; 705 } 706 707 public static FieldAnnotation findFieldAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) { 708 XField field = findXFieldFromValueNumber(method, location, valueNumber, vnaFrame); 709 if (field == null) return null; 710 return FieldAnnotation.fromXField(field); 711 } 712 713 public static XField findXFieldFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) { 714 if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop()) return null; 715 716 AvailableLoad load = vnaFrame.getLoad(valueNumber); 717 if (load != null) { 718 return load.getField(); 719 } 720 return null; 721 } 722 723 public static LocalVariableAnnotation findLocalAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber, ValueNumberFrame vnaFrame) { 724 725 if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop()) return null; 726 727 LocalVariableAnnotation localAnnotation = null; 728 for(int i = 0; i < vnaFrame.getNumLocals(); i++) { 729 if (valueNumber.equals(vnaFrame.getValue(i))) { 730 if (DEBUG) System.out.println("Found it in local " + i); 731 InstructionHandle handle = location.getHandle(); 732 int position1 = handle.getPrev().getPosition(); 733 int position2 = handle.getPosition(); 734 localAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, position1, position2); 735 if (localAnnotation != null) return localAnnotation; 736 } 737 } 738 return null; 739 } 740 741 private void reportNullDeref( 742 WarningPropertySet propertySet, 743 ClassContext classContext, 744 Method method, 745 Location location, 746 String type, 747 int priority, BugAnnotation variable) { 748 MethodGen methodGen = classContext.getMethodGen(method); 749 String sourceFile = classContext.getJavaClass().getSourceFileName(); 750 751 BugInstance bugInstance = new BugInstance(this, type, priority) 752 .addClassAndMethod(methodGen, sourceFile); 753 if (variable != null) 754 bugInstance.add(variable); 755 else bugInstance.add(new LocalVariableAnnotation("?",-1,-1)); 756 bugInstance.addSourceLine(classContext, methodGen, sourceFile, location.getHandle()); 757 758 if (FindBugsAnalysisFeatures.isRelaxedMode()) { 759 WarningPropertyUtil.addPropertiesForLocation(propertySet, classContext, method, location); 760 propertySet.decorateBugInstance(bugInstance); 761 } 762 763 bugReporter.reportBug(bugInstance); 764 } 765 766 public static boolean isThrower(BasicBlock target) { 767 InstructionHandle ins = target.getFirstInstruction(); 768 int maxCount = 7; 769 while (ins != null) { 770 if (maxCount-- <= 0) break; 771 Instruction i = ins.getInstruction(); 772 if (i instanceof ATHROW) { 773 return true; 774 } 775 if (i instanceof InstructionTargeter 776 || i instanceof ReturnInstruction) return false; 777 ins = ins.getNext(); 778 } 779 return false; 780 } 781 public void foundRedundantNullCheck(Location location, RedundantBranch redundantBranch) { 782 String sourceFile = classContext.getJavaClass().getSourceFileName(); 783 MethodGen methodGen = classContext.getMethodGen(method); 784 785 boolean isChecked = redundantBranch.firstValue.isChecked(); 786 boolean wouldHaveBeenAKaboom = redundantBranch.firstValue.wouldHaveBeenAKaboom(); 787 Location locationOfKaBoom = redundantBranch.firstValue.getLocationOfKaBoom(); 788 789 boolean createdDeadCode = false; 790 boolean infeasibleEdgeSimplyThrowsException = false; 791 Edge infeasibleEdge = redundantBranch.infeasibleEdge; 792 if (infeasibleEdge != null) { 793 if (DEBUG) System.out.println("Check if " + redundantBranch + " creates dead code"); 794 BasicBlock target = infeasibleEdge.getTarget(); 795 796 if (DEBUG) System.out.println("Target block is " + (target.isExceptionThrower() ? " exception thrower" : " not exception thrower")); 797 boolean empty = !target.isExceptionThrower() && 801 (target.isEmpty() || isGoto(target.getFirstInstruction().getInstruction())); 802 if (!empty) { 803 try { 804 if (classContext.getCFG(method).getNumIncomingEdges(target) > 1) { 805 if (DEBUG) System.out.println("Target of infeasible edge has multiple incoming edges"); 806 empty = true; 807 }} 808 catch (CFGBuilderException e) { 809 assert true; } 811 } 812 if (DEBUG) System.out.println("Target block is " + (empty ? "empty" : "not empty")); 813 814 if (!empty) { 815 if (isThrower(target)) infeasibleEdgeSimplyThrowsException = true; 816 817 } 818 if (!empty && !previouslyDeadBlocks.get(target.getId())) { 819 if (DEBUG) System.out.println("target was alive previously"); 820 IsNullValueFrame invFrame = invDataflow.getStartFact(target); 824 createdDeadCode = invFrame.isTop(); 825 if (DEBUG) System.out.println("target is now " + (createdDeadCode ? "dead" : "alive")); 826 827 } 828 } 829 830 int priority; 831 boolean valueIsNull = true; 832 String warning; 833 if (redundantBranch.secondValue == null) { 834 if (redundantBranch.firstValue.isDefinitelyNull() ) { 835 warning = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"; 836 priority = NORMAL_PRIORITY; 837 } 838 else { 839 warning = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"; 840 valueIsNull = false; 841 priority = isChecked ? NORMAL_PRIORITY : LOW_PRIORITY; 842 } 843 844 } else { 845 boolean bothNull = redundantBranch.firstValue.isDefinitelyNull() && redundantBranch.secondValue.isDefinitelyNull(); 846 if (redundantBranch.secondValue.isChecked()) isChecked = true; 847 if (redundantBranch.secondValue.wouldHaveBeenAKaboom()) { 848 wouldHaveBeenAKaboom = true; 849 locationOfKaBoom = redundantBranch.secondValue.getLocationOfKaBoom(); 850 } 851 if (bothNull) { 852 warning = "RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES"; 853 priority = NORMAL_PRIORITY; 854 } 855 else { 856 warning = "RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE"; 857 priority = isChecked ? NORMAL_PRIORITY : LOW_PRIORITY; 858 } 859 860 } 861 862 if (wouldHaveBeenAKaboom) { 863 priority = HIGH_PRIORITY; 864 warning = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"; 865 if (locationOfKaBoom == null) throw new NullPointerException ("location of KaBoom is null"); 866 } 867 868 if (DEBUG) System.out.println(createdDeadCode + " " + infeasibleEdgeSimplyThrowsException + " " + valueIsNull + " " + priority); 869 if (createdDeadCode && !infeasibleEdgeSimplyThrowsException) { 870 priority += 0; 871 } else if (createdDeadCode && infeasibleEdgeSimplyThrowsException) { 872 if (valueIsNull) 874 priority += 0; 875 else 876 priority += 1; 877 } else { 878 priority += 1; 880 } 881 882 883 if (DEBUG) { 884 System.out.println("RCN" + priority + " " 885 + redundantBranch.firstValue + " =? " 886 + redundantBranch.secondValue 887 + " : " + warning 888 ); 889 890 if (isChecked) System.out.println("isChecked"); 891 if (wouldHaveBeenAKaboom) System.out.println("wouldHaveBeenAKaboom"); 892 if (createdDeadCode) System.out.println("createdDeadCode"); 893 } 894 895 896 BugAnnotation variableAnnotation = null; 897 try { 898 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location); 900 if (vnaFrame.isValid()) { 901 Instruction ins = location.getHandle().getInstruction(); 902 903 ValueNumber valueNumber = vnaFrame.getInstance(ins, classContext.getConstantPoolGen()); 904 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) return; 905 variableAnnotation = findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame); 906 907 } 908 } catch (DataflowAnalysisException e) { 909 } catch (CFGBuilderException e) { 911 } 913 914 BugInstance bugInstance = 915 new BugInstance(this, warning, priority) 916 .addClassAndMethod(methodGen, sourceFile); 917 if (variableAnnotation != null) bugInstance.add(variableAnnotation); 918 else bugInstance.add(new LocalVariableAnnotation("?", -1, -1)); 919 if (wouldHaveBeenAKaboom) 920 bugInstance.addSourceLine(classContext, methodGen, sourceFile, locationOfKaBoom.getHandle()); 921 bugInstance.addSourceLine(classContext, methodGen, sourceFile, location.getHandle()).describe("SOURCE_REDUNDANT_NULL_CHECK"); 922 923 if (FindBugsAnalysisFeatures.isRelaxedMode()) { 924 WarningPropertySet propertySet = new WarningPropertySet(); 925 WarningPropertyUtil.addPropertiesForLocation(propertySet, classContext, method, location); 926 if (isChecked) 927 propertySet.addProperty(NullDerefProperty.CHECKED_VALUE); 928 if (wouldHaveBeenAKaboom) 929 propertySet.addProperty(NullDerefProperty.WOULD_HAVE_BEEN_A_KABOOM); 930 if (createdDeadCode) 931 propertySet.addProperty(NullDerefProperty.CREATED_DEAD_CODE); 932 933 propertySet.decorateBugInstance(bugInstance); 934 935 priority = propertySet.computePriority(NORMAL_PRIORITY); 936 bugInstance.setPriority(priority); 937 } 938 939 bugReporter.reportBug(bugInstance); 940 } 941 942 948 private boolean isGoto(Instruction instruction) { 949 return instruction.getOpcode() == Constants.GOTO 950 || instruction.getOpcode() == Constants.GOTO_W; 951 } 952 953 956 public void foundGuaranteedNullDeref( 957 @NonNull Set <Location> assignedNullLocationSet, 958 @NonNull Set <Location> derefLocationSet, 959 SortedSet <Location> doomedLocations, 960 ValueNumberDataflow vna, ValueNumber refValue, boolean alwaysOnExceptionPath, boolean npeIfStatementCovered) { 961 if (refValue.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) return; 962 963 if (DEBUG) { 964 System.out.println("Found guaranteed null deref in " + method.getName()); 965 for(Location loc : doomedLocations) 966 System.out.println("Doomed at " + loc); 967 } 968 969 String bugType = alwaysOnExceptionPath 970 ? "NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH" 971 : "NP_GUARANTEED_DEREF"; 972 int priority = alwaysOnExceptionPath ? NORMAL_PRIORITY : HIGH_PRIORITY; 973 if (!npeIfStatementCovered) priority++; 974 975 976 979 TreeSet <Location> sortedDerefLocationSet = new TreeSet <Location>(derefLocationSet); 980 SortedSet <Location> sourceLocations; 981 if (doomedLocations.isEmpty()) sourceLocations= new TreeSet <Location>(assignedNullLocationSet); 982 else sourceLocations = doomedLocations; 983 984 if (doomedLocations.isEmpty() || sortedDerefLocationSet.isEmpty()) return; 985 boolean derefOutsideCatchBlock = false; 986 for (Location loc : sortedDerefLocationSet) 987 if (!inCatchNullBlock(loc)) { 988 derefOutsideCatchBlock = true; 989 break; 990 } 991 992 if (!derefOutsideCatchBlock) { 993 if (skipIfInsideCatchNull()) return; 994 priority++; 995 } 996 BugAnnotation variableAnnotation = null; 997 try { 998 for (Location loc : sourceLocations) { 999 variableAnnotation = findAnnotationFromValueNumber(method, loc, refValue, vna.getFactAtLocation(loc)); 1000 if (variableAnnotation != null) break; 1001 } 1002 if (variableAnnotation == null) for (Location loc : sortedDerefLocationSet) { 1003 variableAnnotation = findAnnotationFromValueNumber(method, loc, refValue, vna.getFactAtLocation(loc)); 1004 if (variableAnnotation != null) break; 1005 } 1006 1007 1008 } catch (DataflowAnalysisException e) { 1009 } 1010 if (variableAnnotation == null) variableAnnotation = new LocalVariableAnnotation("?",-1,-1); 1011 BugInstance bugInstance = new BugInstance(this, bugType, priority) 1013 .addClassAndMethod(classContext.getJavaClass(), method); 1014 1015 bugInstance.add(variableAnnotation); 1016 BitSet knownNull = new BitSet (); 1017 1018 SortedSet <SourceLineAnnotation> knownNullLocations = new TreeSet <SourceLineAnnotation>(); 1019 for (Location loc : sourceLocations) { 1020 SourceLineAnnotation sourceLineAnnotation = 1021 SourceLineAnnotation.fromVisitedInstruction(classContext, classContext.getMethodGen(method), 1022 classContext.getJavaClass().getSourceFileName(), 1023 loc.getHandle()); 1024 if (sourceLineAnnotation == null) continue; 1025 int startLine = sourceLineAnnotation.getStartLine(); 1026 if (startLine == -1) 1027 knownNullLocations.add(sourceLineAnnotation); 1028 else if (!knownNull.get(startLine)) { 1029 knownNull.set(startLine); 1030 knownNullLocations.add(sourceLineAnnotation); 1031 } 1032 } 1033 1034 for(SourceLineAnnotation sourceLineAnnotation : knownNullLocations) 1035 bugInstance.add(sourceLineAnnotation); 1036 1037 1038 for (Location loc : sortedDerefLocationSet) { 1039 bugInstance.addSourceLine(classContext, method, loc).describe("SOURCE_LINE_DEREF"); 1040 } 1041 1042 bugReporter.reportBug(bugInstance); 1044 } 1045 boolean inCatchNullBlock(Location loc) { 1046 int pc = loc.getHandle().getPosition(); 1047 int catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/NullPointerException", pc); 1048 if ( catchSize < Integer.MAX_VALUE) return true; 1049 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/Exception", pc); 1050 if ( catchSize < 5) return true; 1051 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/RuntimeException", pc); 1052 if ( catchSize < 5) return true; 1053 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getConstantPoolGen().getConstantPool(), method.getCode(), "java/lang/Throwable", pc); 1054 if ( catchSize < 5) return true; 1055 return false; 1056 1057 } 1058} 1059 1060 | Popular Tags |