6060#include " clang/Basic/TargetInfo.h"
6161#include " clang/Lex/Lexer.h"
6262#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
63+ #include " clang/StaticAnalyzer/Checkers/Taint.h"
6364#include " clang/StaticAnalyzer/Core/BugReporter/BugType.h"
6465#include " clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
6566#include " clang/StaticAnalyzer/Core/Checker.h"
@@ -322,6 +323,7 @@ class MallocChecker
322323 CK_NewDeleteLeaksChecker,
323324 CK_MismatchedDeallocatorChecker,
324325 CK_InnerPointerChecker,
326+ CK_TaintedAllocChecker,
325327 CK_NumCheckKinds
326328 };
327329
@@ -365,6 +367,7 @@ class MallocChecker
365367 mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
366368 mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
367369 mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
370+ mutable std::unique_ptr<BugType> BT_TaintedAlloc;
368371
369372#define CHECK_FN (NAME ) \
370373 void NAME (const CallEvent &Call, CheckerContext &C) const ;
@@ -462,6 +465,13 @@ class MallocChecker
462465 };
463466
464467 bool isMemCall (const CallEvent &Call) const ;
468+ void reportTaintBug (StringRef Msg, ProgramStateRef State, CheckerContext &C,
469+ llvm::ArrayRef<SymbolRef> TaintedSyms,
470+ AllocationFamily Family) const ;
471+
472+ void checkTaintedness (CheckerContext &C, const CallEvent &Call,
473+ const SVal SizeSVal, ProgramStateRef State,
474+ AllocationFamily Family) const ;
465475
466476 // TODO: Remove mutable by moving the initializtaion to the registry function.
467477 mutable std::optional<uint64_t > KernelZeroFlagVal;
@@ -521,9 +531,9 @@ class MallocChecker
521531 // / malloc leaves it undefined.
522532 // / \param [in] State The \c ProgramState right before allocation.
523533 // / \returns The ProgramState right after allocation.
524- [[nodiscard]] static ProgramStateRef
534+ [[nodiscard]] ProgramStateRef
525535 MallocMemAux (CheckerContext &C, const CallEvent &Call, const Expr *SizeEx,
526- SVal Init, ProgramStateRef State, AllocationFamily Family);
536+ SVal Init, ProgramStateRef State, AllocationFamily Family) const ;
527537
528538 // / Models memory allocation.
529539 // /
@@ -534,9 +544,10 @@ class MallocChecker
534544 // / malloc leaves it undefined.
535545 // / \param [in] State The \c ProgramState right before allocation.
536546 // / \returns The ProgramState right after allocation.
537- [[nodiscard]] static ProgramStateRef
538- MallocMemAux (CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init,
539- ProgramStateRef State, AllocationFamily Family);
547+ [[nodiscard]] ProgramStateRef MallocMemAux (CheckerContext &C,
548+ const CallEvent &Call, SVal Size,
549+ SVal Init, ProgramStateRef State,
550+ AllocationFamily Family) const ;
540551
541552 // Check if this malloc() for special flags. At present that means M_ZERO or
542553 // __GFP_ZERO (in which case, treat it like calloc).
@@ -649,8 +660,9 @@ class MallocChecker
649660 // / \param [in] Call The expression that reallocated memory
650661 // / \param [in] State The \c ProgramState right before reallocation.
651662 // / \returns The ProgramState right after allocation.
652- [[nodiscard]] static ProgramStateRef
653- CallocMem (CheckerContext &C, const CallEvent &Call, ProgramStateRef State);
663+ [[nodiscard]] ProgramStateRef CallocMem (CheckerContext &C,
664+ const CallEvent &Call,
665+ ProgramStateRef State) const ;
654666
655667 // / See if deallocation happens in a suspicious context. If so, escape the
656668 // / pointers that otherwise would have been deallocated and return true.
@@ -1695,6 +1707,11 @@ MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,
16951707 // MallocUpdateRefState() instead of MallocMemAux() which breaks the
16961708 // existing binding.
16971709 SVal Target = Call.getObjectUnderConstruction ();
1710+ if (Call.getOriginExpr ()->isArray ()) {
1711+ if (auto SizeEx = NE->getArraySize ())
1712+ checkTaintedness (C, Call, C.getSVal (*SizeEx), State, AF_CXXNewArray);
1713+ }
1714+
16981715 State = MallocUpdateRefState (C, NE, State, Family, Target);
16991716 State = ProcessZeroAllocCheck (Call, 0 , State, Target);
17001717 return State;
@@ -1779,18 +1796,74 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
17791796 const CallEvent &Call,
17801797 const Expr *SizeEx, SVal Init,
17811798 ProgramStateRef State,
1782- AllocationFamily Family) {
1799+ AllocationFamily Family) const {
17831800 if (!State)
17841801 return nullptr ;
17851802
17861803 assert (SizeEx);
17871804 return MallocMemAux (C, Call, C.getSVal (SizeEx), Init, State, Family);
17881805}
17891806
1807+ void MallocChecker::reportTaintBug (StringRef Msg, ProgramStateRef State,
1808+ CheckerContext &C,
1809+ llvm::ArrayRef<SymbolRef> TaintedSyms,
1810+ AllocationFamily Family) const {
1811+ if (ExplodedNode *N = C.generateNonFatalErrorNode (State, this )) {
1812+ if (!BT_TaintedAlloc)
1813+ BT_TaintedAlloc.reset (new BugType (CheckNames[CK_TaintedAllocChecker],
1814+ " Tainted Memory Allocation" ,
1815+ categories::TaintedData));
1816+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_TaintedAlloc, Msg, N);
1817+ for (auto TaintedSym : TaintedSyms) {
1818+ R->markInteresting (TaintedSym);
1819+ }
1820+ C.emitReport (std::move (R));
1821+ }
1822+ }
1823+
1824+ void MallocChecker::checkTaintedness (CheckerContext &C, const CallEvent &Call,
1825+ const SVal SizeSVal, ProgramStateRef State,
1826+ AllocationFamily Family) const {
1827+ if (!ChecksEnabled[CK_TaintedAllocChecker])
1828+ return ;
1829+ std::vector<SymbolRef> TaintedSyms =
1830+ taint::getTaintedSymbols (State, SizeSVal);
1831+ if (TaintedSyms.empty ())
1832+ return ;
1833+
1834+ SValBuilder &SVB = C.getSValBuilder ();
1835+ QualType SizeTy = SVB.getContext ().getSizeType ();
1836+ QualType CmpTy = SVB.getConditionType ();
1837+ // In case the symbol is tainted, we give a warning if the
1838+ // size is larger than SIZE_MAX/4
1839+ BasicValueFactory &BVF = SVB.getBasicValueFactory ();
1840+ const llvm::APSInt MaxValInt = BVF.getMaxValue (SizeTy);
1841+ NonLoc MaxLength =
1842+ SVB.makeIntVal (MaxValInt / APSIntType (MaxValInt).getValue (4 ));
1843+ std::optional<NonLoc> SizeNL = SizeSVal.getAs <NonLoc>();
1844+ auto Cmp = SVB.evalBinOpNN (State, BO_GE, *SizeNL, MaxLength, CmpTy)
1845+ .getAs <DefinedOrUnknownSVal>();
1846+ if (!Cmp)
1847+ return ;
1848+ auto [StateTooLarge, StateNotTooLarge] = State->assume (*Cmp);
1849+ if (!StateTooLarge && StateNotTooLarge) {
1850+ // We can prove that size is not too large so there is no issue.
1851+ return ;
1852+ }
1853+
1854+ std::string Callee = " Memory allocation function" ;
1855+ if (Call.getCalleeIdentifier ())
1856+ Callee = Call.getCalleeIdentifier ()->getName ().str ();
1857+ reportTaintBug (
1858+ Callee + " is called with a tainted (potentially attacker controlled) "
1859+ " value. Make sure the value is bound checked." ,
1860+ State, C, TaintedSyms, Family);
1861+ }
1862+
17901863ProgramStateRef MallocChecker::MallocMemAux (CheckerContext &C,
17911864 const CallEvent &Call, SVal Size,
17921865 SVal Init, ProgramStateRef State,
1793- AllocationFamily Family) {
1866+ AllocationFamily Family) const {
17941867 if (!State)
17951868 return nullptr ;
17961869
@@ -1819,9 +1892,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
18191892 if (Size.isUndef ())
18201893 Size = UnknownVal ();
18211894
1822- // TODO: If Size is tainted and we cannot prove that it is within
1823- // reasonable bounds, emit a warning that an attacker may
1824- // provoke a memory exhaustion error.
1895+ checkTaintedness (C, Call, Size, State, AF_Malloc);
18251896
18261897 // Set the region's extent.
18271898 State = setDynamicExtent (State, RetVal.getAsRegion (),
@@ -2761,7 +2832,7 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
27612832
27622833ProgramStateRef MallocChecker::CallocMem (CheckerContext &C,
27632834 const CallEvent &Call,
2764- ProgramStateRef State) {
2835+ ProgramStateRef State) const {
27652836 if (!State)
27662837 return nullptr ;
27672838
@@ -3734,3 +3805,4 @@ REGISTER_CHECKER(MallocChecker)
37343805REGISTER_CHECKER (NewDeleteChecker)
37353806REGISTER_CHECKER (NewDeleteLeaksChecker)
37363807REGISTER_CHECKER (MismatchedDeallocatorChecker)
3808+ REGISTER_CHECKER (TaintedAllocChecker)
0 commit comments