/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2005, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.detect; import java.util.BitSet; import java.util.Iterator; import java.util.Set; import javax.annotation.meta.When; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Code; import org.apache.bcel.classfile.CodeException; import org.apache.bcel.classfile.ConstantClass; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.ReferenceType; import org.apache.bcel.generic.Type; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.Detector; import edu.umd.cs.findbugs.FindBugsAnalysisFeatures; import edu.umd.cs.findbugs.LocalVariableAnnotation; import edu.umd.cs.findbugs.Priorities; import edu.umd.cs.findbugs.SystemProperties; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.ba.CFG; import edu.umd.cs.findbugs.ba.ClassContext; import edu.umd.cs.findbugs.ba.Location; import edu.umd.cs.findbugs.ba.SignatureParser; import edu.umd.cs.findbugs.ba.XFactory; import edu.umd.cs.findbugs.ba.XMethod; import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow; import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefSet; import edu.umd.cs.findbugs.ba.interproc.ParameterProperty; import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation; import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications; import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue; import edu.umd.cs.findbugs.ba.vna.ValueNumber; import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow; import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; import edu.umd.cs.findbugs.classfile.ClassDescriptor; import edu.umd.cs.findbugs.classfile.DescriptorFactory; /** * Build database of unconditionally dereferenced parameters. * * @author David Hovemeyer */ public abstract class BuildUnconditionalParamDerefDatabase implements Detector { public static final boolean VERBOSE_DEBUG = SystemProperties.getBoolean("fnd.debug.nullarg.verbose"); private static final boolean DEBUG = SystemProperties.getBoolean("fnd.debug.nullarg") || VERBOSE_DEBUG; public final TypeQualifierValue nonnullTypeQualifierValue; abstract protected void reportBug(BugInstance bug); public BuildUnconditionalParamDerefDatabase() { ClassDescriptor nonnullClassDesc = DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class); this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null); } public void visitClassContext(ClassContext classContext) { boolean fullAnalysis = AnalysisContext.currentAnalysisContext().getBoolProperty( FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES); if (!fullAnalysis && !AnalysisContext.currentAnalysisContext().isApplicationClass(classContext.getJavaClass())) return; if (VERBOSE_DEBUG) System.out.println("Visiting class " + classContext.getJavaClass().getClassName()); for (Method m : classContext.getMethodsInCallOrder()) considerMethod(classContext, m); } private void considerMethod(ClassContext classContext, Method method) { boolean hasReferenceParameters = false; for (Type argument : method.getArgumentTypes()) if (argument instanceof ReferenceType) { hasReferenceParameters = true; } if (hasReferenceParameters && classContext.getMethodGen(method) != null) { if (VERBOSE_DEBUG) System.out.println("Check " + method); analyzeMethod(classContext, method); } } private void analyzeMethod(ClassContext classContext, Method method) { JavaClass jclass = classContext.getJavaClass(); XMethod xmethod = XFactory.createXMethod(jclass, method); try { CFG cfg = classContext.getCFG(method); ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method); UnconditionalValueDerefDataflow dataflow = classContext.getUnconditionalValueDerefDataflow(method); SignatureParser parser = new SignatureParser(method.getSignature()); int paramLocalOffset = method.isStatic() ? 0 : 1; // Build BitSet of params that are unconditionally dereferenced BitSet unconditionalDerefSet = new BitSet(); UnconditionalValueDerefSet entryFact = dataflow.getResultFact(cfg.getEntry()); Iterator<String> paramIterator = parser.parameterSignatureIterator(); int i = 0; while (paramIterator.hasNext()) { String paramSig = paramIterator.next(); ValueNumber paramVN = vnaDataflow.getAnalysis().getEntryValue(paramLocalOffset); handleParameter: if (entryFact.isUnconditionallyDereferenced(paramVN)) { TypeQualifierAnnotation directTypeQualifierAnnotation = TypeQualifierApplications .getDirectTypeQualifierAnnotation(xmethod, i, nonnullTypeQualifierValue); TypeQualifierAnnotation typeQualifierAnnotation = TypeQualifierApplications .getEffectiveTypeQualifierAnnotation(xmethod, i, nonnullTypeQualifierValue); boolean implicitNullCheckForEquals = false; if (directTypeQualifierAnnotation == null && method.getName().equals("equals") && method.getSignature().equals("(Ljava/lang/Object;)Z") && !method.isStatic()) { implicitNullCheckForEquals = true; Code code = method.getCode(); ConstantPool cp = jclass.getConstantPool(); byte codeBytes[] = code.getCode(); for (CodeException e : code.getExceptionTable()) { ConstantClass cl = (ConstantClass) cp.getConstant(e.getCatchType()); int endPC = e.getEndPC(); int startPC = e.getStartPC(); int handlerPC = e.getHandlerPC(); if (startPC == 0 && endPC + 1 == handlerPC && handlerPC == codeBytes.length - 3 && (codeBytes[handlerPC + 1] & 0xff) == Constants.ICONST_0 && (codeBytes[handlerPC + 2] & 0xff) == Constants.IRETURN && FindNullDeref.catchTypesForNull.contains(cl.getBytes(cp))) { // equals method body contained in try clause return; } } typeQualifierAnnotation = TypeQualifierAnnotation.getValue(nonnullTypeQualifierValue, When.MAYBE); } if (typeQualifierAnnotation != null && typeQualifierAnnotation.when == When.ALWAYS) unconditionalDerefSet.set(i); else if (isCaught(classContext, method, entryFact, paramVN)) { // ignore } else if (typeQualifierAnnotation == null) unconditionalDerefSet.set(i); else { int paramLocal = xmethod.isStatic() ? i : i + 1; int priority = Priorities.NORMAL_PRIORITY; if (typeQualifierAnnotation.when != When.UNKNOWN) priority--; if (xmethod.isStatic() || xmethod.isFinal() || xmethod.isPrivate() || xmethod.getName().equals("<init>") || jclass.isFinal()) priority--; if (directTypeQualifierAnnotation == null) priority++; String bugPattern = implicitNullCheckForEquals ? "NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT" : "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE"; reportBug(new BugInstance(this, bugPattern, priority).addClassAndMethod(jclass, method).add( LocalVariableAnnotation.getParameterLocalVariableAnnotation(method, paramLocal))); } } i++; if (paramSig.equals("D") || paramSig.equals("J")) paramLocalOffset += 2; else paramLocalOffset += 1; } // No need to add properties if there are no unconditionally // dereferenced params if (unconditionalDerefSet.isEmpty()) { if (VERBOSE_DEBUG) { System.out.println("\tResult is empty"); } return; } if (VERBOSE_DEBUG) { ClassContext.dumpDataflowInformation(method, cfg, vnaDataflow, classContext.getIsNullValueDataflow(method), dataflow, classContext.getTypeDataflow(method)); } ParameterProperty property = new ParameterProperty(); property.setParamsWithProperty(unconditionalDerefSet); AnalysisContext.currentAnalysisContext().getUnconditionalDerefParamDatabase() .setProperty(xmethod.getMethodDescriptor(), property); if (DEBUG) { System.out.println("Unconditional deref: " + xmethod + "=" + property); } } catch (CheckedAnalysisException e) { AnalysisContext.currentAnalysisContext().getLookupFailureCallback() .logError("Error analyzing " + xmethod + " for unconditional deref training", e); } } /** * @param classContext * @param method * @param entryFact * @param paramVN * @return */ public boolean isCaught(ClassContext classContext, Method method, UnconditionalValueDerefSet entryFact, ValueNumber paramVN) { boolean caught = true; Set<Location> dereferenceSites = entryFact.getDerefLocationSet(paramVN); if (dereferenceSites != null && !dereferenceSites.isEmpty()) { ConstantPool cp = classContext.getJavaClass().getConstantPool(); for(Location loc : dereferenceSites) { if (!FindNullDeref.catchesNull(cp, method.getCode(), loc)) caught = false; } } return caught; } }