Skip to content

Commit c2c9841

Browse files
author
git apple-llvm automerger
committed
Merge commit '289725f11c57' from llvm.org/main into next
2 parents 5b0b196 + 289725f commit c2c9841

File tree

6 files changed

+226
-20
lines changed

6 files changed

+226
-20
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ Warns when a nullable pointer is returned from a function that has _Nonnull retu
599599
optin
600600
^^^^^
601601
602-
Checkers for portability, performance or coding style specific rules.
602+
Checkers for portability, performance, optional security and coding style specific rules.
603603
604604
.. _optin-core-EnumCastOutOfRange:
605605
@@ -938,6 +938,53 @@ optin.portability.UnixAPI
938938
"""""""""""""""""""""""""
939939
Finds implementation-defined behavior in UNIX/Posix functions.
940940
941+
.. _optin-taint-TaintedAlloc:
942+
943+
optin.taint.TaintedAlloc (C, C++)
944+
"""""""""""""""""""""""""""""""""
945+
946+
This checker warns for cases when the ``size`` parameter of the ``malloc`` ,
947+
``calloc``, ``realloc``, ``alloca`` or the size parameter of the
948+
array new C++ operator is tainted (potentially attacker controlled).
949+
If an attacker can inject a large value as the size parameter, memory exhaustion
950+
denial of service attack can be carried out.
951+
952+
The ``alpha.security.taint.TaintPropagation`` checker also needs to be enabled for
953+
this checker to give warnings.
954+
955+
The analyzer emits warning only if it cannot prove that the size parameter is
956+
within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
957+
covers the SEI Cert coding standard rule `INT04-C
958+
<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.
959+
960+
You can silence this warning either by bound checking the ``size`` parameter, or
961+
by explicitly marking the ``size`` parameter as sanitized. See the
962+
:ref:`alpha-security-taint-TaintPropagation` checker for more details.
963+
964+
.. code-block:: c
965+
966+
void vulnerable(void) {
967+
size_t size = 0;
968+
scanf("%zu", &size);
969+
int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
970+
free(p);
971+
}
972+
973+
void not_vulnerable(void) {
974+
size_t size = 0;
975+
scanf("%zu", &size);
976+
if (1024 < size)
977+
return;
978+
int *p = malloc(size); // No warning expected as the the user input is bound
979+
free(p);
980+
}
981+
982+
void vulnerable_cpp(void) {
983+
size_t size = 0;
984+
scanf("%zu", &size);
985+
int *ptr = new int[size];// warn: Memory allocation function is called with a tainted (potentially attacker controlled) value
986+
delete[] ptr;
987+
}
941988
942989
.. _security-checkers:
943990

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,17 @@ def CoreAlpha : Package<"core">, ParentPackage<Alpha>;
3636
// Note: OptIn is *not* intended for checkers that are too noisy to be on by
3737
// default. Such checkers belong in the alpha package.
3838
def OptIn : Package<"optin">;
39+
3940
def CoreOptIn : Package<"core">, ParentPackage<OptIn>;
4041

4142
// In the Portability package reside checkers for finding code that relies on
4243
// implementation-defined behavior. Such checks are wanted for cross-platform
4344
// development, but unwanted for developers who target only a single platform.
4445
def PortabilityOptIn : Package<"portability">, ParentPackage<OptIn>;
4546

47+
// Optional checkers related to taint security analysis.
48+
def TaintOptIn : Package<"taint">, ParentPackage<OptIn>;
49+
4650
def Nullability : Package<"nullability">,
4751
PackageOptions<[
4852
CmdLineOption<Boolean,
@@ -1718,6 +1722,23 @@ def UnixAPIPortabilityChecker : Checker<"UnixAPI">,
17181722

17191723
} // end optin.portability
17201724

1725+
1726+
//===----------------------------------------------------------------------===//
1727+
// Taint checkers.
1728+
//===----------------------------------------------------------------------===//
1729+
1730+
let ParentPackage = TaintOptIn in {
1731+
1732+
def TaintedAllocChecker: Checker<"TaintedAlloc">,
1733+
HelpText<"Check for memory allocations, where the size parameter "
1734+
"might be a tainted (attacker controlled) value.">,
1735+
Dependencies<[DynamicMemoryModeling]>,
1736+
//FIXME: GenericTaintChecker should be a dependency, but only after it
1737+
//is transformed into a modeling checker
1738+
Documentation<HasDocumentation>;
1739+
1740+
} // end "optin.taint"
1741+
17211742
//===----------------------------------------------------------------------===//
17221743
// NonDeterminism checkers.
17231744
//===----------------------------------------------------------------------===//

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
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+
17901863
ProgramStateRef 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

27622833
ProgramStateRef 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)
37343805
REGISTER_CHECKER(NewDeleteChecker)
37353806
REGISTER_CHECKER(NewDeleteLeaksChecker)
37363807
REGISTER_CHECKER(MismatchedDeallocatorChecker)
3808+
REGISTER_CHECKER(TaintedAllocChecker)

clang/test/Analysis/malloc.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// RUN: -analyzer-checker=alpha.deadcode.UnreachableCode \
44
// RUN: -analyzer-checker=alpha.core.CastSize \
55
// RUN: -analyzer-checker=unix \
6-
// RUN: -analyzer-checker=debug.ExprInspection
6+
// RUN: -analyzer-checker=debug.ExprInspection \
7+
// RUN: -analyzer-checker=alpha.security.taint.TaintPropagation \
8+
// RUN: -analyzer-checker=optin.taint.TaintedAlloc
79

810
#include "Inputs/system-header-simulator.h"
911

@@ -48,6 +50,45 @@ void myfoo(int *p);
4850
void myfooint(int p);
4951
char *fooRetPtr(void);
5052

53+
void t1(void) {
54+
size_t size = 0;
55+
scanf("%zu", &size);
56+
int *p = malloc(size); // expected-warning{{malloc is called with a tainted (potentially attacker controlled) value}}
57+
free(p);
58+
}
59+
60+
void t2(void) {
61+
size_t size = 0;
62+
scanf("%zu", &size);
63+
int *p = calloc(size,2); // expected-warning{{calloc is called with a tainted (potentially attacker controlled) value}}
64+
free(p);
65+
}
66+
67+
void t3(void) {
68+
size_t size = 0;
69+
scanf("%zu", &size);
70+
if (1024 < size)
71+
return;
72+
int *p = malloc(size); // No warning expected as the the user input is bound
73+
free(p);
74+
}
75+
76+
void t4(void) {
77+
size_t size = 0;
78+
int *p = malloc(sizeof(int));
79+
scanf("%zu", &size);
80+
p = (int*) realloc((void*) p, size); // expected-warning{{realloc is called with a tainted (potentially attacker controlled) value}}
81+
free(p);
82+
}
83+
84+
void t5(void) {
85+
size_t size = 0;
86+
int *p = alloca(sizeof(int));
87+
scanf("%zu", &size);
88+
p = (int*) alloca(size); // expected-warning{{alloca is called with a tainted (potentially attacker controlled) value}}
89+
}
90+
91+
5192
void f1(void) {
5293
int *p = malloc(12);
5394
return; // expected-warning{{Potential leak of memory pointed to by 'p'}}

0 commit comments

Comments
 (0)