1e5dd7070Spatrick //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines a set of flow-insensitive security checks.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick
13e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14e5dd7070Spatrick #include "clang/AST/StmtVisitor.h"
15e5dd7070Spatrick #include "clang/Analysis/AnalysisDeclContext.h"
16e5dd7070Spatrick #include "clang/Basic/TargetInfo.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
19e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
21e5dd7070Spatrick #include "llvm/ADT/StringSwitch.h"
22e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
23e5dd7070Spatrick
24e5dd7070Spatrick using namespace clang;
25e5dd7070Spatrick using namespace ento;
26e5dd7070Spatrick
isArc4RandomAvailable(const ASTContext & Ctx)27e5dd7070Spatrick static bool isArc4RandomAvailable(const ASTContext &Ctx) {
28e5dd7070Spatrick const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
29e5dd7070Spatrick return T.getVendor() == llvm::Triple::Apple ||
30e5dd7070Spatrick T.getOS() == llvm::Triple::CloudABI ||
31e5dd7070Spatrick T.isOSFreeBSD() ||
32e5dd7070Spatrick T.isOSNetBSD() ||
33e5dd7070Spatrick T.isOSOpenBSD() ||
34e5dd7070Spatrick T.isOSDragonFly();
35e5dd7070Spatrick }
36e5dd7070Spatrick
37e5dd7070Spatrick namespace {
38e5dd7070Spatrick struct ChecksFilter {
39*12c85518Srobert bool check_bcmp = false;
40*12c85518Srobert bool check_bcopy = false;
41*12c85518Srobert bool check_bzero = false;
42*12c85518Srobert bool check_gets = false;
43*12c85518Srobert bool check_getpw = false;
44*12c85518Srobert bool check_mktemp = false;
45*12c85518Srobert bool check_mkstemp = false;
46*12c85518Srobert bool check_strcpy = false;
47*12c85518Srobert bool check_DeprecatedOrUnsafeBufferHandling = false;
48*12c85518Srobert bool check_rand = false;
49*12c85518Srobert bool check_vfork = false;
50*12c85518Srobert bool check_FloatLoopCounter = false;
51*12c85518Srobert bool check_UncheckedReturn = false;
52*12c85518Srobert bool check_decodeValueOfObjCType = false;
53e5dd7070Spatrick
54e5dd7070Spatrick CheckerNameRef checkName_bcmp;
55e5dd7070Spatrick CheckerNameRef checkName_bcopy;
56e5dd7070Spatrick CheckerNameRef checkName_bzero;
57e5dd7070Spatrick CheckerNameRef checkName_gets;
58e5dd7070Spatrick CheckerNameRef checkName_getpw;
59e5dd7070Spatrick CheckerNameRef checkName_mktemp;
60e5dd7070Spatrick CheckerNameRef checkName_mkstemp;
61e5dd7070Spatrick CheckerNameRef checkName_strcpy;
62e5dd7070Spatrick CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
63e5dd7070Spatrick CheckerNameRef checkName_rand;
64e5dd7070Spatrick CheckerNameRef checkName_vfork;
65e5dd7070Spatrick CheckerNameRef checkName_FloatLoopCounter;
66e5dd7070Spatrick CheckerNameRef checkName_UncheckedReturn;
67e5dd7070Spatrick CheckerNameRef checkName_decodeValueOfObjCType;
68e5dd7070Spatrick };
69e5dd7070Spatrick
70e5dd7070Spatrick class WalkAST : public StmtVisitor<WalkAST> {
71e5dd7070Spatrick BugReporter &BR;
72e5dd7070Spatrick AnalysisDeclContext* AC;
73e5dd7070Spatrick enum { num_setids = 6 };
74e5dd7070Spatrick IdentifierInfo *II_setid[num_setids];
75e5dd7070Spatrick
76e5dd7070Spatrick const bool CheckRand;
77e5dd7070Spatrick const ChecksFilter &filter;
78e5dd7070Spatrick
79e5dd7070Spatrick public:
WalkAST(BugReporter & br,AnalysisDeclContext * ac,const ChecksFilter & f)80e5dd7070Spatrick WalkAST(BugReporter &br, AnalysisDeclContext* ac,
81e5dd7070Spatrick const ChecksFilter &f)
82e5dd7070Spatrick : BR(br), AC(ac), II_setid(),
83e5dd7070Spatrick CheckRand(isArc4RandomAvailable(BR.getContext())),
84e5dd7070Spatrick filter(f) {}
85e5dd7070Spatrick
86e5dd7070Spatrick // Statement visitor methods.
87e5dd7070Spatrick void VisitCallExpr(CallExpr *CE);
88e5dd7070Spatrick void VisitObjCMessageExpr(ObjCMessageExpr *CE);
89e5dd7070Spatrick void VisitForStmt(ForStmt *S);
90e5dd7070Spatrick void VisitCompoundStmt (CompoundStmt *S);
VisitStmt(Stmt * S)91e5dd7070Spatrick void VisitStmt(Stmt *S) { VisitChildren(S); }
92e5dd7070Spatrick
93e5dd7070Spatrick void VisitChildren(Stmt *S);
94e5dd7070Spatrick
95e5dd7070Spatrick // Helpers.
96e5dd7070Spatrick bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
97e5dd7070Spatrick
98e5dd7070Spatrick typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
99e5dd7070Spatrick typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
100e5dd7070Spatrick
101e5dd7070Spatrick // Checker-specific methods.
102e5dd7070Spatrick void checkLoopConditionForFloat(const ForStmt *FS);
103e5dd7070Spatrick void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
104e5dd7070Spatrick void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
105e5dd7070Spatrick void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
106e5dd7070Spatrick void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
107e5dd7070Spatrick void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
108e5dd7070Spatrick void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
109e5dd7070Spatrick void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
110e5dd7070Spatrick void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
111e5dd7070Spatrick void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
112e5dd7070Spatrick void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
113e5dd7070Spatrick const FunctionDecl *FD);
114e5dd7070Spatrick void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
115e5dd7070Spatrick void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
116e5dd7070Spatrick void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
117e5dd7070Spatrick void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
118e5dd7070Spatrick void checkUncheckedReturnValue(CallExpr *CE);
119e5dd7070Spatrick };
120e5dd7070Spatrick } // end anonymous namespace
121e5dd7070Spatrick
122e5dd7070Spatrick //===----------------------------------------------------------------------===//
123e5dd7070Spatrick // AST walking.
124e5dd7070Spatrick //===----------------------------------------------------------------------===//
125e5dd7070Spatrick
VisitChildren(Stmt * S)126e5dd7070Spatrick void WalkAST::VisitChildren(Stmt *S) {
127e5dd7070Spatrick for (Stmt *Child : S->children())
128e5dd7070Spatrick if (Child)
129e5dd7070Spatrick Visit(Child);
130e5dd7070Spatrick }
131e5dd7070Spatrick
VisitCallExpr(CallExpr * CE)132e5dd7070Spatrick void WalkAST::VisitCallExpr(CallExpr *CE) {
133e5dd7070Spatrick // Get the callee.
134e5dd7070Spatrick const FunctionDecl *FD = CE->getDirectCallee();
135e5dd7070Spatrick
136e5dd7070Spatrick if (!FD)
137e5dd7070Spatrick return;
138e5dd7070Spatrick
139e5dd7070Spatrick // Get the name of the callee. If it's a builtin, strip off the prefix.
140e5dd7070Spatrick IdentifierInfo *II = FD->getIdentifier();
141e5dd7070Spatrick if (!II) // if no identifier, not a simple C function
142e5dd7070Spatrick return;
143e5dd7070Spatrick StringRef Name = II->getName();
144e5dd7070Spatrick if (Name.startswith("__builtin_"))
145e5dd7070Spatrick Name = Name.substr(10);
146e5dd7070Spatrick
147e5dd7070Spatrick // Set the evaluation function by switching on the callee name.
148e5dd7070Spatrick FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
149e5dd7070Spatrick .Case("bcmp", &WalkAST::checkCall_bcmp)
150e5dd7070Spatrick .Case("bcopy", &WalkAST::checkCall_bcopy)
151e5dd7070Spatrick .Case("bzero", &WalkAST::checkCall_bzero)
152e5dd7070Spatrick .Case("gets", &WalkAST::checkCall_gets)
153e5dd7070Spatrick .Case("getpw", &WalkAST::checkCall_getpw)
154e5dd7070Spatrick .Case("mktemp", &WalkAST::checkCall_mktemp)
155e5dd7070Spatrick .Case("mkstemp", &WalkAST::checkCall_mkstemp)
156e5dd7070Spatrick .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
157e5dd7070Spatrick .Case("mkstemps", &WalkAST::checkCall_mkstemp)
158e5dd7070Spatrick .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
159e5dd7070Spatrick .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
160e5dd7070Spatrick .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
161e5dd7070Spatrick "vscanf", "vwscanf", "vfscanf", "vfwscanf",
162e5dd7070Spatrick &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
163e5dd7070Spatrick .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
164e5dd7070Spatrick "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
165e5dd7070Spatrick &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
166e5dd7070Spatrick .Cases("strncpy", "strncat", "memset",
167e5dd7070Spatrick &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
168e5dd7070Spatrick .Case("drand48", &WalkAST::checkCall_rand)
169e5dd7070Spatrick .Case("erand48", &WalkAST::checkCall_rand)
170e5dd7070Spatrick .Case("jrand48", &WalkAST::checkCall_rand)
171e5dd7070Spatrick .Case("lrand48", &WalkAST::checkCall_rand)
172e5dd7070Spatrick .Case("mrand48", &WalkAST::checkCall_rand)
173e5dd7070Spatrick .Case("nrand48", &WalkAST::checkCall_rand)
174e5dd7070Spatrick .Case("lcong48", &WalkAST::checkCall_rand)
175e5dd7070Spatrick .Case("rand", &WalkAST::checkCall_rand)
176e5dd7070Spatrick .Case("rand_r", &WalkAST::checkCall_rand)
177e5dd7070Spatrick .Case("random", &WalkAST::checkCall_random)
178e5dd7070Spatrick .Case("vfork", &WalkAST::checkCall_vfork)
179e5dd7070Spatrick .Default(nullptr);
180e5dd7070Spatrick
181e5dd7070Spatrick // If the callee isn't defined, it is not of security concern.
182e5dd7070Spatrick // Check and evaluate the call.
183e5dd7070Spatrick if (evalFunction)
184e5dd7070Spatrick (this->*evalFunction)(CE, FD);
185e5dd7070Spatrick
186e5dd7070Spatrick // Recurse and check children.
187e5dd7070Spatrick VisitChildren(CE);
188e5dd7070Spatrick }
189e5dd7070Spatrick
VisitObjCMessageExpr(ObjCMessageExpr * ME)190e5dd7070Spatrick void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
191e5dd7070Spatrick MsgCheck evalFunction =
192e5dd7070Spatrick llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
193e5dd7070Spatrick .Case("decodeValueOfObjCType:at:",
194e5dd7070Spatrick &WalkAST::checkMsg_decodeValueOfObjCType)
195e5dd7070Spatrick .Default(nullptr);
196e5dd7070Spatrick
197e5dd7070Spatrick if (evalFunction)
198e5dd7070Spatrick (this->*evalFunction)(ME);
199e5dd7070Spatrick
200e5dd7070Spatrick // Recurse and check children.
201e5dd7070Spatrick VisitChildren(ME);
202e5dd7070Spatrick }
203e5dd7070Spatrick
VisitCompoundStmt(CompoundStmt * S)204e5dd7070Spatrick void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
205e5dd7070Spatrick for (Stmt *Child : S->children())
206e5dd7070Spatrick if (Child) {
207e5dd7070Spatrick if (CallExpr *CE = dyn_cast<CallExpr>(Child))
208e5dd7070Spatrick checkUncheckedReturnValue(CE);
209e5dd7070Spatrick Visit(Child);
210e5dd7070Spatrick }
211e5dd7070Spatrick }
212e5dd7070Spatrick
VisitForStmt(ForStmt * FS)213e5dd7070Spatrick void WalkAST::VisitForStmt(ForStmt *FS) {
214e5dd7070Spatrick checkLoopConditionForFloat(FS);
215e5dd7070Spatrick
216e5dd7070Spatrick // Recurse and check children.
217e5dd7070Spatrick VisitChildren(FS);
218e5dd7070Spatrick }
219e5dd7070Spatrick
220e5dd7070Spatrick //===----------------------------------------------------------------------===//
221e5dd7070Spatrick // Check: floating point variable used as loop counter.
222e5dd7070Spatrick // Originally: <rdar://problem/6336718>
223e5dd7070Spatrick // Implements: CERT security coding advisory FLP-30.
224e5dd7070Spatrick //===----------------------------------------------------------------------===//
225e5dd7070Spatrick
226e5dd7070Spatrick // Returns either 'x' or 'y', depending on which one of them is incremented
227e5dd7070Spatrick // in 'expr', or nullptr if none of them is incremented.
228e5dd7070Spatrick static const DeclRefExpr*
getIncrementedVar(const Expr * expr,const VarDecl * x,const VarDecl * y)229e5dd7070Spatrick getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
230e5dd7070Spatrick expr = expr->IgnoreParenCasts();
231e5dd7070Spatrick
232e5dd7070Spatrick if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
233e5dd7070Spatrick if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
234e5dd7070Spatrick B->getOpcode() == BO_Comma))
235e5dd7070Spatrick return nullptr;
236e5dd7070Spatrick
237e5dd7070Spatrick if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
238e5dd7070Spatrick return lhs;
239e5dd7070Spatrick
240e5dd7070Spatrick if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
241e5dd7070Spatrick return rhs;
242e5dd7070Spatrick
243e5dd7070Spatrick return nullptr;
244e5dd7070Spatrick }
245e5dd7070Spatrick
246e5dd7070Spatrick if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
247e5dd7070Spatrick const NamedDecl *ND = DR->getDecl();
248e5dd7070Spatrick return ND == x || ND == y ? DR : nullptr;
249e5dd7070Spatrick }
250e5dd7070Spatrick
251e5dd7070Spatrick if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
252e5dd7070Spatrick return U->isIncrementDecrementOp()
253e5dd7070Spatrick ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
254e5dd7070Spatrick
255e5dd7070Spatrick return nullptr;
256e5dd7070Spatrick }
257e5dd7070Spatrick
258e5dd7070Spatrick /// CheckLoopConditionForFloat - This check looks for 'for' statements that
259e5dd7070Spatrick /// use a floating point variable as a loop counter.
260e5dd7070Spatrick /// CERT: FLP30-C, FLP30-CPP.
261e5dd7070Spatrick ///
checkLoopConditionForFloat(const ForStmt * FS)262e5dd7070Spatrick void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
263e5dd7070Spatrick if (!filter.check_FloatLoopCounter)
264e5dd7070Spatrick return;
265e5dd7070Spatrick
266e5dd7070Spatrick // Does the loop have a condition?
267e5dd7070Spatrick const Expr *condition = FS->getCond();
268e5dd7070Spatrick
269e5dd7070Spatrick if (!condition)
270e5dd7070Spatrick return;
271e5dd7070Spatrick
272e5dd7070Spatrick // Does the loop have an increment?
273e5dd7070Spatrick const Expr *increment = FS->getInc();
274e5dd7070Spatrick
275e5dd7070Spatrick if (!increment)
276e5dd7070Spatrick return;
277e5dd7070Spatrick
278e5dd7070Spatrick // Strip away '()' and casts.
279e5dd7070Spatrick condition = condition->IgnoreParenCasts();
280e5dd7070Spatrick increment = increment->IgnoreParenCasts();
281e5dd7070Spatrick
282e5dd7070Spatrick // Is the loop condition a comparison?
283e5dd7070Spatrick const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
284e5dd7070Spatrick
285e5dd7070Spatrick if (!B)
286e5dd7070Spatrick return;
287e5dd7070Spatrick
288e5dd7070Spatrick // Is this a comparison?
289e5dd7070Spatrick if (!(B->isRelationalOp() || B->isEqualityOp()))
290e5dd7070Spatrick return;
291e5dd7070Spatrick
292e5dd7070Spatrick // Are we comparing variables?
293e5dd7070Spatrick const DeclRefExpr *drLHS =
294e5dd7070Spatrick dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
295e5dd7070Spatrick const DeclRefExpr *drRHS =
296e5dd7070Spatrick dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
297e5dd7070Spatrick
298e5dd7070Spatrick // Does at least one of the variables have a floating point type?
299e5dd7070Spatrick drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
300e5dd7070Spatrick drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
301e5dd7070Spatrick
302e5dd7070Spatrick if (!drLHS && !drRHS)
303e5dd7070Spatrick return;
304e5dd7070Spatrick
305e5dd7070Spatrick const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
306e5dd7070Spatrick const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
307e5dd7070Spatrick
308e5dd7070Spatrick if (!vdLHS && !vdRHS)
309e5dd7070Spatrick return;
310e5dd7070Spatrick
311e5dd7070Spatrick // Does either variable appear in increment?
312e5dd7070Spatrick const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
313e5dd7070Spatrick if (!drInc)
314e5dd7070Spatrick return;
315e5dd7070Spatrick
316e5dd7070Spatrick const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
317e5dd7070Spatrick assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
318e5dd7070Spatrick
319e5dd7070Spatrick // Emit the error. First figure out which DeclRefExpr in the condition
320e5dd7070Spatrick // referenced the compared variable.
321e5dd7070Spatrick const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
322e5dd7070Spatrick
323e5dd7070Spatrick SmallVector<SourceRange, 2> ranges;
324e5dd7070Spatrick SmallString<256> sbuf;
325e5dd7070Spatrick llvm::raw_svector_ostream os(sbuf);
326e5dd7070Spatrick
327e5dd7070Spatrick os << "Variable '" << drCond->getDecl()->getName()
328*12c85518Srobert << "' with floating point type '" << drCond->getType()
329e5dd7070Spatrick << "' should not be used as a loop counter";
330e5dd7070Spatrick
331e5dd7070Spatrick ranges.push_back(drCond->getSourceRange());
332e5dd7070Spatrick ranges.push_back(drInc->getSourceRange());
333e5dd7070Spatrick
334e5dd7070Spatrick const char *bugType = "Floating point variable used as loop counter";
335e5dd7070Spatrick
336e5dd7070Spatrick PathDiagnosticLocation FSLoc =
337e5dd7070Spatrick PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
338e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
339e5dd7070Spatrick bugType, "Security", os.str(),
340e5dd7070Spatrick FSLoc, ranges);
341e5dd7070Spatrick }
342e5dd7070Spatrick
343e5dd7070Spatrick //===----------------------------------------------------------------------===//
344e5dd7070Spatrick // Check: Any use of bcmp.
345e5dd7070Spatrick // CWE-477: Use of Obsolete Functions
346e5dd7070Spatrick // bcmp was deprecated in POSIX.1-2008
347e5dd7070Spatrick //===----------------------------------------------------------------------===//
348e5dd7070Spatrick
checkCall_bcmp(const CallExpr * CE,const FunctionDecl * FD)349e5dd7070Spatrick void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
350e5dd7070Spatrick if (!filter.check_bcmp)
351e5dd7070Spatrick return;
352e5dd7070Spatrick
353e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
354e5dd7070Spatrick if (!FPT)
355e5dd7070Spatrick return;
356e5dd7070Spatrick
357e5dd7070Spatrick // Verify that the function takes three arguments.
358e5dd7070Spatrick if (FPT->getNumParams() != 3)
359e5dd7070Spatrick return;
360e5dd7070Spatrick
361e5dd7070Spatrick for (int i = 0; i < 2; i++) {
362e5dd7070Spatrick // Verify the first and second argument type is void*.
363e5dd7070Spatrick const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
364e5dd7070Spatrick if (!PT)
365e5dd7070Spatrick return;
366e5dd7070Spatrick
367e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
368e5dd7070Spatrick return;
369e5dd7070Spatrick }
370e5dd7070Spatrick
371e5dd7070Spatrick // Verify the third argument type is integer.
372e5dd7070Spatrick if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
373e5dd7070Spatrick return;
374e5dd7070Spatrick
375e5dd7070Spatrick // Issue a warning.
376e5dd7070Spatrick PathDiagnosticLocation CELoc =
377e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
378e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
379e5dd7070Spatrick "Use of deprecated function in call to 'bcmp()'",
380e5dd7070Spatrick "Security",
381e5dd7070Spatrick "The bcmp() function is obsoleted by memcmp().",
382e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
383e5dd7070Spatrick }
384e5dd7070Spatrick
385e5dd7070Spatrick //===----------------------------------------------------------------------===//
386e5dd7070Spatrick // Check: Any use of bcopy.
387e5dd7070Spatrick // CWE-477: Use of Obsolete Functions
388e5dd7070Spatrick // bcopy was deprecated in POSIX.1-2008
389e5dd7070Spatrick //===----------------------------------------------------------------------===//
390e5dd7070Spatrick
checkCall_bcopy(const CallExpr * CE,const FunctionDecl * FD)391e5dd7070Spatrick void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
392e5dd7070Spatrick if (!filter.check_bcopy)
393e5dd7070Spatrick return;
394e5dd7070Spatrick
395e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
396e5dd7070Spatrick if (!FPT)
397e5dd7070Spatrick return;
398e5dd7070Spatrick
399e5dd7070Spatrick // Verify that the function takes three arguments.
400e5dd7070Spatrick if (FPT->getNumParams() != 3)
401e5dd7070Spatrick return;
402e5dd7070Spatrick
403e5dd7070Spatrick for (int i = 0; i < 2; i++) {
404e5dd7070Spatrick // Verify the first and second argument type is void*.
405e5dd7070Spatrick const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
406e5dd7070Spatrick if (!PT)
407e5dd7070Spatrick return;
408e5dd7070Spatrick
409e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
410e5dd7070Spatrick return;
411e5dd7070Spatrick }
412e5dd7070Spatrick
413e5dd7070Spatrick // Verify the third argument type is integer.
414e5dd7070Spatrick if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
415e5dd7070Spatrick return;
416e5dd7070Spatrick
417e5dd7070Spatrick // Issue a warning.
418e5dd7070Spatrick PathDiagnosticLocation CELoc =
419e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
420e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
421e5dd7070Spatrick "Use of deprecated function in call to 'bcopy()'",
422e5dd7070Spatrick "Security",
423e5dd7070Spatrick "The bcopy() function is obsoleted by memcpy() "
424e5dd7070Spatrick "or memmove().",
425e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
426e5dd7070Spatrick }
427e5dd7070Spatrick
428e5dd7070Spatrick //===----------------------------------------------------------------------===//
429e5dd7070Spatrick // Check: Any use of bzero.
430e5dd7070Spatrick // CWE-477: Use of Obsolete Functions
431e5dd7070Spatrick // bzero was deprecated in POSIX.1-2008
432e5dd7070Spatrick //===----------------------------------------------------------------------===//
433e5dd7070Spatrick
checkCall_bzero(const CallExpr * CE,const FunctionDecl * FD)434e5dd7070Spatrick void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
435e5dd7070Spatrick if (!filter.check_bzero)
436e5dd7070Spatrick return;
437e5dd7070Spatrick
438e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
439e5dd7070Spatrick if (!FPT)
440e5dd7070Spatrick return;
441e5dd7070Spatrick
442e5dd7070Spatrick // Verify that the function takes two arguments.
443e5dd7070Spatrick if (FPT->getNumParams() != 2)
444e5dd7070Spatrick return;
445e5dd7070Spatrick
446e5dd7070Spatrick // Verify the first argument type is void*.
447e5dd7070Spatrick const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
448e5dd7070Spatrick if (!PT)
449e5dd7070Spatrick return;
450e5dd7070Spatrick
451e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
452e5dd7070Spatrick return;
453e5dd7070Spatrick
454e5dd7070Spatrick // Verify the second argument type is integer.
455e5dd7070Spatrick if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
456e5dd7070Spatrick return;
457e5dd7070Spatrick
458e5dd7070Spatrick // Issue a warning.
459e5dd7070Spatrick PathDiagnosticLocation CELoc =
460e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
461e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
462e5dd7070Spatrick "Use of deprecated function in call to 'bzero()'",
463e5dd7070Spatrick "Security",
464e5dd7070Spatrick "The bzero() function is obsoleted by memset().",
465e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
466e5dd7070Spatrick }
467e5dd7070Spatrick
468e5dd7070Spatrick
469e5dd7070Spatrick //===----------------------------------------------------------------------===//
470e5dd7070Spatrick // Check: Any use of 'gets' is insecure.
471e5dd7070Spatrick // Originally: <rdar://problem/6335715>
472e5dd7070Spatrick // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
473e5dd7070Spatrick // CWE-242: Use of Inherently Dangerous Function
474e5dd7070Spatrick //===----------------------------------------------------------------------===//
475e5dd7070Spatrick
checkCall_gets(const CallExpr * CE,const FunctionDecl * FD)476e5dd7070Spatrick void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
477e5dd7070Spatrick if (!filter.check_gets)
478e5dd7070Spatrick return;
479e5dd7070Spatrick
480e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
481e5dd7070Spatrick if (!FPT)
482e5dd7070Spatrick return;
483e5dd7070Spatrick
484e5dd7070Spatrick // Verify that the function takes a single argument.
485e5dd7070Spatrick if (FPT->getNumParams() != 1)
486e5dd7070Spatrick return;
487e5dd7070Spatrick
488e5dd7070Spatrick // Is the argument a 'char*'?
489e5dd7070Spatrick const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
490e5dd7070Spatrick if (!PT)
491e5dd7070Spatrick return;
492e5dd7070Spatrick
493e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
494e5dd7070Spatrick return;
495e5dd7070Spatrick
496e5dd7070Spatrick // Issue a warning.
497e5dd7070Spatrick PathDiagnosticLocation CELoc =
498e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
499e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
500e5dd7070Spatrick "Potential buffer overflow in call to 'gets'",
501e5dd7070Spatrick "Security",
502e5dd7070Spatrick "Call to function 'gets' is extremely insecure as it can "
503e5dd7070Spatrick "always result in a buffer overflow",
504e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
505e5dd7070Spatrick }
506e5dd7070Spatrick
507e5dd7070Spatrick //===----------------------------------------------------------------------===//
508e5dd7070Spatrick // Check: Any use of 'getpwd' is insecure.
509e5dd7070Spatrick // CWE-477: Use of Obsolete Functions
510e5dd7070Spatrick //===----------------------------------------------------------------------===//
511e5dd7070Spatrick
checkCall_getpw(const CallExpr * CE,const FunctionDecl * FD)512e5dd7070Spatrick void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
513e5dd7070Spatrick if (!filter.check_getpw)
514e5dd7070Spatrick return;
515e5dd7070Spatrick
516e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
517e5dd7070Spatrick if (!FPT)
518e5dd7070Spatrick return;
519e5dd7070Spatrick
520e5dd7070Spatrick // Verify that the function takes two arguments.
521e5dd7070Spatrick if (FPT->getNumParams() != 2)
522e5dd7070Spatrick return;
523e5dd7070Spatrick
524e5dd7070Spatrick // Verify the first argument type is integer.
525e5dd7070Spatrick if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
526e5dd7070Spatrick return;
527e5dd7070Spatrick
528e5dd7070Spatrick // Verify the second argument type is char*.
529e5dd7070Spatrick const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
530e5dd7070Spatrick if (!PT)
531e5dd7070Spatrick return;
532e5dd7070Spatrick
533e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
534e5dd7070Spatrick return;
535e5dd7070Spatrick
536e5dd7070Spatrick // Issue a warning.
537e5dd7070Spatrick PathDiagnosticLocation CELoc =
538e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
539e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
540e5dd7070Spatrick "Potential buffer overflow in call to 'getpw'",
541e5dd7070Spatrick "Security",
542e5dd7070Spatrick "The getpw() function is dangerous as it may overflow the "
543e5dd7070Spatrick "provided buffer. It is obsoleted by getpwuid().",
544e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
545e5dd7070Spatrick }
546e5dd7070Spatrick
547e5dd7070Spatrick //===----------------------------------------------------------------------===//
548e5dd7070Spatrick // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
549e5dd7070Spatrick // CWE-377: Insecure Temporary File
550e5dd7070Spatrick //===----------------------------------------------------------------------===//
551e5dd7070Spatrick
checkCall_mktemp(const CallExpr * CE,const FunctionDecl * FD)552e5dd7070Spatrick void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
553e5dd7070Spatrick if (!filter.check_mktemp) {
554e5dd7070Spatrick // Fall back to the security check of looking for enough 'X's in the
555e5dd7070Spatrick // format string, since that is a less severe warning.
556e5dd7070Spatrick checkCall_mkstemp(CE, FD);
557e5dd7070Spatrick return;
558e5dd7070Spatrick }
559e5dd7070Spatrick
560e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
561e5dd7070Spatrick if(!FPT)
562e5dd7070Spatrick return;
563e5dd7070Spatrick
564e5dd7070Spatrick // Verify that the function takes a single argument.
565e5dd7070Spatrick if (FPT->getNumParams() != 1)
566e5dd7070Spatrick return;
567e5dd7070Spatrick
568e5dd7070Spatrick // Verify that the argument is Pointer Type.
569e5dd7070Spatrick const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
570e5dd7070Spatrick if (!PT)
571e5dd7070Spatrick return;
572e5dd7070Spatrick
573e5dd7070Spatrick // Verify that the argument is a 'char*'.
574e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
575e5dd7070Spatrick return;
576e5dd7070Spatrick
577e5dd7070Spatrick // Issue a warning.
578e5dd7070Spatrick PathDiagnosticLocation CELoc =
579e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
580e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
581e5dd7070Spatrick "Potential insecure temporary file in call 'mktemp'",
582e5dd7070Spatrick "Security",
583e5dd7070Spatrick "Call to function 'mktemp' is insecure as it always "
584e5dd7070Spatrick "creates or uses insecure temporary file. Use 'mkstemp' "
585e5dd7070Spatrick "instead",
586e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
587e5dd7070Spatrick }
588e5dd7070Spatrick
589e5dd7070Spatrick //===----------------------------------------------------------------------===//
590e5dd7070Spatrick // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
591e5dd7070Spatrick //===----------------------------------------------------------------------===//
592e5dd7070Spatrick
checkCall_mkstemp(const CallExpr * CE,const FunctionDecl * FD)593e5dd7070Spatrick void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
594e5dd7070Spatrick if (!filter.check_mkstemp)
595e5dd7070Spatrick return;
596e5dd7070Spatrick
597e5dd7070Spatrick StringRef Name = FD->getIdentifier()->getName();
598e5dd7070Spatrick std::pair<signed, signed> ArgSuffix =
599e5dd7070Spatrick llvm::StringSwitch<std::pair<signed, signed> >(Name)
600e5dd7070Spatrick .Case("mktemp", std::make_pair(0,-1))
601e5dd7070Spatrick .Case("mkstemp", std::make_pair(0,-1))
602e5dd7070Spatrick .Case("mkdtemp", std::make_pair(0,-1))
603e5dd7070Spatrick .Case("mkstemps", std::make_pair(0,1))
604e5dd7070Spatrick .Default(std::make_pair(-1, -1));
605e5dd7070Spatrick
606e5dd7070Spatrick assert(ArgSuffix.first >= 0 && "Unsupported function");
607e5dd7070Spatrick
608e5dd7070Spatrick // Check if the number of arguments is consistent with out expectations.
609e5dd7070Spatrick unsigned numArgs = CE->getNumArgs();
610e5dd7070Spatrick if ((signed) numArgs <= ArgSuffix.first)
611e5dd7070Spatrick return;
612e5dd7070Spatrick
613e5dd7070Spatrick const StringLiteral *strArg =
614e5dd7070Spatrick dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
615e5dd7070Spatrick ->IgnoreParenImpCasts());
616e5dd7070Spatrick
617e5dd7070Spatrick // Currently we only handle string literals. It is possible to do better,
618e5dd7070Spatrick // either by looking at references to const variables, or by doing real
619e5dd7070Spatrick // flow analysis.
620e5dd7070Spatrick if (!strArg || strArg->getCharByteWidth() != 1)
621e5dd7070Spatrick return;
622e5dd7070Spatrick
623e5dd7070Spatrick // Count the number of X's, taking into account a possible cutoff suffix.
624e5dd7070Spatrick StringRef str = strArg->getString();
625e5dd7070Spatrick unsigned numX = 0;
626e5dd7070Spatrick unsigned n = str.size();
627e5dd7070Spatrick
628e5dd7070Spatrick // Take into account the suffix.
629e5dd7070Spatrick unsigned suffix = 0;
630e5dd7070Spatrick if (ArgSuffix.second >= 0) {
631e5dd7070Spatrick const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
632e5dd7070Spatrick Expr::EvalResult EVResult;
633e5dd7070Spatrick if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
634e5dd7070Spatrick return;
635e5dd7070Spatrick llvm::APSInt Result = EVResult.Val.getInt();
636e5dd7070Spatrick // FIXME: Issue a warning.
637e5dd7070Spatrick if (Result.isNegative())
638e5dd7070Spatrick return;
639e5dd7070Spatrick suffix = (unsigned) Result.getZExtValue();
640e5dd7070Spatrick n = (n > suffix) ? n - suffix : 0;
641e5dd7070Spatrick }
642e5dd7070Spatrick
643e5dd7070Spatrick for (unsigned i = 0; i < n; ++i)
644e5dd7070Spatrick if (str[i] == 'X') ++numX;
645e5dd7070Spatrick
646e5dd7070Spatrick if (numX >= 6)
647e5dd7070Spatrick return;
648e5dd7070Spatrick
649e5dd7070Spatrick // Issue a warning.
650e5dd7070Spatrick PathDiagnosticLocation CELoc =
651e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
652e5dd7070Spatrick SmallString<512> buf;
653e5dd7070Spatrick llvm::raw_svector_ostream out(buf);
654e5dd7070Spatrick out << "Call to '" << Name << "' should have at least 6 'X's in the"
655e5dd7070Spatrick " format string to be secure (" << numX << " 'X'";
656e5dd7070Spatrick if (numX != 1)
657e5dd7070Spatrick out << 's';
658e5dd7070Spatrick out << " seen";
659e5dd7070Spatrick if (suffix) {
660e5dd7070Spatrick out << ", " << suffix << " character";
661e5dd7070Spatrick if (suffix > 1)
662e5dd7070Spatrick out << 's';
663e5dd7070Spatrick out << " used as a suffix";
664e5dd7070Spatrick }
665e5dd7070Spatrick out << ')';
666e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
667e5dd7070Spatrick "Insecure temporary file creation", "Security",
668e5dd7070Spatrick out.str(), CELoc, strArg->getSourceRange());
669e5dd7070Spatrick }
670e5dd7070Spatrick
671e5dd7070Spatrick //===----------------------------------------------------------------------===//
672e5dd7070Spatrick // Check: Any use of 'strcpy' is insecure.
673e5dd7070Spatrick //
674e5dd7070Spatrick // CWE-119: Improper Restriction of Operations within
675e5dd7070Spatrick // the Bounds of a Memory Buffer
676e5dd7070Spatrick //===----------------------------------------------------------------------===//
677e5dd7070Spatrick
checkCall_strcpy(const CallExpr * CE,const FunctionDecl * FD)678e5dd7070Spatrick void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
679e5dd7070Spatrick if (!filter.check_strcpy)
680e5dd7070Spatrick return;
681e5dd7070Spatrick
682e5dd7070Spatrick if (!checkCall_strCommon(CE, FD))
683e5dd7070Spatrick return;
684e5dd7070Spatrick
685e5dd7070Spatrick const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
686e5dd7070Spatrick *Source = CE->getArg(1)->IgnoreImpCasts();
687e5dd7070Spatrick
688e5dd7070Spatrick if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
689e5dd7070Spatrick uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
690e5dd7070Spatrick if (const auto *String = dyn_cast<StringLiteral>(Source)) {
691e5dd7070Spatrick if (ArraySize >= String->getLength() + 1)
692e5dd7070Spatrick return;
693e5dd7070Spatrick }
694e5dd7070Spatrick }
695e5dd7070Spatrick
696e5dd7070Spatrick // Issue a warning.
697e5dd7070Spatrick PathDiagnosticLocation CELoc =
698e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
699e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
700e5dd7070Spatrick "Potential insecure memory buffer bounds restriction in "
701e5dd7070Spatrick "call 'strcpy'",
702e5dd7070Spatrick "Security",
703e5dd7070Spatrick "Call to function 'strcpy' is insecure as it does not "
704e5dd7070Spatrick "provide bounding of the memory buffer. Replace "
705e5dd7070Spatrick "unbounded copy functions with analogous functions that "
706e5dd7070Spatrick "support length arguments such as 'strlcpy'. CWE-119.",
707e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
708e5dd7070Spatrick }
709e5dd7070Spatrick
710e5dd7070Spatrick //===----------------------------------------------------------------------===//
711e5dd7070Spatrick // Check: Any use of 'strcat' is insecure.
712e5dd7070Spatrick //
713e5dd7070Spatrick // CWE-119: Improper Restriction of Operations within
714e5dd7070Spatrick // the Bounds of a Memory Buffer
715e5dd7070Spatrick //===----------------------------------------------------------------------===//
716e5dd7070Spatrick
checkCall_strcat(const CallExpr * CE,const FunctionDecl * FD)717e5dd7070Spatrick void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
718e5dd7070Spatrick if (!filter.check_strcpy)
719e5dd7070Spatrick return;
720e5dd7070Spatrick
721e5dd7070Spatrick if (!checkCall_strCommon(CE, FD))
722e5dd7070Spatrick return;
723e5dd7070Spatrick
724e5dd7070Spatrick // Issue a warning.
725e5dd7070Spatrick PathDiagnosticLocation CELoc =
726e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
727e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
728e5dd7070Spatrick "Potential insecure memory buffer bounds restriction in "
729e5dd7070Spatrick "call 'strcat'",
730e5dd7070Spatrick "Security",
731e5dd7070Spatrick "Call to function 'strcat' is insecure as it does not "
732e5dd7070Spatrick "provide bounding of the memory buffer. Replace "
733e5dd7070Spatrick "unbounded copy functions with analogous functions that "
734e5dd7070Spatrick "support length arguments such as 'strlcat'. CWE-119.",
735e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
736e5dd7070Spatrick }
737e5dd7070Spatrick
738e5dd7070Spatrick //===----------------------------------------------------------------------===//
739e5dd7070Spatrick // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
740e5dd7070Spatrick // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
741e5dd7070Spatrick // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
742e5dd7070Spatrick // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
743e5dd7070Spatrick // is deprecated since C11.
744e5dd7070Spatrick //
745e5dd7070Spatrick // Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
746e5dd7070Spatrick // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
747e5dd7070Spatrick // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
748e5dd7070Spatrick // is insecure.
749e5dd7070Spatrick //
750e5dd7070Spatrick // CWE-119: Improper Restriction of Operations within
751e5dd7070Spatrick // the Bounds of a Memory Buffer
752e5dd7070Spatrick //===----------------------------------------------------------------------===//
753e5dd7070Spatrick
checkDeprecatedOrUnsafeBufferHandling(const CallExpr * CE,const FunctionDecl * FD)754e5dd7070Spatrick void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
755e5dd7070Spatrick const FunctionDecl *FD) {
756e5dd7070Spatrick if (!filter.check_DeprecatedOrUnsafeBufferHandling)
757e5dd7070Spatrick return;
758e5dd7070Spatrick
759e5dd7070Spatrick if (!BR.getContext().getLangOpts().C11)
760e5dd7070Spatrick return;
761e5dd7070Spatrick
762e5dd7070Spatrick // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
763e5dd7070Spatrick // restrictions).
764e5dd7070Spatrick enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
765e5dd7070Spatrick
766e5dd7070Spatrick StringRef Name = FD->getIdentifier()->getName();
767e5dd7070Spatrick if (Name.startswith("__builtin_"))
768e5dd7070Spatrick Name = Name.substr(10);
769e5dd7070Spatrick
770e5dd7070Spatrick int ArgIndex =
771e5dd7070Spatrick llvm::StringSwitch<int>(Name)
772e5dd7070Spatrick .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
773e5dd7070Spatrick .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
774e5dd7070Spatrick "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
775e5dd7070Spatrick .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
776e5dd7070Spatrick "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
777e5dd7070Spatrick .Default(UNKNOWN_CALL);
778e5dd7070Spatrick
779e5dd7070Spatrick assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
780e5dd7070Spatrick bool BoundsProvided = ArgIndex == DEPR_ONLY;
781e5dd7070Spatrick
782e5dd7070Spatrick if (!BoundsProvided) {
783e5dd7070Spatrick // Currently we only handle (not wide) string literals. It is possible to do
784e5dd7070Spatrick // better, either by looking at references to const variables, or by doing
785e5dd7070Spatrick // real flow analysis.
786e5dd7070Spatrick auto FormatString =
787e5dd7070Spatrick dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
788*12c85518Srobert if (FormatString && !FormatString->getString().contains("%s") &&
789*12c85518Srobert !FormatString->getString().contains("%["))
790e5dd7070Spatrick BoundsProvided = true;
791e5dd7070Spatrick }
792e5dd7070Spatrick
793e5dd7070Spatrick SmallString<128> Buf1;
794e5dd7070Spatrick SmallString<512> Buf2;
795e5dd7070Spatrick llvm::raw_svector_ostream Out1(Buf1);
796e5dd7070Spatrick llvm::raw_svector_ostream Out2(Buf2);
797e5dd7070Spatrick
798e5dd7070Spatrick Out1 << "Potential insecure memory buffer bounds restriction in call '"
799e5dd7070Spatrick << Name << "'";
800e5dd7070Spatrick Out2 << "Call to function '" << Name
801e5dd7070Spatrick << "' is insecure as it does not provide ";
802e5dd7070Spatrick
803e5dd7070Spatrick if (!BoundsProvided) {
804e5dd7070Spatrick Out2 << "bounding of the memory buffer or ";
805e5dd7070Spatrick }
806e5dd7070Spatrick
807e5dd7070Spatrick Out2 << "security checks introduced "
808e5dd7070Spatrick "in the C11 standard. Replace with analogous functions that "
809e5dd7070Spatrick "support length arguments or provides boundary checks such as '"
810e5dd7070Spatrick << Name << "_s' in case of C11";
811e5dd7070Spatrick
812e5dd7070Spatrick PathDiagnosticLocation CELoc =
813e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
814e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(),
815e5dd7070Spatrick filter.checkName_DeprecatedOrUnsafeBufferHandling,
816e5dd7070Spatrick Out1.str(), "Security", Out2.str(), CELoc,
817e5dd7070Spatrick CE->getCallee()->getSourceRange());
818e5dd7070Spatrick }
819e5dd7070Spatrick
820e5dd7070Spatrick //===----------------------------------------------------------------------===//
821e5dd7070Spatrick // Common check for str* functions with no bounds parameters.
822e5dd7070Spatrick //===----------------------------------------------------------------------===//
823e5dd7070Spatrick
checkCall_strCommon(const CallExpr * CE,const FunctionDecl * FD)824e5dd7070Spatrick bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
825e5dd7070Spatrick const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
826e5dd7070Spatrick if (!FPT)
827e5dd7070Spatrick return false;
828e5dd7070Spatrick
829e5dd7070Spatrick // Verify the function takes two arguments, three in the _chk version.
830e5dd7070Spatrick int numArgs = FPT->getNumParams();
831e5dd7070Spatrick if (numArgs != 2 && numArgs != 3)
832e5dd7070Spatrick return false;
833e5dd7070Spatrick
834e5dd7070Spatrick // Verify the type for both arguments.
835e5dd7070Spatrick for (int i = 0; i < 2; i++) {
836e5dd7070Spatrick // Verify that the arguments are pointers.
837e5dd7070Spatrick const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
838e5dd7070Spatrick if (!PT)
839e5dd7070Spatrick return false;
840e5dd7070Spatrick
841e5dd7070Spatrick // Verify that the argument is a 'char*'.
842e5dd7070Spatrick if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
843e5dd7070Spatrick return false;
844e5dd7070Spatrick }
845e5dd7070Spatrick
846e5dd7070Spatrick return true;
847e5dd7070Spatrick }
848e5dd7070Spatrick
849e5dd7070Spatrick //===----------------------------------------------------------------------===//
850e5dd7070Spatrick // Check: Linear congruent random number generators should not be used
851e5dd7070Spatrick // Originally: <rdar://problem/63371000>
852e5dd7070Spatrick // CWE-338: Use of cryptographically weak prng
853e5dd7070Spatrick //===----------------------------------------------------------------------===//
854e5dd7070Spatrick
checkCall_rand(const CallExpr * CE,const FunctionDecl * FD)855e5dd7070Spatrick void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
856e5dd7070Spatrick if (!filter.check_rand || !CheckRand)
857e5dd7070Spatrick return;
858e5dd7070Spatrick
859e5dd7070Spatrick const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
860e5dd7070Spatrick if (!FTP)
861e5dd7070Spatrick return;
862e5dd7070Spatrick
863e5dd7070Spatrick if (FTP->getNumParams() == 1) {
864e5dd7070Spatrick // Is the argument an 'unsigned short *'?
865e5dd7070Spatrick // (Actually any integer type is allowed.)
866e5dd7070Spatrick const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
867e5dd7070Spatrick if (!PT)
868e5dd7070Spatrick return;
869e5dd7070Spatrick
870e5dd7070Spatrick if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
871e5dd7070Spatrick return;
872e5dd7070Spatrick } else if (FTP->getNumParams() != 0)
873e5dd7070Spatrick return;
874e5dd7070Spatrick
875e5dd7070Spatrick // Issue a warning.
876e5dd7070Spatrick SmallString<256> buf1;
877e5dd7070Spatrick llvm::raw_svector_ostream os1(buf1);
878e5dd7070Spatrick os1 << '\'' << *FD << "' is a poor random number generator";
879e5dd7070Spatrick
880e5dd7070Spatrick SmallString<256> buf2;
881e5dd7070Spatrick llvm::raw_svector_ostream os2(buf2);
882e5dd7070Spatrick os2 << "Function '" << *FD
883e5dd7070Spatrick << "' is obsolete because it implements a poor random number generator."
884e5dd7070Spatrick << " Use 'arc4random' instead";
885e5dd7070Spatrick
886e5dd7070Spatrick PathDiagnosticLocation CELoc =
887e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
888e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
889e5dd7070Spatrick "Security", os2.str(), CELoc,
890e5dd7070Spatrick CE->getCallee()->getSourceRange());
891e5dd7070Spatrick }
892e5dd7070Spatrick
893e5dd7070Spatrick //===----------------------------------------------------------------------===//
894e5dd7070Spatrick // Check: 'random' should not be used
895e5dd7070Spatrick // Originally: <rdar://problem/63371000>
896e5dd7070Spatrick //===----------------------------------------------------------------------===//
897e5dd7070Spatrick
checkCall_random(const CallExpr * CE,const FunctionDecl * FD)898e5dd7070Spatrick void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
899e5dd7070Spatrick if (!CheckRand || !filter.check_rand)
900e5dd7070Spatrick return;
901e5dd7070Spatrick
902e5dd7070Spatrick const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
903e5dd7070Spatrick if (!FTP)
904e5dd7070Spatrick return;
905e5dd7070Spatrick
906e5dd7070Spatrick // Verify that the function takes no argument.
907e5dd7070Spatrick if (FTP->getNumParams() != 0)
908e5dd7070Spatrick return;
909e5dd7070Spatrick
910e5dd7070Spatrick // Issue a warning.
911e5dd7070Spatrick PathDiagnosticLocation CELoc =
912e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
913e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
914e5dd7070Spatrick "'random' is not a secure random number generator",
915e5dd7070Spatrick "Security",
916e5dd7070Spatrick "The 'random' function produces a sequence of values that "
917e5dd7070Spatrick "an adversary may be able to predict. Use 'arc4random' "
918e5dd7070Spatrick "instead", CELoc, CE->getCallee()->getSourceRange());
919e5dd7070Spatrick }
920e5dd7070Spatrick
921e5dd7070Spatrick //===----------------------------------------------------------------------===//
922e5dd7070Spatrick // Check: 'vfork' should not be used.
923e5dd7070Spatrick // POS33-C: Do not use vfork().
924e5dd7070Spatrick //===----------------------------------------------------------------------===//
925e5dd7070Spatrick
checkCall_vfork(const CallExpr * CE,const FunctionDecl * FD)926e5dd7070Spatrick void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
927e5dd7070Spatrick if (!filter.check_vfork)
928e5dd7070Spatrick return;
929e5dd7070Spatrick
930e5dd7070Spatrick // All calls to vfork() are insecure, issue a warning.
931e5dd7070Spatrick PathDiagnosticLocation CELoc =
932e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
933e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
934e5dd7070Spatrick "Potential insecure implementation-specific behavior in "
935e5dd7070Spatrick "call 'vfork'",
936e5dd7070Spatrick "Security",
937e5dd7070Spatrick "Call to function 'vfork' is insecure as it can lead to "
938e5dd7070Spatrick "denial of service situations in the parent process. "
939e5dd7070Spatrick "Replace calls to vfork with calls to the safer "
940e5dd7070Spatrick "'posix_spawn' function",
941e5dd7070Spatrick CELoc, CE->getCallee()->getSourceRange());
942e5dd7070Spatrick }
943e5dd7070Spatrick
944e5dd7070Spatrick //===----------------------------------------------------------------------===//
945e5dd7070Spatrick // Check: '-decodeValueOfObjCType:at:' should not be used.
946e5dd7070Spatrick // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
947e5dd7070Spatrick // likelihood of buffer overflows.
948e5dd7070Spatrick //===----------------------------------------------------------------------===//
949e5dd7070Spatrick
checkMsg_decodeValueOfObjCType(const ObjCMessageExpr * ME)950e5dd7070Spatrick void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
951e5dd7070Spatrick if (!filter.check_decodeValueOfObjCType)
952e5dd7070Spatrick return;
953e5dd7070Spatrick
954e5dd7070Spatrick // Check availability of the secure alternative:
955e5dd7070Spatrick // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
956e5dd7070Spatrick // FIXME: We probably shouldn't register the check if it's not available.
957e5dd7070Spatrick const TargetInfo &TI = AC->getASTContext().getTargetInfo();
958e5dd7070Spatrick const llvm::Triple &T = TI.getTriple();
959e5dd7070Spatrick const VersionTuple &VT = TI.getPlatformMinVersion();
960e5dd7070Spatrick switch (T.getOS()) {
961e5dd7070Spatrick case llvm::Triple::IOS:
962e5dd7070Spatrick if (VT < VersionTuple(11, 0))
963e5dd7070Spatrick return;
964e5dd7070Spatrick break;
965e5dd7070Spatrick case llvm::Triple::MacOSX:
966e5dd7070Spatrick if (VT < VersionTuple(10, 13))
967e5dd7070Spatrick return;
968e5dd7070Spatrick break;
969e5dd7070Spatrick case llvm::Triple::WatchOS:
970e5dd7070Spatrick if (VT < VersionTuple(4, 0))
971e5dd7070Spatrick return;
972e5dd7070Spatrick break;
973e5dd7070Spatrick case llvm::Triple::TvOS:
974e5dd7070Spatrick if (VT < VersionTuple(11, 0))
975e5dd7070Spatrick return;
976e5dd7070Spatrick break;
977e5dd7070Spatrick default:
978e5dd7070Spatrick return;
979e5dd7070Spatrick }
980e5dd7070Spatrick
981e5dd7070Spatrick PathDiagnosticLocation MELoc =
982e5dd7070Spatrick PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
983e5dd7070Spatrick BR.EmitBasicReport(
984e5dd7070Spatrick AC->getDecl(), filter.checkName_decodeValueOfObjCType,
985e5dd7070Spatrick "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
986e5dd7070Spatrick "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
987e5dd7070Spatrick "as it can lead to potential buffer overflows. Use the safer "
988e5dd7070Spatrick "'-decodeValueOfObjCType:at:size:' method.",
989e5dd7070Spatrick MELoc, ME->getSourceRange());
990e5dd7070Spatrick }
991e5dd7070Spatrick
992e5dd7070Spatrick //===----------------------------------------------------------------------===//
993e5dd7070Spatrick // Check: Should check whether privileges are dropped successfully.
994e5dd7070Spatrick // Originally: <rdar://problem/6337132>
995e5dd7070Spatrick //===----------------------------------------------------------------------===//
996e5dd7070Spatrick
checkUncheckedReturnValue(CallExpr * CE)997e5dd7070Spatrick void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
998e5dd7070Spatrick if (!filter.check_UncheckedReturn)
999e5dd7070Spatrick return;
1000e5dd7070Spatrick
1001e5dd7070Spatrick const FunctionDecl *FD = CE->getDirectCallee();
1002e5dd7070Spatrick if (!FD)
1003e5dd7070Spatrick return;
1004e5dd7070Spatrick
1005e5dd7070Spatrick if (II_setid[0] == nullptr) {
1006e5dd7070Spatrick static const char * const identifiers[num_setids] = {
1007e5dd7070Spatrick "setuid", "setgid", "seteuid", "setegid",
1008e5dd7070Spatrick "setreuid", "setregid"
1009e5dd7070Spatrick };
1010e5dd7070Spatrick
1011e5dd7070Spatrick for (size_t i = 0; i < num_setids; i++)
1012e5dd7070Spatrick II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1013e5dd7070Spatrick }
1014e5dd7070Spatrick
1015e5dd7070Spatrick const IdentifierInfo *id = FD->getIdentifier();
1016e5dd7070Spatrick size_t identifierid;
1017e5dd7070Spatrick
1018e5dd7070Spatrick for (identifierid = 0; identifierid < num_setids; identifierid++)
1019e5dd7070Spatrick if (id == II_setid[identifierid])
1020e5dd7070Spatrick break;
1021e5dd7070Spatrick
1022e5dd7070Spatrick if (identifierid >= num_setids)
1023e5dd7070Spatrick return;
1024e5dd7070Spatrick
1025e5dd7070Spatrick const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
1026e5dd7070Spatrick if (!FTP)
1027e5dd7070Spatrick return;
1028e5dd7070Spatrick
1029e5dd7070Spatrick // Verify that the function takes one or two arguments (depending on
1030e5dd7070Spatrick // the function).
1031e5dd7070Spatrick if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
1032e5dd7070Spatrick return;
1033e5dd7070Spatrick
1034e5dd7070Spatrick // The arguments must be integers.
1035e5dd7070Spatrick for (unsigned i = 0; i < FTP->getNumParams(); i++)
1036e5dd7070Spatrick if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
1037e5dd7070Spatrick return;
1038e5dd7070Spatrick
1039e5dd7070Spatrick // Issue a warning.
1040e5dd7070Spatrick SmallString<256> buf1;
1041e5dd7070Spatrick llvm::raw_svector_ostream os1(buf1);
1042e5dd7070Spatrick os1 << "Return value is not checked in call to '" << *FD << '\'';
1043e5dd7070Spatrick
1044e5dd7070Spatrick SmallString<256> buf2;
1045e5dd7070Spatrick llvm::raw_svector_ostream os2(buf2);
1046e5dd7070Spatrick os2 << "The return value from the call to '" << *FD
1047e5dd7070Spatrick << "' is not checked. If an error occurs in '" << *FD
1048e5dd7070Spatrick << "', the following code may execute with unexpected privileges";
1049e5dd7070Spatrick
1050e5dd7070Spatrick PathDiagnosticLocation CELoc =
1051e5dd7070Spatrick PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1052e5dd7070Spatrick BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1053e5dd7070Spatrick "Security", os2.str(), CELoc,
1054e5dd7070Spatrick CE->getCallee()->getSourceRange());
1055e5dd7070Spatrick }
1056e5dd7070Spatrick
1057e5dd7070Spatrick //===----------------------------------------------------------------------===//
1058e5dd7070Spatrick // SecuritySyntaxChecker
1059e5dd7070Spatrick //===----------------------------------------------------------------------===//
1060e5dd7070Spatrick
1061e5dd7070Spatrick namespace {
1062e5dd7070Spatrick class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1063e5dd7070Spatrick public:
1064e5dd7070Spatrick ChecksFilter filter;
1065e5dd7070Spatrick
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const1066e5dd7070Spatrick void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
1067e5dd7070Spatrick BugReporter &BR) const {
1068e5dd7070Spatrick WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1069e5dd7070Spatrick walker.Visit(D->getBody());
1070e5dd7070Spatrick }
1071e5dd7070Spatrick };
1072e5dd7070Spatrick }
1073e5dd7070Spatrick
registerSecuritySyntaxChecker(CheckerManager & mgr)1074e5dd7070Spatrick void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1075e5dd7070Spatrick mgr.registerChecker<SecuritySyntaxChecker>();
1076e5dd7070Spatrick }
1077e5dd7070Spatrick
shouldRegisterSecuritySyntaxChecker(const CheckerManager & mgr)1078ec727ea7Spatrick bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
1079e5dd7070Spatrick return true;
1080e5dd7070Spatrick }
1081e5dd7070Spatrick
1082e5dd7070Spatrick #define REGISTER_CHECKER(name) \
1083e5dd7070Spatrick void ento::register##name(CheckerManager &mgr) { \
1084e5dd7070Spatrick SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1085e5dd7070Spatrick checker->filter.check_##name = true; \
1086e5dd7070Spatrick checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
1087e5dd7070Spatrick } \
1088e5dd7070Spatrick \
1089ec727ea7Spatrick bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1090e5dd7070Spatrick
1091e5dd7070Spatrick REGISTER_CHECKER(bcmp)
1092e5dd7070Spatrick REGISTER_CHECKER(bcopy)
1093e5dd7070Spatrick REGISTER_CHECKER(bzero)
1094e5dd7070Spatrick REGISTER_CHECKER(gets)
1095e5dd7070Spatrick REGISTER_CHECKER(getpw)
1096e5dd7070Spatrick REGISTER_CHECKER(mkstemp)
1097e5dd7070Spatrick REGISTER_CHECKER(mktemp)
1098e5dd7070Spatrick REGISTER_CHECKER(strcpy)
1099e5dd7070Spatrick REGISTER_CHECKER(rand)
1100e5dd7070Spatrick REGISTER_CHECKER(vfork)
1101e5dd7070Spatrick REGISTER_CHECKER(FloatLoopCounter)
1102e5dd7070Spatrick REGISTER_CHECKER(UncheckedReturn)
1103e5dd7070Spatrick REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1104e5dd7070Spatrick REGISTER_CHECKER(decodeValueOfObjCType)
1105