void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { const Expr *receiver = msg.getInstanceReceiver(); if (!receiver) return; // FIXME: Enhance with value-tracking information instead of consulting // the type of the expression. const ObjCObjectPointerType* PT = receiver->getType()->getAs<ObjCObjectPointerType>(); if (!PT) return; const ObjCInterfaceDecl *OD = PT->getInterfaceDecl(); if (!OD) return; if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) return; if (releaseS.isNull()) releaseS = GetNullarySelector("release", C.getASTContext()); // Sending 'release' message? if (msg.getSelector() != releaseS) return; SourceRange R = msg.getSourceRange(); C.getBugReporter().EmitBasicReport("Use -drain instead of -release", "API Upgrade (Apple)", "Use -drain instead of -release when using NSAutoreleasePool " "and garbage collection", R.getBegin(), &R, 1); }
void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, CheckerContext &Ctx) const { if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { return; } const MemRegion *const MR = PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); if (!MR) return; const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); // The region must be typed, in order to reason about it. if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) return; ProgramStateRef State = Ctx.getState(); const Request *const Req = State->get<RequestMap>(MR); // double nonblocking detected if (Req && Req->CurrentState == Request::State::Nonblocking) { ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, Ctx.getBugReporter()); Ctx.addTransition(ErrorNode->getState(), ErrorNode); } // no error else { State = State->set<RequestMap>(MR, Request::State::Nonblocking); Ctx.addTransition(State); } }
void NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME) { const Expr *receiver = ME->getReceiver(); if (!receiver) return; // FIXME: Enhance with value-tracking information instead of consulting // the type of the expression. const ObjCObjectPointerType* PT = receiver->getType()->getAs<ObjCObjectPointerType>(); if (!PT) return; const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); if (!OD) return; if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) return; // Sending 'release' message? if (ME->getSelector() != releaseS) return; SourceRange R = ME->getSourceRange(); C.getBugReporter().EmitBasicReport("Use -drain instead of -release", "API Upgrade (Apple)", "Use -drain instead of -release when using NSAutoreleasePool " "and garbage collection", ME->getLocStart(), &R, 1); }
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { // If we're binding to a reference, check if the value is known to be null. if (V.isUndef()) return; const MemRegion *MR = L.getAsRegion(); const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); if (!TVR) return; if (!TVR->getValueType()->isReferenceType()) return; ProgramStateRef State = C.getState(); ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull) { if (!StNonNull) { reportBug(StNull, S, C, /*isBind=*/true); return; } // At this point the value could be either null or non-null. // Record this as an "implicit" null dereference. if (ExplodedNode *N = C.generateSink(StNull)) { ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N, &C.getBugReporter() }; dispatchEvent(event); } } // Unlike a regular null dereference, initializing a reference with a // dereferenced null pointer does not actually cause a runtime exception in // Clang's implementation of references. // // int &r = *p; // safe?? // if (p != NULL) return; // uh-oh // r = 5; // trap here // // The standard says this is invalid as soon as we try to create a "null // reference" (there is no such thing), but turning this into an assumption // that 'p' is never null will not match our actual runtime behavior. // So we do not record this assumption, allowing us to warn on the last line // of this example. // // We do need to add a transition because we may have generated a sink for // the "implicit" null dereference. C.addTransition(State, this); }
void NullabilityChecker::reportBugIfPreconditionHolds( ErrorKind Error, ExplodedNode *N, const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { ProgramStateRef OriginalState = N->getState(); if (checkPreconditionViolation(OriginalState, N, C)) return; if (SuppressPath) { OriginalState = OriginalState->set<PreconditionViolated>(true); N = C.addTransition(OriginalState, N); } reportBug(Error, N, Region, C.getBugReporter(), ValueExpr); }
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); bugreporter::addTrackNullOrUndefValueVisitor(N, bugreporter::GetDerefExpr(N), report); report->disablePathPruning(); C.EmitReport(report); } return; } DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); // Check for null dereferences. if (!isa<Loc>(location)) return; ProgramStateRef state = C.getState(); ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. if (nullState) { if (!notNullState) { reportBug(nullState, S, C); return; } // Otherwise, we have the case where the location could either be // null or not-null. Record the error node as an "implicit" null // dereference. if (ExplodedNode *N = C.generateSink(nullState)) { ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() }; dispatchEvent(event); } } // From this point forward, we know that the location is not null. C.addTransition(notNullState); }
void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, CheckerContext &Ctx) const { if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) return; const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); if (!MR) return; const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); // The region must be typed, in order to reason about it. if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) return; llvm::SmallVector<const MemRegion *, 2> ReqRegions; allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); if (ReqRegions.empty()) return; ProgramStateRef State = Ctx.getState(); static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); ExplodedNode *ErrorNode{nullptr}; // Check all request regions used by the wait function. for (const auto &ReqRegion : ReqRegions) { const Request *const Req = State->get<RequestMap>(ReqRegion); State = State->set<RequestMap>(ReqRegion, Request::State::Wait); if (!Req) { if (!ErrorNode) { ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); State = ErrorNode->getState(); } // A wait has no matching nonblocking call. BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, Ctx.getBugReporter()); } } if (!ErrorNode) { Ctx.addTransition(State); } else { Ctx.addTransition(State, ErrorNode); } }
void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, CheckerContext &Ctx) const { if (!SymReaper.hasDeadSymbols()) return; ProgramStateRef State = Ctx.getState(); const auto &Requests = State->get<RequestMap>(); if (Requests.isEmpty()) return; static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); ExplodedNode *ErrorNode{nullptr}; auto ReqMap = State->get<RequestMap>(); for (const auto &Req : ReqMap) { if (!SymReaper.isLiveRegion(Req.first)) { if (Req.second.CurrentState == Request::State::Nonblocking) { if (!ErrorNode) { ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); State = ErrorNode->getState(); } BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, Ctx.getBugReporter()); } State = State->remove<RequestMap>(Req.first); } } // Transition to update the state regarding removed requests. if (!ErrorNode) { Ctx.addTransition(State); } else { Ctx.addTransition(State, ErrorNode); } }
void NonNullParamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) return; llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); unsigned NumArgs = Call.getNumArgs(); ProgramStateRef state = C.getState(); ArrayRef<ParmVarDecl*> parms = Call.parameters(); for (unsigned idx = 0; idx < NumArgs; ++idx) { // For vararg functions, a corresponding parameter decl may not exist. bool HasParam = idx < parms.size(); // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a parameter. bool haveRefTypeParam = HasParam ? parms[idx]->getType()->isReferenceType() : false; bool haveAttrNonNull = AttrNonNull[idx]; // Check if the parameter is also marked 'nonnull'. if (!haveAttrNonNull && HasParam) haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); if (!haveAttrNonNull && !haveRefTypeParam) continue; // If the value is unknown or undefined, we can't perform this check. const Expr *ArgE = Call.getArgExpr(idx); SVal V = Call.getArgSVal(idx); auto DV = V.getAs<DefinedSVal>(); if (!DV) continue; assert(!haveRefTypeParam || DV->getAs<Loc>()); // Process the case when the argument is not a location. if (haveAttrNonNull && !DV->getAs<Loc>()) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) continue; QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; auto CSV = DV->getAs<nonloc::CompoundVal>(); // FIXME: Handle LazyCompoundVals? if (!CSV) continue; V = *(CSV->begin()); DV = V.getAs<DefinedSVal>(); assert(++CSV->begin() == CSV->end()); // FIXME: Handle (some_union){ some_other_union_val }, which turns into // a LazyCompoundVal inside a CompoundVal. if (!V.getAs<Loc>()) continue; // Retrieve the corresponding expression. if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) ArgE = dyn_cast<Expr>(*(IE->begin())); } ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef stateNotNull, stateNull; std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); // Generate an error node. Check for a null node in case // we cache out. if (stateNull && !stateNotNull) { if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { std::unique_ptr<BugReport> R; if (haveAttrNonNull) R = genReportNullAttrNonNull(errorNode, ArgE); else if (haveRefTypeParam) R = genReportReferenceToNullPointer(errorNode, ArgE); // Highlight the range of the argument that was null. R->addRange(Call.getArgSourceRange(idx)); // Emit the bug report. C.emitReport(std::move(R)); } // Always return. Either we cached out or we just emitted an error. return; } if (stateNull) { if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { ImplicitNullDerefEvent event = { V, false, N, &C.getBugReporter(), /*IsDirectDereference=*/haveRefTypeParam}; dispatchEvent(event); } } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. state = stateNotNull; } // If we reach here all of the arguments passed the nonnull check. // If 'state' has been updated generated a new node. C.addTransition(state); }