static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, CallEvent::BindingsTy &Bindings, SValBuilder &SVB, const CallEvent &Call, CallEvent::param_iterator I, CallEvent::param_iterator E) { MemRegionManager &MRMgr = SVB.getRegionManager(); // If the function has fewer parameters than the call has arguments, we simply // do not bind any values to them. unsigned NumArgs = Call.getNumArgs(); unsigned Idx = 0; for (; I != E && Idx < NumArgs; ++I, ++Idx) { const ParmVarDecl *ParamDecl = *I; assert(ParamDecl && "Formal parameter has no decl?"); SVal ArgVal = Call.getArgSVal(Idx); if (!ArgVal.isUnknown()) { Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); } } // FIXME: Variadic arguments are not handled at all right now. }
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 ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisDeclContext()->getDecl()))) return; ProgramStateRef state = C.getState(); unsigned NumArgs = CE.getNumArgs(); // If we passed 'self' as and argument to the call, record it in the state // to be propagated after the call. // Note, we could have just given up, but try to be more optimistic here and // assume that the functions are going to continue initialization or will not // modify self. for (unsigned i = 0; i < NumArgs; ++i) { SVal argV = CE.getArgSVal(i); if (isSelfVar(argV, C)) { unsigned selfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { unsigned selfFlags = getSelfFlags(argV, C); C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } } }
void SimpleStreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); if (!Call.isGlobalCFunction()) return; if (Call.getCalleeIdentifier() != IIfclose) return; if (Call.getNumArgs() != 1) return; // Get the symbolic value corresponding to the file handle. SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol(); if (!FileDesc) return; // Check if the stream has already been closed. ProgramStateRef State = C.getState(); const StreamState *SS = State->get<StreamMap>(FileDesc); if (SS && SS->isClosed()) { reportDoubleClose(FileDesc, Call, C); return; } // Generate the next transition, in which the stream is closed. State = State->set<StreamMap>(FileDesc, StreamState::getClosed()); C.addTransition(State); }
void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisDeclContext()->getDecl()))) return; ProgramStateRef state = C.getState(); SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); if (!prevFlags) return; state = state->remove<PreCallSelfFlags>(); unsigned NumArgs = CE.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { SVal argV = CE.getArgSVal(i); if (isSelfVar(argV, C)) { // If the address of 'self' is being passed to the call, assume that the // 'self' after the call will have the same flags. // EX: log(&self) addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { // If 'self' is passed to the call by value, assume that the function // returns 'self'. So assign the flags, which were set on 'self' to the // return value. // EX: self = performMoreInitialization(self) addSelfFlag(state, CE.getReturnValue(), prevFlags, C); return; } } C.addTransition(state); }
void CallAndMessageChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); // If this is a call to a C++ method, check if the callee is null or // undefined. if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { SVal V = CC->getCXXThisVal(); if (V.isUndef()) { if (!BT_cxx_call_undef) BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is " "uninitialized")); emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); return; } ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull && !StNonNull) { if (!BT_cxx_call_null) BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " "is null")); emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } State = StNonNull; } // Don't check for uninitialized field values in arguments if the // caller has a body that is available and we have the chance to inline it. // This is a hack, but is a reasonable compromise betweens sometimes warning // and sometimes not depending on if we decide to inline a function. const Decl *D = Call.getDecl(); const bool checkUninitFields = !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); OwningPtr<BugType> *BT; if (isa<ObjCMethodCall>(Call)) BT = &BT_msg_arg; else BT = &BT_call_arg; for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT)) return; // If we make it here, record our assumptions about the callee. C.addTransition(State); }
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker]) return; if (!Call.isGlobalCFunction("dispatch_after") && !Call.isGlobalCFunction("dispatch_async")) return; for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) { if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>( Call.getArgSVal(Idx).getAsRegion())) checkAsyncExecutedBlockCaptures(*B, C); } }
/// Diagnose if any of the arguments to CE have already been /// dealloc'd. void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const { ProgramStateRef State = C.getState(); unsigned ArgCount = CE.getNumArgs(); for (unsigned I = 0; I < ArgCount; I++) { SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); if (!Sym) continue; if (State->contains<CalledSuperDealloc>(Sym)) { reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); return; } } }
/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is /// call to Block_release(). void ObjCDeallocChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { const IdentifierInfo *II = Call.getCalleeIdentifier(); if (II != Block_releaseII) return; if (Call.getNumArgs() != 1) return; SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol(); if (!ReleasedValue) return; transitionToReleaseValue(C, ReleasedValue); }
/// \return Bitvector marking non-null attributes. static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { const Decl *FD = Call.getDecl(); unsigned NumArgs = Call.getNumArgs(); llvm::SmallBitVector AttrNonNull(NumArgs); for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { if (!NonNull->args_size()) { AttrNonNull.set(0, NumArgs); break; } for (const ParamIdx &Idx : NonNull->args()) { unsigned IdxAST = Idx.getASTIndex(); if (IdxAST >= NumArgs) continue; AttrNonNull.set(IdxAST); } } return AttrNonNull; }
void CallAndMessageChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // If this is a call to a C++ method, check if the callee is null or // undefined. // FIXME: Generalize this to CXXInstanceCall once it supports // getCXXThisVal(). if (const CXXMemberCall *CC = dyn_cast<CXXMemberCall>(&Call)) { SVal V = CC->getCXXThisVal(); if (V.isUndef()) { if (!BT_cxx_call_undef) BT_cxx_call_undef.reset(new BuiltinBug("Called C++ object pointer is " "uninitialized")); EmitBadCall(BT_cxx_call_undef.get(), C, CC->getOriginExpr()); return; } if (V.isZeroConstant()) { if (!BT_cxx_call_null) BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " "is null")); EmitBadCall(BT_cxx_call_null.get(), C, CC->getOriginExpr()); return; } } // Don't check for uninitialized field values in arguments if the // caller has a body that is available and we have the chance to inline it. // This is a hack, but is a reasonable compromise betweens sometimes warning // and sometimes not depending on if we decide to inline a function. const Decl *D = Call.getDecl(); const bool checkUninitFields = !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); OwningPtr<BugType> *BT; if (isa<ObjCMethodCall>(Call)) BT = &BT_msg_arg; else BT = &BT_call_arg; for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT)) return; }
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); }
void AttrNonNullChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { const Decl *FD = Call.getDecl(); if (!FD) return; const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); if (!Att) return; ProgramStateRef state = C.getState(); // Iterate through the arguments of CE and check them for null. for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) { if (!Att->isNonNull(idx)) continue; SVal V = Call.getArgSVal(idx); DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); // If the value is unknown or undefined, we can't perform this check. if (!DV) continue; if (!isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. const Expr *ArgE = Call.getArgExpr(idx); if (!ArgE) continue; QualType T = ArgE->getType(); const RecordType *UT = T->getAsUnionType(); if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) continue; if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) { nonloc::CompoundVal::iterator CSV_I = CSV->begin(); assert(CSV_I != CSV->end()); V = *CSV_I; DV = dyn_cast<DefinedSVal>(&V); assert(++CSV_I == CSV->end()); if (!DV) continue; } else { // FIXME: Handle LazyCompoundVals? continue; } } ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef stateNotNull, stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (stateNull && !stateNotNull) { // Generate an error node. Check for a null node in case // we cache out. if (ExplodedNode *errorNode = C.generateSink(stateNull)) { // Lazily allocate the BugType object if it hasn't already been // created. Ownership is transferred to the BugReporter object once // the BugReport is passed to 'EmitWarning'. if (!BT) BT.reset(new BugType("Argument with 'nonnull' attribute passed null", "API")); BugReport *R = new BugReport(*BT, "Null pointer passed as an argument to a " "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. R->addRange(Call.getArgSourceRange(idx)); if (const Expr *ArgE = Call.getArgExpr(idx)) bugreporter::addTrackNullOrUndefValueVisitor(errorNode, ArgE, R); // Emit the bug report. C.EmitReport(R); } // Always return. Either we cached out or we just emitted an error. return; } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. assert(stateNotNull); 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); }
void CallAndMessageChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); // If this is a call to a C++ method, check if the callee is null or // undefined. if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { SVal V = CC->getCXXThisVal(); if (V.isUndef()) { if (!BT_cxx_call_undef) BT_cxx_call_undef.reset( new BuiltinBug(this, "Called C++ object pointer is uninitialized")); emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); return; } ProgramStateRef StNonNull, StNull; std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull && !StNonNull) { if (!BT_cxx_call_null) BT_cxx_call_null.reset( new BuiltinBug(this, "Called C++ object pointer is null")); emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } State = StNonNull; } const Decl *D = Call.getDecl(); const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D); if (FD) { // If we have a declaration, we can make sure we pass enough parameters to // the function. unsigned Params = FD->getNumParams(); if (Call.getNumArgs() < Params) { ExplodedNode *N = C.generateSink(); if (!N) return; LazyInit_BT("Function call with too few arguments", BT_call_few_args); SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Function taking " << Params << " argument" << (Params == 1 ? "" : "s") << " is called with less (" << Call.getNumArgs() << ")"; C.emitReport( llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N)); } } // Don't check for uninitialized field values in arguments if the // caller has a body that is available and we have the chance to inline it. // This is a hack, but is a reasonable compromise betweens sometimes warning // and sometimes not depending on if we decide to inline a function. const bool checkUninitFields = !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); std::unique_ptr<BugType> *BT; if (isa<ObjCMethodCall>(Call)) BT = &BT_msg_arg; else BT = &BT_call_arg; for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) { const ParmVarDecl *ParamDecl = nullptr; if(FD && i < FD->getNumParams()) ParamDecl = FD->getParamDecl(i); if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT, ParamDecl)) return; } // If we make it here, record our assumptions about the callee. C.addTransition(State); }
void PHPZPPCheckerImpl::checkPreCall(const CallEvent &Call, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); unsigned offset; if (!Call.isGlobalCFunction()) return; if (Call.getCalleeIdentifier() == IIzpp) { offset = 1; } else if (Call.getCalleeIdentifier() == IIzpp_ex) { offset = 2; } else if (Call.getCalleeIdentifier() == IIzpmp) { offset = 2; } else if (Call.getCalleeIdentifier() == IIzpmp_ex) { offset = 3; } else { return; } if (TSRMBuild) { ++offset; } if (Call.getNumArgs() <= offset) // Something is really weird - this should be caught by the compiler return; const StringLiteral *format_spec_sl = getCStringLiteral(Call.getArgSVal(offset)); if (!format_spec_sl) { // TODO need a good way to report this, even though this is no error std::cout << "Couldn't get format string looked at offset " << offset << std::endl; Call.dump(); return; } const StringRef format_spec = format_spec_sl->getBytes(); // Call.dump(); for (StringRef::const_iterator modifier = format_spec.begin(), last_mod = format_spec.end(); modifier != last_mod; ++modifier) { //std::cout << " I am checking for " << *modifier << std::endl; const PHPTypeRange range = map.equal_range(*modifier); if (range.first == range.second) { BugReport *R = new BugReport( *InvalidModifierBugType, std::string("Unknown modifier '") + *modifier + "'", C.addTransition()); C.emitReport(R); return; } for (PHPTypeMap::const_iterator type = range.first; type != range.second; ++type) { if (!type->second) { // Current modifier doesn't need an argument, these are special things // like |, ! or / continue; } ++offset; //std::cout << " I need a " << *type->second << " (" << offset << ")" << std::endl; if (Call.getNumArgs() <= offset) { BugReport *R = new BugReport(*WrongArgumentNumberBugType, "Too few arguments for format specified", C.addTransition()); C.emitReport(R); //std::cout << "!!!!I am missing args! " << Call.getNumArgs() << "<=" << offset << std::endl; return; } SVal val = Call.getArgSVal(offset); if (!compareTypeWithSVal(val, *type->second, C)) { // TODO: Move error reporting here? // Even if there is a type mismatch we can continue, most of the time // this should be a simple mistake by the user, in rare cases the user // missed an argument and will get many subsequent errors } } } if (Call.getNumArgs() > 1 + offset) { BugReport *R = new BugReport(*WrongArgumentNumberBugType, "Too many arguments for format specified", C.addTransition()); R->markInteresting(Call.getArgSVal(offset)); C.emitReport(R); } }
/// This callback warns when a nullable pointer or a null value is passed to a /// function that expects its argument to be nonnull. void NullabilityChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) return; ProgramStateRef State = C.getState(); if (State->get<InvariantViolated>()) return; ProgramStateRef OrigState = State; unsigned Idx = 0; for (const ParmVarDecl *Param : Call.parameters()) { if (Param->isParameterPack()) break; if (Idx >= Call.getNumArgs()) break; const Expr *ArgExpr = Call.getArgExpr(Idx); auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); if (!ArgSVal) continue; if (!Param->getType()->isAnyPointerType() && !Param->getType()->isReferenceType()) continue; NullConstraint Nullness = getNullConstraint(*ArgSVal, State); Nullability RequiredNullability = getNullabilityAnnotation(Param->getType()); Nullability ArgExprTypeLevelNullability = getNullabilityAnnotation(ArgExpr->getType()); unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgExprTypeLevelNullability != Nullability::Nonnull && RequiredNullability == Nullability::Nonnull && isDiagnosableCall(Call)) { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null"); OS << " passed to a callee that requires a non-null " << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, nullptr, C, ArgExpr, /*SuppressPath=*/false); return; } const MemRegion *Region = getTrackRegion(*ArgSVal); if (!Region) continue; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); if (TrackedNullability) { if (Nullness == NullConstraint::IsNotNull || TrackedNullability->getValue() != Nullability::Nullable) continue; if (Filter.CheckNullablePassedToNonnull && RequiredNullability == Nullability::Nonnull && isDiagnosableCall(Call)) { ExplodedNode *N = C.addTransition(State); SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "Nullable pointer is passed to a callee that requires a non-null " << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); reportBugIfInvariantHolds("Nullable pointer is dereferenced", ErrorKind::NullableDereferenced, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } continue; } // No tracked nullability yet. if (ArgExprTypeLevelNullability != Nullability::Nullable) continue; State = State->set<NullabilityMap>( Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr)); } if (State != OrigState) C.addTransition(State); }
void DoubleFetchChecker::checkPostCall(const CallEvent &Call,CheckerContext &Ctx) const { const IdentifierInfo *ID = Call.getCalleeIdentifier(); std::cout<<"\n"; std::cout<<"[checkPostCall]------call function:"<<ID->getName().str()<<std::endl; ProgramStateRef state = Ctx.getState(); if(ID == NULL) { return; } unsigned int curTime = this->getCurTime(state); /*everytime copy_from_user is invoked, *a new tainted is added to the taintList of the first Arg, *which is the fetched value in kernel */ if (ID->getName() == "copy_from_user"){ SVal Val0 = state->getSVal(Call.getArgExpr(0), Ctx.getLocationContext()); SVal origin = state->getSVal(Call.getArgExpr(1), Ctx.getLocationContext()); SVal len = state->getSVal(Call.getArgExpr(2), Ctx.getLocationContext()); std::cout<<"[checkPostCall]---> Val0: "<<toStr(Val0)<<std::endl; std::cout<<"[checkPostCall]---> origin: "<<toStr(origin)<<std::endl; std::cout<<"[checkPostCall]---> len: "<<toStr(len)<<std::endl; /* here has to use val1 as the origin, * since it is conveted from the actuall Expr */ state = this->addNewTaint(state, Val0, origin); /*after making change to time line, we need to increase the time */ state = this->increTime(state); std::cout<<"[checkPostCall] timer++"<<std::endl; if(state != NULL) Ctx.addTransition(state); } if (ID->getName() == "get_user" || ID->getName() =="__get_user"){ SVal arg0 = Call.getArgSVal(0); SVal arg1 = Call.getArgSVal(1); std::cout<<"--->arg0: "<<toStr(arg0)<<std::endl; std::cout<<"--->arg1: "<<toStr(arg1)<<std::endl; //const Expr* e0 = Call.getArgExpr(0); //const Expr* e1 =Call.getArgExpr(1); /* here has to use val1 as the origin, * since it is conveted from the actuall Expr */ state = this->addNewTaint(state, arg0, arg1); /*after making change to time line, we need to increase the time */ state = this->increTime(state); std::cout<<"[checkPostCall] timer++"<<std::endl; if(state != NULL) Ctx.addTransition(state); } if (ID->getName() == "malloc" || ID->getName() == "UserAllocPoolWithQuota") { int num = Call.getNumArgs(); SVal arg = Call.getArgSVal(0); SVal ret = Call.getReturnValue(); /*no need to check by time*/ if (this->isValTainted(state, arg)){ std::cout<<"[checkPostCall] arg of malloc is tainted."<<"\targ is:"<<toStr(arg)<<std::endl; //pass current taint taint to return value ProgramStateRef newstate = passTaints(state, arg, ret); if (newstate!=state && newstate != NULL){ Ctx.addTransition(newstate); std::cout<<"[checkPostCall]add ret Taint finish, ret is: "<<toStr(ret)<<std::endl; showValTaints(newstate, ret, "--->ret: "); } else std::cout<<"[checkPostCall] add ret Taint failed, ret is "<<toStr(ret)<<std::endl; } else{ std::cout<<"[checkPostCall] arg of malloc not tainted."<<"\targ is:"<<toStr(arg)<<std::endl; } } //int num = Call.getNumArgs(); //for(int i =0; i< num; i++){ if (ID->getName() == "CMSG_COMPAT_ALIGN") { SVal arg = Call.getArgSVal(0); SVal ret = Call.getReturnValue(); /*no need to check by time*/ if (this->isValTainted(state, arg)){ std::cout<<"[checkPostCall] arg of anyfunc is tainted."<<"\targ is:"<<toStr(arg)<<std::endl; //pass current taint taint to return value ProgramStateRef newstate = passTaints(state, arg, ret); if (newstate!=state && newstate != NULL){ Ctx.addTransition(newstate); std::cout<<"[checkPostCall]add ret Taint finish, ret is: "<<toStr(ret)<<std::endl; showValTaints(newstate, ret, "--->ret: "); return; } else std::cout<<"[checkPostCall] add ret Taint failed, ret is "<<toStr(ret)<<std::endl; } else{ std::cout<<"[checkPostCall] arg of anyfunc not tainted."<<"\targ is:"<<toStr(arg)<<std::endl; } } }
void IteratorChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // Record new iterator positions and iterator position changes const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!Func) return; if (Func->isOverloadedOperator()) { const auto Op = Func->getOverloadedOperator(); if (isSimpleComparisonOperator(Op)) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); } else { handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0), Call.getArgSVal(1), Op); } } } else { const auto *OrigExpr = Call.getOriginExpr(); if (!OrigExpr) return; if (!isIteratorType(Call.getResultType())) return; auto State = C.getState(); // Already bound to container? if (getIteratorPosition(State, Call.getReturnValue())) return; if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { if (isEndCall(Func)) { handleEnd(C, OrigExpr, Call.getReturnValue(), InstCall->getCXXThisVal()); return; } } // Copy-like and move constructors if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { State = setIteratorPosition(State, Call.getReturnValue(), *Pos); if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { State = removeIteratorPosition(State, Call.getArgSVal(0)); } C.addTransition(State); return; } } // Assumption: if return value is an iterator which is not yet bound to a // container, then look for the first iterator argument, and // bind the return value to the same container. This approach // works for STL algorithms. // FIXME: Add a more conservative mode for (unsigned i = 0; i < Call.getNumArgs(); ++i) { if (isIteratorType(Call.getArgExpr(i)->getType())) { if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { assignToContainer(C, OrigExpr, Call.getReturnValue(), Pos->getContainer()); return; } } } } }
// Checking for SecItemAdd and SecItemUpdate, no particular reason for assigning it to PostCall void iOSAppSecLeakingLogsChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { do { if ( !m_pInsecureInstBugType ) { MSEC_DEBUG( "redwud: ", "!m_pInsecureInstBugType" ) ; break ; } initIdentifierInfo( C.getASTContext() ) ; //redwud: Obviously it is what it is if ( !Call.isGlobalCFunction() ) { MSEC_DEBUG( "redwud: ", "!Call.isGlobalCFunction" ) ; break ; } const IdentifierInfo *pCalleeIdent = Call.getCalleeIdentifier() ; unsigned iNumArgs = Call.getNumArgs() ; //We do away with array because it's only two of them and it //will break the while..passing pattern if ( pCalleeIdent != m_piiNSLog ) { if ( (pCalleeIdent != m_piiNSLogv) ) { break ; } //FIXME: Workaround to evade 2nd and onward parameters of NSLogv // Idea: Use identifier for slot from s iNumArgs = 1 ; } CSensitiveInfo &rSenInfo = CSensitiveInfo::create() ; SymbolRef pSymToCheck = NULL ; // Go through each parameter unless find some sensitive info in one of them for ( unsigned iCtr = 0; (iCtr < iNumArgs) && (!pSymToCheck); iCtr++ ) { const Expr *pExpr = Call.getArgExpr( iCtr ) ; StringRef szString ; StringRef szVarName ; CMSecCommon::getStrFromExpr( szString, pExpr, &szVarName ) ; if ( szString.empty() ) // Nil is supported here, so no need to check { // MSEC_DEBUG( "redwud: ", "Empty string" ) ; continue ; } if ( !rSenInfo.isSensitive( szString.str() ) && !rSenInfo.isSensitive( szVarName.str() ) ) { // MSEC_DEBUG( "redwud: ", "!Sensitive :" << szString << "Var name: " << szVarName ) ; continue ; } // Get the symbolic value corresponding to the target parameter. pSymToCheck = Call.getArgSVal( iCtr ).getAsSymbol() ; //Force the issue if ( !pSymToCheck ) { pSymToCheck = CMSecCommon::conjureSymbolRef() ; } } if ( !pSymToCheck ) { break ; } ProgramStateRef pProgState = C.getState() ; //Report this instance CMSecCommon::reportInsecureInstance( pSymToCheck, C, C.addTransition( pProgState ) , *m_pInsecureInstBugType, m_szReportDesc ) ; } while ( _PASSING_ ) ; }
void NonNullParamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { const Decl *FD = Call.getDecl(); if (!FD) return; const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); ProgramStateRef state = C.getState(); CallEvent::param_type_iterator TyI = Call.param_type_begin(), TyE = Call.param_type_end(); for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){ // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a paramter. bool haveRefTypeParam = false; if (TyI != TyE) { haveRefTypeParam = (*TyI)->isReferenceType(); TyI++; } bool haveAttrNonNull = Att && Att->isNonNull(idx); if (!haveAttrNonNull) { // Check if the parameter is also marked 'nonnull'. ArrayRef<ParmVarDecl*> parms = Call.parameters(); if (idx < parms.size()) haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); } if (!haveRefTypeParam && !haveAttrNonNull) 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); Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); if (!DV) continue; // Process the case when the argument is not a location. assert(!haveRefTypeParam || DV->getAs<Loc>()); 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; if (Optional<nonloc::CompoundVal> CSV = DV->getAs<nonloc::CompoundVal>()) { nonloc::CompoundVal::iterator CSV_I = CSV->begin(); assert(CSV_I != CSV->end()); V = *CSV_I; DV = V.getAs<DefinedSVal>(); assert(++CSV_I == 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 CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) if (const InitListExpr *IE = dyn_cast<InitListExpr>(CE->getInitializer())) ArgE = dyn_cast<Expr>(*(IE->begin())); } else { // FIXME: Handle LazyCompoundVals? continue; } } ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef stateNotNull, stateNull; std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (stateNull && !stateNotNull) { // Generate an error node. Check for a null node in case // we cache out. if (ExplodedNode *errorNode = C.generateSink(stateNull)) { BugReport *R = 0; 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(R); } // Always return. Either we cached out or we just emitted an error. return; } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. assert(stateNotNull); 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); }
/// This callback warns when a nullable pointer or a null value is passed to a /// function that expects its argument to be nonnull. void NullabilityChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) return; ProgramStateRef State = C.getState(); if (State->get<PreconditionViolated>()) return; ProgramStateRef OrigState = State; unsigned Idx = 0; for (const ParmVarDecl *Param : Call.parameters()) { if (Param->isParameterPack()) break; const Expr *ArgExpr = nullptr; if (Idx < Call.getNumArgs()) ArgExpr = Call.getArgExpr(Idx); auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); if (!ArgSVal) continue; if (!Param->getType()->isAnyPointerType() && !Param->getType()->isReferenceType()) continue; NullConstraint Nullness = getNullConstraint(*ArgSVal, State); Nullability ParamNullability = getNullabilityAnnotation(Param->getType()); Nullability ArgStaticNullability = getNullabilityAnnotation(ArgExpr->getType()); if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgStaticNullability != Nullability::Nonnull && ParamNullability == Nullability::Nonnull) { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C, ArgExpr); return; } const MemRegion *Region = getTrackRegion(*ArgSVal); if (!Region) continue; const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); if (TrackedNullability) { if (Nullness == NullConstraint::IsNotNull || TrackedNullability->getValue() != Nullability::Nullable) continue; if (Filter.CheckNullablePassedToNonnull && ParamNullability == Nullability::Nonnull) { ExplodedNode *N = C.addTransition(State); reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } continue; } // No tracked nullability yet. if (ArgStaticNullability != Nullability::Nullable) continue; State = State->set<NullabilityMap>( Region, NullabilityState(ArgStaticNullability, ArgExpr)); } if (State != OrigState) C.addTransition(State); }