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 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 DoubleFetchChecker::checkPostCall(const CallEvent &Call,CheckerContext &Ctx) const { const IdentifierInfo *ID = Call.getCalleeIdentifier(); std::cout<<"[checkPostCall]------call function:"<<ID->getName().str()<<std::endl; ProgramStateRef state = Ctx.getState(); if(ID == NULL) { return; } if (ID->getName() == "malloc") { SVal arg = Call.getArgSVal(0); SVal ret = Call.getReturnValue(); if (this->isTaintedByTime(state, arg)){ std::cout<<"[checkPostCall] arg of malloc is tainted."<<"\targ is:"<<toStr(arg)<<std::endl; //pass current taint tag 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; showValTaintTags(newstate, 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; } } }
void DoubleFetchChecker::checkPreCall(const CallEvent &Call,CheckerContext &Ctx) const { const IdentifierInfo *ID = Call.getCalleeIdentifier(); ProgramStateRef state = Ctx.getState(); if (ID == NULL) { return; } std::cout<<"\n"; std::cout<<"[checkPreCall]-----call function:"<<ID->getName().str()<<std::endl; }
void BlockInCriticalSectionChecker::reportBlockInCritSection( SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); if (!ErrNode) return; std::string msg; llvm::raw_string_ostream os(msg); os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() << "' inside of critical section"; auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(BlockDescSym); C.emitReport(std::move(R)); }
/// 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); }
void MPIChecker::allRegionsUsedByWait( llvm::SmallVector<const MemRegion *, 2> &ReqRegions, const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { MemRegionManager *const RegionManager = MR->getMemRegionManager(); if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { const MemRegion *SuperRegion{nullptr}; if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { SuperRegion = ER->getSuperRegion(); } // A single request is passed to MPI_Waitall. if (!SuperRegion) { ReqRegions.push_back(MR); return; } const auto &Size = Ctx.getStoreManager().getSizeInElements( Ctx.getState(), SuperRegion, CE.getArgExpr(1)->getType()->getPointeeType()); const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); for (size_t i = 0; i < ArrSize; ++i) { const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); const ElementRegion *const ER = RegionManager->getElementRegion( CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, Ctx.getASTContext()); ReqRegions.push_back(ER->getAs<MemRegion>()); } } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { ReqRegions.push_back(MR); } }
void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); bool BuildSinks = false; if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) BuildSinks = FD->getAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); const Expr *Callee = CE.getOriginExpr(); if (!BuildSinks && Callee) BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); if (!BuildSinks && CE.isGlobalCFunction()) { if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { // HACK: Some functions are not marked noreturn, and don't return. // Here are a few hardwired ones. If this takes too long, we can // potentially cache these results. BuildSinks = llvm::StringSwitch<bool>(StringRef(II->getName())) .Case("exit", true) .Case("panic", true) .Case("error", true) .Case("Assert", true) // FIXME: This is just a wrapper around throwing an exception. // Eventually inter-procedural analysis should handle this easily. .Case("ziperr", true) .Case("assfail", true) .Case("db_error", true) .Case("__assert", true) // For the purpose of static analysis, we do not care that // this MSVC function will return if the user decides to continue. .Case("_wassert", true) .Case("__assert_rtn", true) .Case("__assert_fail", true) .Case("dtrace_assfail", true) .Case("yy_fatal_error", true) .Case("_XCAssertionFailureHandler", true) .Case("_DTAssertionFailureHandler", true) .Case("_TSAssertionFailureHandler", true) .Default(false); } } if (BuildSinks) C.generateSink(); }
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 SimpleStreamChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); if (!Call.isGlobalCFunction()) return; if (Call.getCalleeIdentifier() != IIfopen) return; // Get the symbolic value corresponding to the file handle. SymbolRef FileDesc = Call.getReturnValue().getAsSymbol(); if (!FileDesc) return; // Generate the next transition (an edge in the exploded graph). ProgramStateRef State = C.getState(); State = State->set<StreamMap>(FileDesc, StreamState::getOpened()); C.addTransition(State); }
void DoubleFetchChecker::checkPreCall(const CallEvent &Call,CheckerContext &Ctx) const { const IdentifierInfo *ID = Call.getCalleeIdentifier(); ProgramStateRef state = Ctx.getState(); if (ID == NULL) { return; } std::cout<<"[checkPreCall]-----call function:"<<ID->getName().str()<<std::endl; if(ID->getName() == "kernel_func") { ProgramStateRef state = Ctx.getState(); SVal arg = Call.getArgSVal(0); const MemRegion* mr = arg.getAsRegion(); /* state = state->add<TaintRegionMap>(mr); Ctx.addTransition(state); SVal val = state->getSVal(mr); ProgramStateRef newstate = addTaintToSymExpr(state, val); if(newstate){ Ctx.addTransition(newstate); std::cout<<"[checkPreCall] arg add taint finish: "<<toStr(arg)<<std::endl; } else std::cout<<"[checkPreCall] arg add taint failed: "<<toStr(arg)<<std::endl; */ } if (ID->getName() == "__builtin___memcpy_chk") { SVal Arg0 = Call.getArgSVal(0); SVal Arg1 = Call.getArgSVal(1); SVal Arg2 = Call.getArgSVal(2); const Expr * erg0 = Call.getArgExpr(0); const Expr * erg1 = Call.getArgExpr(1); const Expr * erg2 = Call.getArgExpr(2); if(this->isTaintedByTime(state,Arg0)){ std::cout<<"[checkPreCall]"<<"\tArg0, tainted, \t "<<std::endl; showTaintByTime(state, Arg0); } else std::cout<<"[checkPreCall]"<<"\tArg0, not tainted, \t "<<std::endl; if(this->isTaintedByTime(state,Arg1)){ std::cout<<"[checkPreCall]"<<"\tArg1, tainted, \t "<<std::endl; showTaintByTime(state, Arg1); } else std::cout<<"[checkPreCall]"<<"\tArg1, not tainted, \t "<<std::endl; if(this->isTaintedByTime(state,Arg2)){ std::cout<<"[checkPreCall]"<<"\tArg2, tainted, \t "<<std::endl; showTaintByTime(state, Arg2); } else std::cout<<"[checkPreCall]"<<"\tArg2, not tainted, \t "<<std::endl; if(diffTaintInBranch(state,Arg0,erg0,Ctx.getSourceManager())){ llvm::errs() << "### Found DF1!!#####\n"; this->reportDoubleFetch(Ctx, Call); } if(diffTaintInBranch(state,Arg1,erg1,Ctx.getSourceManager())){ llvm::errs() << "### Found DF2!!#####\n"; this->reportDoubleFetch(Ctx, Call); } if(diffTaintInBranch(state,Arg2,erg2, Ctx.getSourceManager())){ llvm::errs() << "### Found DF3!!#####\n"; this->reportDoubleFetch(Ctx, Call); } } }
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 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); } }
// Checking for SecItemAdd and SecItemUpdate, no particular reason for assigning it to PostCall void iOSAppSecInsecureKeyChainStorageChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { do { initIdentifierInfo( C.getASTContext() ) ; //redwud: Obviously it is what it is if ( !Call.isGlobalCFunction() ) { break ; } const IdentifierInfo *pCalleeIdent = Call.getCalleeIdentifier() ; // for SecITemAdd() by default unsigned int uiParam = 0 ; //We do away with array because it's only two of them and it //will break the while..passing pattern if ( pCalleeIdent != m_piiSecItemAdd ) { if ( (pCalleeIdent != m_piiSecItemUpdate) ) { break ; } else { uiParam = 1 ; } } //Get the query parameter //Check if it was recorded as not secure //Then create a report //Else do nothing // Use new method to reuse for checking each parameters, // 1st param for SecItemAdd(), 2nd param for SecItemUpdate() // Get the symbolic value corresponding to the "attributes" parameter. SymbolRef pSymToCheck = Call.getArgSVal( uiParam ).getAsSymbol() ; //rewud: Not sure how to interpret this, it seems there are no // no symbol for the first parameter if ( !pSymToCheck ) { break ; } ProgramStateRef pProgState = C.getState() ; if ( !isInsecureSymbol( pSymToCheck, pProgState ) ) { break ; } if ( !m_pInsecureInstBugType ) { MSEC_DEBUG( "redwud: ", "!m_pInsecureInstBugType" ) ; break ; } //Report this instance CMSecCommon::reportInsecureInstance( pSymToCheck, C, C.addTransition( pProgState ) , *m_pInsecureInstBugType, m_szReportDesc ) ; } while ( _PASSING_ ) ; }
// 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_ ) ; }