void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; // Don't treat functions in namespaces with the same name a Unix function // as a call to the Unix function. const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) return; StringRef FName = C.getCalleeName(FD); if (FName.empty()) return; SubChecker SC = llvm::StringSwitch<SubChecker>(FName) .Case("open", &UnixAPIChecker::CheckOpen) .Case("openat", &UnixAPIChecker::CheckOpenAt) .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) .Case("calloc", &UnixAPIChecker::CheckCallocZero) .Case("malloc", &UnixAPIChecker::CheckMallocZero) .Case("realloc", &UnixAPIChecker::CheckReallocZero) .Case("reallocf", &UnixAPIChecker::CheckReallocfZero) .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero) .Case("__builtin_alloca_with_align", &UnixAPIChecker::CheckAllocaWithAlignZero) .Case("valloc", &UnixAPIChecker::CheckVallocZero) .Default(nullptr); if (SC) (this->*SC)(C, CE); }
void GenericTaintChecker::addSourcesPre(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = nullptr; const FunctionDecl *FDecl = C.getCalleeDecl(CE); if (!FDecl || FDecl->getKind() != Decl::Function) return; StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return; // First, try generating a propagation rule for this function. TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C); if (!Rule.isNull()) { State = Rule.process(CE, C); if (!State) return; C.addTransition(State); return; } // Otherwise, check if we have custom pre-processing implemented. FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) .Case("fscanf", &GenericTaintChecker::preFscanf) .Default(nullptr); // Check and evaluate the call. if (evalFunction) State = (this->*evalFunction)(CE, C); if (!State) return; C.addTransition(State); }
static bool getPrintfFormatArgumentNum(const CallExpr *CE, const CheckerContext &C, unsigned int &ArgNum) { // Find if the function contains a format string argument. // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, // vsnprintf, syslog, custom annotated functions. const FunctionDecl *FDecl = C.getCalleeDecl(CE); if (!FDecl) return false; for (specific_attr_iterator<FormatAttr> i = FDecl->specific_attr_begin<FormatAttr>(), e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) { const FormatAttr *Format = *i; ArgNum = Format->getFormatIdx() - 1; if ((Format->getType() == "printf") && CE->getNumArgs() > ArgNum) return true; } // Or if a function is named setproctitle (this is a heuristic). if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) { ArgNum = 0; return true; } return false; }
void GenericTaintChecker::addSourcesPost(const CallExpr *CE, CheckerContext &C) const { // Define the attack surface. // Set the evaluation function by switching on the callee name. const FunctionDecl *FDecl = C.getCalleeDecl(CE); if (!FDecl || FDecl->getKind() != Decl::Function) return; StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return; FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) .Case("scanf", &GenericTaintChecker::postScanf) // TODO: Add support for vfscanf & family. .Case("getchar", &GenericTaintChecker::postRetTaint) .Case("getchar_unlocked", &GenericTaintChecker::postRetTaint) .Case("getenv", &GenericTaintChecker::postRetTaint) .Case("fopen", &GenericTaintChecker::postRetTaint) .Case("fdopen", &GenericTaintChecker::postRetTaint) .Case("freopen", &GenericTaintChecker::postRetTaint) .Case("getch", &GenericTaintChecker::postRetTaint) .Case("wgetch", &GenericTaintChecker::postRetTaint) .Case("socket", &GenericTaintChecker::postSocket) .Default(nullptr); // If the callee isn't defined, it is not of security concern. // Check and evaluate the call. ProgramStateRef State = nullptr; if (evalFunction) State = (this->*evalFunction)(CE, C); if (!State) return; C.addTransition(State); }
void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; StringRef FName = C.getCalleeName(FD); if (FName.empty()) return; SubChecker SC = llvm::StringSwitch<SubChecker>(FName) .Case("open", &UnixAPIChecker::CheckOpen) .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) .Case("calloc", &UnixAPIChecker::CheckCallocZero) .Case("malloc", &UnixAPIChecker::CheckMallocZero) .Case("realloc", &UnixAPIChecker::CheckReallocZero) .Case("reallocf", &UnixAPIChecker::CheckReallocfZero) .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero) .Case("valloc", &UnixAPIChecker::CheckVallocZero) .Default(nullptr); if (SC) (this->*SC)(C, CE); }
void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); if (state->getSVal(B, LCtx).isUndef()) { // Do not report assignments of uninitialized values inside swap functions. // This should allow to swap partially uninitialized structs // (radar://14129997) if (const FunctionDecl *EnclosingFunctionDecl = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) if (C.getCalleeName(EnclosingFunctionDecl) == "swap") return; // Generate an error node. ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT) BT.reset( new BuiltinBug(this, "Result of operation is garbage or undefined")); SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = nullptr; bool isLeft = true; if (state->getSVal(B->getLHS(), LCtx).isUndef()) { Ex = B->getLHS()->IgnoreParenCasts(); isLeft = true; } else if (state->getSVal(B->getRHS(), LCtx).isUndef()) { Ex = B->getRHS()->IgnoreParenCasts(); isLeft = false; } if (Ex) { OS << "The " << (isLeft ? "left" : "right") << " operand of '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' is a garbage value"; } else { // Neither operand was undefined, but the result is undefined. OS << "The result of the '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' expression is undefined"; } auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); bugreporter::trackNullOrUndefValue(N, Ex, *report); } else bugreporter::trackNullOrUndefValue(N, B, *report); C.emitReport(std::move(report)); } }
void DoubleFetchChecker::checkPostStmt(const CallExpr *CE, CheckerContext &Ctx) const{ ProgramStateRef state = Ctx.getState(); const FunctionDecl *FDecl = Ctx.getCalleeDecl(CE); StringRef funcName = Ctx.getCalleeName(FDecl); //std::cout<<"[checkPostStmt<CallExpr>] func name is:"<<funcName<<std::endl; //printf("[checkPostStmt<CallExpr>] func name is:%s\n",funcName); }
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) return; // Do not report assignments of uninitialized values inside swap functions. // This should allow to swap partially uninitialized structs // (radar://14129997) if (const FunctionDecl *EnclosingFunctionDecl = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) if (C.getCalleeName(EnclosingFunctionDecl) == "swap") return; ExplodedNode *N = C.generateSink(); if (!N) return; const char *str = "Assigned value is garbage or undefined"; if (!BT) BT.reset(new BuiltinBug(str)); // Generate a report for this bug. const Expr *ex = 0; while (StoreE) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { ProgramStateRef state = C.getState(); if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) { str = "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; ex = B->getLHS(); break; } } ex = B->getRHS(); break; } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } break; } BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); bugreporter::trackNullOrUndefValue(N, ex, *R); } C.emitReport(R); }
bool getParamDumper::evalCall(const CallExpr *CE, CheckerContext &C) const { FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) .Case("getParameter",&getParamDumper::analyzerEval) .Case("getUntrackedParameter",&getParamDumper::analyzerEval) .Default(nullptr); if (!Handler) return false; (this->*Handler)(CE,C); return true; }
void DoubleFetchChecker::checkPreStmt(const CallExpr *CE, CheckerContext &Ctx) const{ ProgramStateRef state = Ctx.getState(); const FunctionDecl *FDecl = Ctx.getCalleeDecl(CE); StringRef funcName = Ctx.getCalleeName(FDecl); //std::cout<<"[checkPreStmt<CallExpr>] func name is:"<<funcName.<<std::endl; //printf("[checkPreStmt<CallExpr>] func name is:%s\n",funcName); //std::cout<<"-------------------->getLocStart: "<<CE->getLocStart().getRawEncoding()<<std::endl; //std::cout<<"--------------------->getLocEnd: "<<CE->getLocEnd().getRawEncoding()<<std::endl; //std::cout<<"-------------------->getExprLoc: "<<CE->getExprLoc().getRawEncoding()<<std::endl; unsigned int spelling = Ctx.getSourceManager().getSpellingLineNumber(CE->getExprLoc()); unsigned int ex = Ctx.getSourceManager().getExpansionLineNumber(CE->getExprLoc()); }
void DoubleFetchChecker::checkPostStmt(const CallExpr *CE, CheckerContext &Ctx) const{ ProgramStateRef state = Ctx.getState(); const FunctionDecl *FDecl = Ctx.getCalleeDecl(CE); StringRef funcName = Ctx.getCalleeName(FDecl); /* const Expr * arg0 = CE->getArg(0); std::cout<<"[checkPostStmt]--->arg0: "<<toStr(arg0)<<std::endl; SourceLocation Loc0 = arg0->getExprLoc(); StringRef name0 = Ctx.getMacroNameOrSpelling(Loc0); std::cout<<"[checkPostStmt]---> arg0Namen: "<<name0.str()<<std::endl; */ }
void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { StringRef Name = C.getCalleeName(CE); if (Name.empty()) return; SubChecker SC = llvm::StringSwitch<SubChecker>(Name) .Cases("dispatch_once", "dispatch_once_f", &MacOSXAPIChecker::CheckDispatchOnce) .Default(NULL); if (SC) (this->*SC)(C, CE, Name); }
bool ExprInspectionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // These checks should have no effect on the surrounding environment // (globals should not be invalidated, etc), hence the use of evalCall. FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) .Case("lfort_analyzer_eval", &ExprInspectionChecker::analyzerEval) .Case("lfort_analyzer_checkInlined", &ExprInspectionChecker::analyzerCheckInlined) .Default(0); if (!Handler) return false; (this->*Handler)(CE, C); return true; }
void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef State = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; StringRef funName = C.getCalleeName(FD); // If a value has been allocated, add it to the set for tracking. unsigned idx = getTrackedFunctionIndex(funName, true); if (idx == InvalidIdx) return; const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); // If the argument entered as an enclosing function parameter, skip it to // avoid false positives. if (isEnclosingFunctionParam(ArgExpr) && C.getLocationContext()->getParent() == 0) return; if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { // If the argument points to something that's not a symbolic region, it // can be: // - unknown (cannot reason about it) // - undefined (already reported by other checker) // - constant (null - should not be tracked, // other constant will generate a compiler warning) // - goto (should be reported by other checker) // The call return value symbol should stay alive for as long as the // allocated value symbol, since our diagnostics depend on the value // returned by the call. Ex: Data should only be freed if noErr was // returned during allocation.) SymbolRef RetStatusSymbol = State->getSVal(CE, C.getLocationContext()).getAsSymbol(); C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); // Track the allocated value in the checker state. State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, RetStatusSymbol)); assert(State); C.addTransition(State); } }
void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { StringRef Name = C.getCalleeName(CE); if (Name.empty() || CE->getNumArgs() < 2) return; // Check the array access. if (Name.equals("CFArrayGetValueAtIndex")) { ProgramStateRef State = C.getState(); // Retrieve the size. // Find out if we saw this array symbol before and have information about // it. const Expr *ArrayExpr = CE->getArg(0); SymbolRef ArraySym = getArraySym(ArrayExpr, C); if (!ArraySym) return; const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); if (!Size) return; // Get the index. const Expr *IdxExpr = CE->getArg(1); SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); if (IdxVal.isUnknownOrUndef()) return; DefinedSVal Idx = IdxVal.castAs<DefinedSVal>(); // Now, check if 'Idx in [0, Size-1]'. const QualType T = IdxExpr->getType(); ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) return; initBugType(); auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N); R->addRange(IdxExpr->getSourceRange()); C.emitReport(std::move(R)); return; } } }
void PthreadLockChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); StringRef FName = C.getCalleeName(CE); if (FName.empty()) return; if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) return; if (FName == "pthread_mutex_lock" || FName == "pthread_rwlock_rdlock" || FName == "pthread_rwlock_wrlock") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), false, PthreadSemantics); else if (FName == "lck_mtx_lock" || FName == "lck_rw_lock_exclusive" || FName == "lck_rw_lock_shared") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), false, XNUSemantics); else if (FName == "pthread_mutex_trylock" || FName == "pthread_rwlock_tryrdlock" || FName == "pthread_rwlock_trywrlock") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), true, PthreadSemantics); else if (FName == "lck_mtx_try_lock" || FName == "lck_rw_try_lock_exclusive" || FName == "lck_rw_try_lock_shared") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), true, XNUSemantics); else if (FName == "pthread_mutex_unlock" || FName == "pthread_rwlock_unlock" || FName == "lck_mtx_unlock" || FName == "lck_rw_done") ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); else if (FName == "pthread_mutex_destroy" || FName == "lck_mtx_destroy") DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); else if (FName == "pthread_mutex_init") InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); }
void GenericTaintChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { if (!C.getState()) return; StringRef Name = C.getCalleeName(CE); // Define the attack surface. // Set the evaluation function by switching on the callee name. FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) .Case("scanf", &GenericTaintChecker::processScanf) .Case("getchar", &GenericTaintChecker::processRetTaint) .Default(NULL); // If the callee isn't defined, it is not of security concern. // Check and evaluate the call. if (evalFunction) (this->*evalFunction)(CE, C); }
void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { StringRef FName = C.getCalleeName(CE); if (FName.empty()) return; SubChecker SC = llvm::StringSwitch<SubChecker>(FName) .Case("open", &UnixAPIChecker::CheckOpen) .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) .Case("calloc", &UnixAPIChecker::CheckCallocZero) .Case("malloc", &UnixAPIChecker::CheckMallocZero) .Case("realloc", &UnixAPIChecker::CheckReallocZero) .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero) .Case("valloc", &UnixAPIChecker::CheckVallocZero) .Default(NULL); if (SC) (this->*SC)(C, CE); }
bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{ if (checkUncontrolledFormatString(CE, C)) return true; const FunctionDecl *FDecl = C.getCalleeDecl(CE); if (!FDecl || FDecl->getKind() != Decl::Function) return false; StringRef Name = C.getCalleeName(FDecl); if (Name.empty()) return false; if (checkSystemCall(CE, Name, C)) return true; if (checkTaintedBufferSize(CE, FDecl, C)) return true; return false; }
void ObjCContainersChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { StringRef Name = C.getCalleeName(CE); if (Name.empty() || CE->getNumArgs() < 1) return; // Add array size information to the state. if (Name.equals("CFArrayCreate")) { if (CE->getNumArgs() < 3) return; // Note, we can visit the Create method in the post-visit because // the CFIndex parameter is passed in by value and will not be invalidated // by the call. addSizeInfo(CE, CE->getArg(2), C); return; } if (Name.equals("CFArrayGetCount")) { addSizeInfo(CE->getArg(0), CE, C); return; } }
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) return; // Do not report assignments of uninitialized values inside swap functions. // This should allow to swap partially uninitialized structs // (radar://14129997) if (const FunctionDecl *EnclosingFunctionDecl = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) if (C.getCalleeName(EnclosingFunctionDecl) == "swap") return; ExplodedNode *N = C.generateErrorNode(); if (!N) return; static const char *const DefaultMsg = "Assigned value is garbage or undefined"; if (!BT) BT.reset(new BuiltinBug(this, DefaultMsg)); // Generate a report for this bug. llvm::SmallString<128> Str; llvm::raw_svector_ostream OS(Str); const Expr *ex = nullptr; while (StoreE) { if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) { OS << "The expression is an uninitialized value. " "The computed value will also be garbage"; ex = U->getSubExpr(); break; } if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { if (C.getSVal(B->getLHS()).isUndef()) { OS << "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; ex = B->getLHS(); break; } } ex = B->getRHS(); break; } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } if (const auto *CD = dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) { if (CD->isImplicit()) { for (auto I : CD->inits()) { if (I->getInit()->IgnoreImpCasts() == StoreE) { OS << "Value assigned to field '" << I->getMember()->getName() << "' in implicit constructor is garbage or undefined"; break; } } } } break; } if (OS.str().empty()) OS << DefaultMsg; auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N); if (ex) { R->addRange(ex->getSourceRange()); bugreporter::trackExpressionValue(N, ex, *R); } C.emitReport(std::move(R)); }
void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { unsigned idx = InvalidIdx; ProgramStateRef State = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD || FD->getKind() != Decl::Function) return; StringRef funName = C.getCalleeName(FD); if (funName.empty()) return; // If it is a call to an allocator function, it could be a double allocation. idx = getTrackedFunctionIndex(funName, true); if (idx != InvalidIdx) { const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) if (const AllocationState *AS = State->get<AllocatedData>(V)) { if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { // Remove the value from the state. The new symbol will be added for // tracking when the second allocator is processed in checkPostStmt(). State = State->remove<AllocatedData>(V); ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; os << "Allocated data should be released before another call to " << "the allocator: missing a call to '" << FunctionsToTrack[DIdx].Name << "'."; BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); } } return; } // Is it a call to one of deallocator functions? idx = getTrackedFunctionIndex(funName, false); if (idx == InvalidIdx) return; // Check the argument to the deallocator. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); // Undef is reported by another checker. if (ArgSVal.isUndef()) return; SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); // If the argument is coming from the heap, globals, or unknown, do not // report it. bool RegionArgIsBad = false; if (!ArgSM) { if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) return; RegionArgIsBad = true; } // Is the argument to the call being tracked? const AllocationState *AS = State->get<AllocatedData>(ArgSM); if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { return; } // If trying to free data which has not been allocated yet, report as a bug. // TODO: We might want a more precise diagnostic for double free // (that would involve tracking all the freed symbols in the checker state). if (!AS || RegionArgIsBad) { // It is possible that this is a false positive - the argument might // have entered as an enclosing function parameter. if (isEnclosingFunctionParam(ArgExpr)) return; ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) Report->markInteresting(AS->Region); C.emitReport(Report); return; } // Process functions which might deallocate. if (FunctionsToTrack[idx].Kind == PossibleAPI) { if (funName == "CFStringCreateWithBytesNoCopy") { const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); // NULL ~ default deallocator, so warn. if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), Expr::NPC_ValueDependentIsNotNull)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // One of the default allocators, so warn. if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { StringRef DeallocatorName = DE->getFoundDecl()->getName(); if (DeallocatorName == "kCFAllocatorDefault" || DeallocatorName == "kCFAllocatorSystemDefault" || DeallocatorName == "kCFAllocatorMalloc") { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If kCFAllocatorNull, which does not deallocate, we still have to // find the deallocator. if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") return; } // In all other cases, assume the user supplied a correct deallocator // that will free memory so stop tracking. State = State->remove<AllocatedData>(ArgSM); C.addTransition(State); return; } llvm_unreachable("We know of no other possible APIs."); } // The call is deallocating a value we previously allocated, so remove it // from the next state. State = State->remove<AllocatedData>(ArgSM); // Check if the proper deallocator is used. unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { const AllocationPair AP = std::make_pair(ArgSM, AS); generateDeallocatorMismatchReport(AP, ArgExpr, C); return; } // If the buffer can be null and the return status can be an error, // report a bad call to free. if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Only call free if a valid (non-NULL) buffer was returned.", N); Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); return; } C.addTransition(State); }