60
60
#include " clang/Basic/TargetInfo.h"
61
61
#include " clang/Lex/Lexer.h"
62
62
#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
63
+ #include " clang/StaticAnalyzer/Checkers/Taint.h"
63
64
#include " clang/StaticAnalyzer/Core/BugReporter/BugType.h"
64
65
#include " clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
65
66
#include " clang/StaticAnalyzer/Core/Checker.h"
@@ -322,6 +323,7 @@ class MallocChecker
322
323
CK_NewDeleteLeaksChecker,
323
324
CK_MismatchedDeallocatorChecker,
324
325
CK_InnerPointerChecker,
326
+ CK_TaintedAllocChecker,
325
327
CK_NumCheckKinds
326
328
};
327
329
@@ -365,6 +367,7 @@ class MallocChecker
365
367
mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
366
368
mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
367
369
mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
370
+ mutable std::unique_ptr<BugType> BT_TaintedAlloc;
368
371
369
372
#define CHECK_FN (NAME ) \
370
373
void NAME (const CallEvent &Call, CheckerContext &C) const ;
@@ -462,6 +465,13 @@ class MallocChecker
462
465
};
463
466
464
467
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 ;
465
475
466
476
// TODO: Remove mutable by moving the initializtaion to the registry function.
467
477
mutable std::optional<uint64_t > KernelZeroFlagVal;
@@ -521,9 +531,9 @@ class MallocChecker
521
531
// / malloc leaves it undefined.
522
532
// / \param [in] State The \c ProgramState right before allocation.
523
533
// / \returns The ProgramState right after allocation.
524
- [[nodiscard]] static ProgramStateRef
534
+ [[nodiscard]] ProgramStateRef
525
535
MallocMemAux (CheckerContext &C, const CallEvent &Call, const Expr *SizeEx,
526
- SVal Init, ProgramStateRef State, AllocationFamily Family);
536
+ SVal Init, ProgramStateRef State, AllocationFamily Family) const ;
527
537
528
538
// / Models memory allocation.
529
539
// /
@@ -534,9 +544,10 @@ class MallocChecker
534
544
// / malloc leaves it undefined.
535
545
// / \param [in] State The \c ProgramState right before allocation.
536
546
// / \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 ;
540
551
541
552
// Check if this malloc() for special flags. At present that means M_ZERO or
542
553
// __GFP_ZERO (in which case, treat it like calloc).
@@ -649,8 +660,9 @@ class MallocChecker
649
660
// / \param [in] Call The expression that reallocated memory
650
661
// / \param [in] State The \c ProgramState right before reallocation.
651
662
// / \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 ;
654
666
655
667
// / See if deallocation happens in a suspicious context. If so, escape the
656
668
// / pointers that otherwise would have been deallocated and return true.
@@ -1695,6 +1707,11 @@ MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,
1695
1707
// MallocUpdateRefState() instead of MallocMemAux() which breaks the
1696
1708
// existing binding.
1697
1709
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
+
1698
1715
State = MallocUpdateRefState (C, NE, State, Family, Target);
1699
1716
State = ProcessZeroAllocCheck (Call, 0 , State, Target);
1700
1717
return State;
@@ -1779,18 +1796,74 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
1779
1796
const CallEvent &Call,
1780
1797
const Expr *SizeEx, SVal Init,
1781
1798
ProgramStateRef State,
1782
- AllocationFamily Family) {
1799
+ AllocationFamily Family) const {
1783
1800
if (!State)
1784
1801
return nullptr ;
1785
1802
1786
1803
assert (SizeEx);
1787
1804
return MallocMemAux (C, Call, C.getSVal (SizeEx), Init, State, Family);
1788
1805
}
1789
1806
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
+
1790
1863
ProgramStateRef MallocChecker::MallocMemAux (CheckerContext &C,
1791
1864
const CallEvent &Call, SVal Size,
1792
1865
SVal Init, ProgramStateRef State,
1793
- AllocationFamily Family) {
1866
+ AllocationFamily Family) const {
1794
1867
if (!State)
1795
1868
return nullptr ;
1796
1869
@@ -1819,9 +1892,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
1819
1892
if (Size.isUndef ())
1820
1893
Size = UnknownVal ();
1821
1894
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);
1825
1896
1826
1897
// Set the region's extent.
1827
1898
State = setDynamicExtent (State, RetVal.getAsRegion (),
@@ -2761,7 +2832,7 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
2761
2832
2762
2833
ProgramStateRef MallocChecker::CallocMem (CheckerContext &C,
2763
2834
const CallEvent &Call,
2764
- ProgramStateRef State) {
2835
+ ProgramStateRef State) const {
2765
2836
if (!State)
2766
2837
return nullptr ;
2767
2838
@@ -3734,3 +3805,4 @@ REGISTER_CHECKER(MallocChecker)
3734
3805
REGISTER_CHECKER (NewDeleteChecker)
3735
3806
REGISTER_CHECKER (NewDeleteLeaksChecker)
3736
3807
REGISTER_CHECKER (MismatchedDeallocatorChecker)
3808
+ REGISTER_CHECKER (TaintedAllocChecker)
0 commit comments