1 //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a set of flow-insensitive security checks.
11 //===----------------------------------------------------------------------===//
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/AST/StmtVisitor.h"
15 #include "clang/Analysis/AnalysisDeclContext.h"
16 #include "clang/Basic/TargetInfo.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/ADT/StringSwitch.h"
22 #include "llvm/Support/raw_ostream.h"
24 using namespace clang
;
27 static bool isArc4RandomAvailable(const ASTContext
&Ctx
) {
28 const llvm::Triple
&T
= Ctx
.getTargetInfo().getTriple();
29 return T
.getVendor() == llvm::Triple::Apple
||
38 bool check_bcmp
= false;
39 bool check_bcopy
= false;
40 bool check_bzero
= false;
41 bool check_gets
= false;
42 bool check_getpw
= false;
43 bool check_mktemp
= false;
44 bool check_mkstemp
= false;
45 bool check_strcpy
= false;
46 bool check_DeprecatedOrUnsafeBufferHandling
= false;
47 bool check_rand
= false;
48 bool check_vfork
= false;
49 bool check_FloatLoopCounter
= false;
50 bool check_UncheckedReturn
= false;
51 bool check_decodeValueOfObjCType
= false;
53 CheckerNameRef checkName_bcmp
;
54 CheckerNameRef checkName_bcopy
;
55 CheckerNameRef checkName_bzero
;
56 CheckerNameRef checkName_gets
;
57 CheckerNameRef checkName_getpw
;
58 CheckerNameRef checkName_mktemp
;
59 CheckerNameRef checkName_mkstemp
;
60 CheckerNameRef checkName_strcpy
;
61 CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling
;
62 CheckerNameRef checkName_rand
;
63 CheckerNameRef checkName_vfork
;
64 CheckerNameRef checkName_FloatLoopCounter
;
65 CheckerNameRef checkName_UncheckedReturn
;
66 CheckerNameRef checkName_decodeValueOfObjCType
;
69 class WalkAST
: public StmtVisitor
<WalkAST
> {
71 AnalysisDeclContext
* AC
;
72 enum { num_setids
= 6 };
73 IdentifierInfo
*II_setid
[num_setids
];
76 const ChecksFilter
&filter
;
79 WalkAST(BugReporter
&br
, AnalysisDeclContext
* ac
,
80 const ChecksFilter
&f
)
81 : BR(br
), AC(ac
), II_setid(),
82 CheckRand(isArc4RandomAvailable(BR
.getContext())),
85 // Statement visitor methods.
86 void VisitCallExpr(CallExpr
*CE
);
87 void VisitObjCMessageExpr(ObjCMessageExpr
*CE
);
88 void VisitForStmt(ForStmt
*S
);
89 void VisitCompoundStmt (CompoundStmt
*S
);
90 void VisitStmt(Stmt
*S
) { VisitChildren(S
); }
92 void VisitChildren(Stmt
*S
);
95 bool checkCall_strCommon(const CallExpr
*CE
, const FunctionDecl
*FD
);
97 typedef void (WalkAST::*FnCheck
)(const CallExpr
*, const FunctionDecl
*);
98 typedef void (WalkAST::*MsgCheck
)(const ObjCMessageExpr
*);
100 // Checker-specific methods.
101 void checkLoopConditionForFloat(const ForStmt
*FS
);
102 void checkCall_bcmp(const CallExpr
*CE
, const FunctionDecl
*FD
);
103 void checkCall_bcopy(const CallExpr
*CE
, const FunctionDecl
*FD
);
104 void checkCall_bzero(const CallExpr
*CE
, const FunctionDecl
*FD
);
105 void checkCall_gets(const CallExpr
*CE
, const FunctionDecl
*FD
);
106 void checkCall_getpw(const CallExpr
*CE
, const FunctionDecl
*FD
);
107 void checkCall_mktemp(const CallExpr
*CE
, const FunctionDecl
*FD
);
108 void checkCall_mkstemp(const CallExpr
*CE
, const FunctionDecl
*FD
);
109 void checkCall_strcpy(const CallExpr
*CE
, const FunctionDecl
*FD
);
110 void checkCall_strcat(const CallExpr
*CE
, const FunctionDecl
*FD
);
111 void checkDeprecatedOrUnsafeBufferHandling(const CallExpr
*CE
,
112 const FunctionDecl
*FD
);
113 void checkCall_rand(const CallExpr
*CE
, const FunctionDecl
*FD
);
114 void checkCall_random(const CallExpr
*CE
, const FunctionDecl
*FD
);
115 void checkCall_vfork(const CallExpr
*CE
, const FunctionDecl
*FD
);
116 void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr
*ME
);
117 void checkUncheckedReturnValue(CallExpr
*CE
);
119 } // end anonymous namespace
121 //===----------------------------------------------------------------------===//
123 //===----------------------------------------------------------------------===//
125 void WalkAST::VisitChildren(Stmt
*S
) {
126 for (Stmt
*Child
: S
->children())
131 void WalkAST::VisitCallExpr(CallExpr
*CE
) {
133 const FunctionDecl
*FD
= CE
->getDirectCallee();
138 // Get the name of the callee. If it's a builtin, strip off the prefix.
139 IdentifierInfo
*II
= FD
->getIdentifier();
140 if (!II
) // if no identifier, not a simple C function
142 StringRef Name
= II
->getName();
143 if (Name
.startswith("__builtin_"))
144 Name
= Name
.substr(10);
146 // Set the evaluation function by switching on the callee name.
147 FnCheck evalFunction
= llvm::StringSwitch
<FnCheck
>(Name
)
148 .Case("bcmp", &WalkAST::checkCall_bcmp
)
149 .Case("bcopy", &WalkAST::checkCall_bcopy
)
150 .Case("bzero", &WalkAST::checkCall_bzero
)
151 .Case("gets", &WalkAST::checkCall_gets
)
152 .Case("getpw", &WalkAST::checkCall_getpw
)
153 .Case("mktemp", &WalkAST::checkCall_mktemp
)
154 .Case("mkstemp", &WalkAST::checkCall_mkstemp
)
155 .Case("mkdtemp", &WalkAST::checkCall_mkstemp
)
156 .Case("mkstemps", &WalkAST::checkCall_mkstemp
)
157 .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy
)
158 .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat
)
159 .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
160 "vscanf", "vwscanf", "vfscanf", "vfwscanf",
161 &WalkAST::checkDeprecatedOrUnsafeBufferHandling
)
162 .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
163 "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
164 &WalkAST::checkDeprecatedOrUnsafeBufferHandling
)
165 .Cases("strncpy", "strncat", "memset",
166 &WalkAST::checkDeprecatedOrUnsafeBufferHandling
)
167 .Case("drand48", &WalkAST::checkCall_rand
)
168 .Case("erand48", &WalkAST::checkCall_rand
)
169 .Case("jrand48", &WalkAST::checkCall_rand
)
170 .Case("lrand48", &WalkAST::checkCall_rand
)
171 .Case("mrand48", &WalkAST::checkCall_rand
)
172 .Case("nrand48", &WalkAST::checkCall_rand
)
173 .Case("lcong48", &WalkAST::checkCall_rand
)
174 .Case("rand", &WalkAST::checkCall_rand
)
175 .Case("rand_r", &WalkAST::checkCall_rand
)
176 .Case("random", &WalkAST::checkCall_random
)
177 .Case("vfork", &WalkAST::checkCall_vfork
)
180 // If the callee isn't defined, it is not of security concern.
181 // Check and evaluate the call.
183 (this->*evalFunction
)(CE
, FD
);
185 // Recurse and check children.
189 void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr
*ME
) {
190 MsgCheck evalFunction
=
191 llvm::StringSwitch
<MsgCheck
>(ME
->getSelector().getAsString())
192 .Case("decodeValueOfObjCType:at:",
193 &WalkAST::checkMsg_decodeValueOfObjCType
)
197 (this->*evalFunction
)(ME
);
199 // Recurse and check children.
203 void WalkAST::VisitCompoundStmt(CompoundStmt
*S
) {
204 for (Stmt
*Child
: S
->children())
206 if (CallExpr
*CE
= dyn_cast
<CallExpr
>(Child
))
207 checkUncheckedReturnValue(CE
);
212 void WalkAST::VisitForStmt(ForStmt
*FS
) {
213 checkLoopConditionForFloat(FS
);
215 // Recurse and check children.
219 //===----------------------------------------------------------------------===//
220 // Check: floating point variable used as loop counter.
221 // Implements: CERT security coding advisory FLP-30.
222 //===----------------------------------------------------------------------===//
224 // Returns either 'x' or 'y', depending on which one of them is incremented
225 // in 'expr', or nullptr if none of them is incremented.
226 static const DeclRefExpr
*
227 getIncrementedVar(const Expr
*expr
, const VarDecl
*x
, const VarDecl
*y
) {
228 expr
= expr
->IgnoreParenCasts();
230 if (const BinaryOperator
*B
= dyn_cast
<BinaryOperator
>(expr
)) {
231 if (!(B
->isAssignmentOp() || B
->isCompoundAssignmentOp() ||
232 B
->getOpcode() == BO_Comma
))
235 if (const DeclRefExpr
*lhs
= getIncrementedVar(B
->getLHS(), x
, y
))
238 if (const DeclRefExpr
*rhs
= getIncrementedVar(B
->getRHS(), x
, y
))
244 if (const DeclRefExpr
*DR
= dyn_cast
<DeclRefExpr
>(expr
)) {
245 const NamedDecl
*ND
= DR
->getDecl();
246 return ND
== x
|| ND
== y
? DR
: nullptr;
249 if (const UnaryOperator
*U
= dyn_cast
<UnaryOperator
>(expr
))
250 return U
->isIncrementDecrementOp()
251 ? getIncrementedVar(U
->getSubExpr(), x
, y
) : nullptr;
256 /// CheckLoopConditionForFloat - This check looks for 'for' statements that
257 /// use a floating point variable as a loop counter.
258 /// CERT: FLP30-C, FLP30-CPP.
260 void WalkAST::checkLoopConditionForFloat(const ForStmt
*FS
) {
261 if (!filter
.check_FloatLoopCounter
)
264 // Does the loop have a condition?
265 const Expr
*condition
= FS
->getCond();
270 // Does the loop have an increment?
271 const Expr
*increment
= FS
->getInc();
276 // Strip away '()' and casts.
277 condition
= condition
->IgnoreParenCasts();
278 increment
= increment
->IgnoreParenCasts();
280 // Is the loop condition a comparison?
281 const BinaryOperator
*B
= dyn_cast
<BinaryOperator
>(condition
);
286 // Is this a comparison?
287 if (!(B
->isRelationalOp() || B
->isEqualityOp()))
290 // Are we comparing variables?
291 const DeclRefExpr
*drLHS
=
292 dyn_cast
<DeclRefExpr
>(B
->getLHS()->IgnoreParenLValueCasts());
293 const DeclRefExpr
*drRHS
=
294 dyn_cast
<DeclRefExpr
>(B
->getRHS()->IgnoreParenLValueCasts());
296 // Does at least one of the variables have a floating point type?
297 drLHS
= drLHS
&& drLHS
->getType()->isRealFloatingType() ? drLHS
: nullptr;
298 drRHS
= drRHS
&& drRHS
->getType()->isRealFloatingType() ? drRHS
: nullptr;
300 if (!drLHS
&& !drRHS
)
303 const VarDecl
*vdLHS
= drLHS
? dyn_cast
<VarDecl
>(drLHS
->getDecl()) : nullptr;
304 const VarDecl
*vdRHS
= drRHS
? dyn_cast
<VarDecl
>(drRHS
->getDecl()) : nullptr;
306 if (!vdLHS
&& !vdRHS
)
309 // Does either variable appear in increment?
310 const DeclRefExpr
*drInc
= getIncrementedVar(increment
, vdLHS
, vdRHS
);
314 const VarDecl
*vdInc
= cast
<VarDecl
>(drInc
->getDecl());
315 assert(vdInc
&& (vdInc
== vdLHS
|| vdInc
== vdRHS
));
317 // Emit the error. First figure out which DeclRefExpr in the condition
318 // referenced the compared variable.
319 const DeclRefExpr
*drCond
= vdLHS
== vdInc
? drLHS
: drRHS
;
321 SmallVector
<SourceRange
, 2> ranges
;
322 SmallString
<256> sbuf
;
323 llvm::raw_svector_ostream
os(sbuf
);
325 os
<< "Variable '" << drCond
->getDecl()->getName()
326 << "' with floating point type '" << drCond
->getType()
327 << "' should not be used as a loop counter";
329 ranges
.push_back(drCond
->getSourceRange());
330 ranges
.push_back(drInc
->getSourceRange());
332 const char *bugType
= "Floating point variable used as loop counter";
334 PathDiagnosticLocation FSLoc
=
335 PathDiagnosticLocation::createBegin(FS
, BR
.getSourceManager(), AC
);
336 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_FloatLoopCounter
,
337 bugType
, "Security", os
.str(),
341 //===----------------------------------------------------------------------===//
342 // Check: Any use of bcmp.
343 // CWE-477: Use of Obsolete Functions
344 // bcmp was deprecated in POSIX.1-2008
345 //===----------------------------------------------------------------------===//
347 void WalkAST::checkCall_bcmp(const CallExpr
*CE
, const FunctionDecl
*FD
) {
348 if (!filter
.check_bcmp
)
351 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
355 // Verify that the function takes three arguments.
356 if (FPT
->getNumParams() != 3)
359 for (int i
= 0; i
< 2; i
++) {
360 // Verify the first and second argument type is void*.
361 const PointerType
*PT
= FPT
->getParamType(i
)->getAs
<PointerType
>();
365 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().VoidTy
)
369 // Verify the third argument type is integer.
370 if (!FPT
->getParamType(2)->isIntegralOrUnscopedEnumerationType())
374 PathDiagnosticLocation CELoc
=
375 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
376 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_bcmp
,
377 "Use of deprecated function in call to 'bcmp()'",
379 "The bcmp() function is obsoleted by memcmp().",
380 CELoc
, CE
->getCallee()->getSourceRange());
383 //===----------------------------------------------------------------------===//
384 // Check: Any use of bcopy.
385 // CWE-477: Use of Obsolete Functions
386 // bcopy was deprecated in POSIX.1-2008
387 //===----------------------------------------------------------------------===//
389 void WalkAST::checkCall_bcopy(const CallExpr
*CE
, const FunctionDecl
*FD
) {
390 if (!filter
.check_bcopy
)
393 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
397 // Verify that the function takes three arguments.
398 if (FPT
->getNumParams() != 3)
401 for (int i
= 0; i
< 2; i
++) {
402 // Verify the first and second argument type is void*.
403 const PointerType
*PT
= FPT
->getParamType(i
)->getAs
<PointerType
>();
407 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().VoidTy
)
411 // Verify the third argument type is integer.
412 if (!FPT
->getParamType(2)->isIntegralOrUnscopedEnumerationType())
416 PathDiagnosticLocation CELoc
=
417 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
418 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_bcopy
,
419 "Use of deprecated function in call to 'bcopy()'",
421 "The bcopy() function is obsoleted by memcpy() "
423 CELoc
, CE
->getCallee()->getSourceRange());
426 //===----------------------------------------------------------------------===//
427 // Check: Any use of bzero.
428 // CWE-477: Use of Obsolete Functions
429 // bzero was deprecated in POSIX.1-2008
430 //===----------------------------------------------------------------------===//
432 void WalkAST::checkCall_bzero(const CallExpr
*CE
, const FunctionDecl
*FD
) {
433 if (!filter
.check_bzero
)
436 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
440 // Verify that the function takes two arguments.
441 if (FPT
->getNumParams() != 2)
444 // Verify the first argument type is void*.
445 const PointerType
*PT
= FPT
->getParamType(0)->getAs
<PointerType
>();
449 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().VoidTy
)
452 // Verify the second argument type is integer.
453 if (!FPT
->getParamType(1)->isIntegralOrUnscopedEnumerationType())
457 PathDiagnosticLocation CELoc
=
458 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
459 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_bzero
,
460 "Use of deprecated function in call to 'bzero()'",
462 "The bzero() function is obsoleted by memset().",
463 CELoc
, CE
->getCallee()->getSourceRange());
467 //===----------------------------------------------------------------------===//
468 // Check: Any use of 'gets' is insecure. Most man pages literally says this.
470 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
471 // CWE-242: Use of Inherently Dangerous Function
472 //===----------------------------------------------------------------------===//
474 void WalkAST::checkCall_gets(const CallExpr
*CE
, const FunctionDecl
*FD
) {
475 if (!filter
.check_gets
)
478 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
482 // Verify that the function takes a single argument.
483 if (FPT
->getNumParams() != 1)
486 // Is the argument a 'char*'?
487 const PointerType
*PT
= FPT
->getParamType(0)->getAs
<PointerType
>();
491 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().CharTy
)
495 PathDiagnosticLocation CELoc
=
496 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
497 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_gets
,
498 "Potential buffer overflow in call to 'gets'",
500 "Call to function 'gets' is extremely insecure as it can "
501 "always result in a buffer overflow",
502 CELoc
, CE
->getCallee()->getSourceRange());
505 //===----------------------------------------------------------------------===//
506 // Check: Any use of 'getpwd' is insecure.
507 // CWE-477: Use of Obsolete Functions
508 //===----------------------------------------------------------------------===//
510 void WalkAST::checkCall_getpw(const CallExpr
*CE
, const FunctionDecl
*FD
) {
511 if (!filter
.check_getpw
)
514 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
518 // Verify that the function takes two arguments.
519 if (FPT
->getNumParams() != 2)
522 // Verify the first argument type is integer.
523 if (!FPT
->getParamType(0)->isIntegralOrUnscopedEnumerationType())
526 // Verify the second argument type is char*.
527 const PointerType
*PT
= FPT
->getParamType(1)->getAs
<PointerType
>();
531 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().CharTy
)
535 PathDiagnosticLocation CELoc
=
536 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
537 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_getpw
,
538 "Potential buffer overflow in call to 'getpw'",
540 "The getpw() function is dangerous as it may overflow the "
541 "provided buffer. It is obsoleted by getpwuid().",
542 CELoc
, CE
->getCallee()->getSourceRange());
545 //===----------------------------------------------------------------------===//
546 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
547 // CWE-377: Insecure Temporary File
548 //===----------------------------------------------------------------------===//
550 void WalkAST::checkCall_mktemp(const CallExpr
*CE
, const FunctionDecl
*FD
) {
551 if (!filter
.check_mktemp
) {
552 // Fall back to the security check of looking for enough 'X's in the
553 // format string, since that is a less severe warning.
554 checkCall_mkstemp(CE
, FD
);
558 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
562 // Verify that the function takes a single argument.
563 if (FPT
->getNumParams() != 1)
566 // Verify that the argument is Pointer Type.
567 const PointerType
*PT
= FPT
->getParamType(0)->getAs
<PointerType
>();
571 // Verify that the argument is a 'char*'.
572 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().CharTy
)
576 PathDiagnosticLocation CELoc
=
577 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
578 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_mktemp
,
579 "Potential insecure temporary file in call 'mktemp'",
581 "Call to function 'mktemp' is insecure as it always "
582 "creates or uses insecure temporary file. Use 'mkstemp' "
584 CELoc
, CE
->getCallee()->getSourceRange());
587 //===----------------------------------------------------------------------===//
588 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
589 //===----------------------------------------------------------------------===//
591 void WalkAST::checkCall_mkstemp(const CallExpr
*CE
, const FunctionDecl
*FD
) {
592 if (!filter
.check_mkstemp
)
595 StringRef Name
= FD
->getIdentifier()->getName();
596 std::pair
<signed, signed> ArgSuffix
=
597 llvm::StringSwitch
<std::pair
<signed, signed> >(Name
)
598 .Case("mktemp", std::make_pair(0,-1))
599 .Case("mkstemp", std::make_pair(0,-1))
600 .Case("mkdtemp", std::make_pair(0,-1))
601 .Case("mkstemps", std::make_pair(0,1))
602 .Default(std::make_pair(-1, -1));
604 assert(ArgSuffix
.first
>= 0 && "Unsupported function");
606 // Check if the number of arguments is consistent with out expectations.
607 unsigned numArgs
= CE
->getNumArgs();
608 if ((signed) numArgs
<= ArgSuffix
.first
)
611 const StringLiteral
*strArg
=
612 dyn_cast
<StringLiteral
>(CE
->getArg((unsigned)ArgSuffix
.first
)
613 ->IgnoreParenImpCasts());
615 // Currently we only handle string literals. It is possible to do better,
616 // either by looking at references to const variables, or by doing real
618 if (!strArg
|| strArg
->getCharByteWidth() != 1)
621 // Count the number of X's, taking into account a possible cutoff suffix.
622 StringRef str
= strArg
->getString();
624 unsigned n
= str
.size();
626 // Take into account the suffix.
628 if (ArgSuffix
.second
>= 0) {
629 const Expr
*suffixEx
= CE
->getArg((unsigned)ArgSuffix
.second
);
630 Expr::EvalResult EVResult
;
631 if (!suffixEx
->EvaluateAsInt(EVResult
, BR
.getContext()))
633 llvm::APSInt Result
= EVResult
.Val
.getInt();
634 // FIXME: Issue a warning.
635 if (Result
.isNegative())
637 suffix
= (unsigned) Result
.getZExtValue();
638 n
= (n
> suffix
) ? n
- suffix
: 0;
641 for (unsigned i
= 0; i
< n
; ++i
)
642 if (str
[i
] == 'X') ++numX
;
648 PathDiagnosticLocation CELoc
=
649 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
650 SmallString
<512> buf
;
651 llvm::raw_svector_ostream
out(buf
);
652 out
<< "Call to '" << Name
<< "' should have at least 6 'X's in the"
653 " format string to be secure (" << numX
<< " 'X'";
658 out
<< ", " << suffix
<< " character";
661 out
<< " used as a suffix";
664 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_mkstemp
,
665 "Insecure temporary file creation", "Security",
666 out
.str(), CELoc
, strArg
->getSourceRange());
669 //===----------------------------------------------------------------------===//
670 // Check: Any use of 'strcpy' is insecure.
672 // CWE-119: Improper Restriction of Operations within
673 // the Bounds of a Memory Buffer
674 //===----------------------------------------------------------------------===//
676 void WalkAST::checkCall_strcpy(const CallExpr
*CE
, const FunctionDecl
*FD
) {
677 if (!filter
.check_strcpy
)
680 if (!checkCall_strCommon(CE
, FD
))
683 const auto *Target
= CE
->getArg(0)->IgnoreImpCasts(),
684 *Source
= CE
->getArg(1)->IgnoreImpCasts();
686 if (const auto *Array
= dyn_cast
<ConstantArrayType
>(Target
->getType())) {
687 uint64_t ArraySize
= BR
.getContext().getTypeSize(Array
) / 8;
688 if (const auto *String
= dyn_cast
<StringLiteral
>(Source
)) {
689 if (ArraySize
>= String
->getLength() + 1)
695 PathDiagnosticLocation CELoc
=
696 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
697 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_strcpy
,
698 "Potential insecure memory buffer bounds restriction in "
701 "Call to function 'strcpy' is insecure as it does not "
702 "provide bounding of the memory buffer. Replace "
703 "unbounded copy functions with analogous functions that "
704 "support length arguments such as 'strlcpy'. CWE-119.",
705 CELoc
, CE
->getCallee()->getSourceRange());
708 //===----------------------------------------------------------------------===//
709 // Check: Any use of 'strcat' is insecure.
711 // CWE-119: Improper Restriction of Operations within
712 // the Bounds of a Memory Buffer
713 //===----------------------------------------------------------------------===//
715 void WalkAST::checkCall_strcat(const CallExpr
*CE
, const FunctionDecl
*FD
) {
716 if (!filter
.check_strcpy
)
719 if (!checkCall_strCommon(CE
, FD
))
723 PathDiagnosticLocation CELoc
=
724 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
725 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_strcpy
,
726 "Potential insecure memory buffer bounds restriction in "
729 "Call to function 'strcat' is insecure as it does not "
730 "provide bounding of the memory buffer. Replace "
731 "unbounded copy functions with analogous functions that "
732 "support length arguments such as 'strlcat'. CWE-119.",
733 CELoc
, CE
->getCallee()->getSourceRange());
736 //===----------------------------------------------------------------------===//
737 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
738 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
739 // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
740 // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
741 // is deprecated since C11.
743 // Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
744 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
745 // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
748 // CWE-119: Improper Restriction of Operations within
749 // the Bounds of a Memory Buffer
750 //===----------------------------------------------------------------------===//
752 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr
*CE
,
753 const FunctionDecl
*FD
) {
754 if (!filter
.check_DeprecatedOrUnsafeBufferHandling
)
757 if (!BR
.getContext().getLangOpts().C11
)
760 // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
762 enum { DEPR_ONLY
= -1, UNKNOWN_CALL
= -2 };
764 StringRef Name
= FD
->getIdentifier()->getName();
765 if (Name
.startswith("__builtin_"))
766 Name
= Name
.substr(10);
769 llvm::StringSwitch
<int>(Name
)
770 .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
771 .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
772 "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
773 .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
774 "memmove", "memset", "strncpy", "strncat", DEPR_ONLY
)
775 .Default(UNKNOWN_CALL
);
777 assert(ArgIndex
!= UNKNOWN_CALL
&& "Unsupported function");
778 bool BoundsProvided
= ArgIndex
== DEPR_ONLY
;
780 if (!BoundsProvided
) {
781 // Currently we only handle (not wide) string literals. It is possible to do
782 // better, either by looking at references to const variables, or by doing
783 // real flow analysis.
785 dyn_cast
<StringLiteral
>(CE
->getArg(ArgIndex
)->IgnoreParenImpCasts());
786 if (FormatString
&& !FormatString
->getString().contains("%s") &&
787 !FormatString
->getString().contains("%["))
788 BoundsProvided
= true;
791 SmallString
<128> Buf1
;
792 SmallString
<512> Buf2
;
793 llvm::raw_svector_ostream
Out1(Buf1
);
794 llvm::raw_svector_ostream
Out2(Buf2
);
796 Out1
<< "Potential insecure memory buffer bounds restriction in call '"
798 Out2
<< "Call to function '" << Name
799 << "' is insecure as it does not provide ";
801 if (!BoundsProvided
) {
802 Out2
<< "bounding of the memory buffer or ";
805 Out2
<< "security checks introduced "
806 "in the C11 standard. Replace with analogous functions that "
807 "support length arguments or provides boundary checks such as '"
808 << Name
<< "_s' in case of C11";
810 PathDiagnosticLocation CELoc
=
811 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
812 BR
.EmitBasicReport(AC
->getDecl(),
813 filter
.checkName_DeprecatedOrUnsafeBufferHandling
,
814 Out1
.str(), "Security", Out2
.str(), CELoc
,
815 CE
->getCallee()->getSourceRange());
818 //===----------------------------------------------------------------------===//
819 // Common check for str* functions with no bounds parameters.
820 //===----------------------------------------------------------------------===//
822 bool WalkAST::checkCall_strCommon(const CallExpr
*CE
, const FunctionDecl
*FD
) {
823 const FunctionProtoType
*FPT
= FD
->getType()->getAs
<FunctionProtoType
>();
827 // Verify the function takes two arguments, three in the _chk version.
828 int numArgs
= FPT
->getNumParams();
829 if (numArgs
!= 2 && numArgs
!= 3)
832 // Verify the type for both arguments.
833 for (int i
= 0; i
< 2; i
++) {
834 // Verify that the arguments are pointers.
835 const PointerType
*PT
= FPT
->getParamType(i
)->getAs
<PointerType
>();
839 // Verify that the argument is a 'char*'.
840 if (PT
->getPointeeType().getUnqualifiedType() != BR
.getContext().CharTy
)
847 //===----------------------------------------------------------------------===//
848 // Check: Linear congruent random number generators should not be used,
849 // i.e. rand(), random().
851 // E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"
852 // in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,
853 // May 1998, https://doi.org/10.1109/18.669305
855 // CWE-338: Use of cryptographically weak prng
856 //===----------------------------------------------------------------------===//
858 void WalkAST::checkCall_rand(const CallExpr
*CE
, const FunctionDecl
*FD
) {
859 if (!filter
.check_rand
|| !CheckRand
)
862 const FunctionProtoType
*FTP
= FD
->getType()->getAs
<FunctionProtoType
>();
866 if (FTP
->getNumParams() == 1) {
867 // Is the argument an 'unsigned short *'?
868 // (Actually any integer type is allowed.)
869 const PointerType
*PT
= FTP
->getParamType(0)->getAs
<PointerType
>();
873 if (! PT
->getPointeeType()->isIntegralOrUnscopedEnumerationType())
875 } else if (FTP
->getNumParams() != 0)
879 SmallString
<256> buf1
;
880 llvm::raw_svector_ostream
os1(buf1
);
881 os1
<< '\'' << *FD
<< "' is a poor random number generator";
883 SmallString
<256> buf2
;
884 llvm::raw_svector_ostream
os2(buf2
);
885 os2
<< "Function '" << *FD
886 << "' is obsolete because it implements a poor random number generator."
887 << " Use 'arc4random' instead";
889 PathDiagnosticLocation CELoc
=
890 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
891 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_rand
, os1
.str(),
892 "Security", os2
.str(), CELoc
,
893 CE
->getCallee()->getSourceRange());
896 // See justification for rand().
897 void WalkAST::checkCall_random(const CallExpr
*CE
, const FunctionDecl
*FD
) {
898 if (!CheckRand
|| !filter
.check_rand
)
901 const FunctionProtoType
*FTP
= FD
->getType()->getAs
<FunctionProtoType
>();
905 // Verify that the function takes no argument.
906 if (FTP
->getNumParams() != 0)
910 PathDiagnosticLocation CELoc
=
911 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
912 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_rand
,
913 "'random' is not a secure random number generator",
915 "The 'random' function produces a sequence of values that "
916 "an adversary may be able to predict. Use 'arc4random' "
917 "instead", CELoc
, CE
->getCallee()->getSourceRange());
920 //===----------------------------------------------------------------------===//
921 // Check: 'vfork' should not be used.
922 // POS33-C: Do not use vfork().
923 //===----------------------------------------------------------------------===//
925 void WalkAST::checkCall_vfork(const CallExpr
*CE
, const FunctionDecl
*FD
) {
926 if (!filter
.check_vfork
)
929 // All calls to vfork() are insecure, issue a warning.
930 PathDiagnosticLocation CELoc
=
931 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
932 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_vfork
,
933 "Potential insecure implementation-specific behavior in "
936 "Call to function 'vfork' is insecure as it can lead to "
937 "denial of service situations in the parent process. "
938 "Replace calls to vfork with calls to the safer "
939 "'posix_spawn' function",
940 CELoc
, CE
->getCallee()->getSourceRange());
943 //===----------------------------------------------------------------------===//
944 // Check: '-decodeValueOfObjCType:at:' should not be used.
945 // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
946 // likelihood of buffer overflows.
947 //===----------------------------------------------------------------------===//
949 void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr
*ME
) {
950 if (!filter
.check_decodeValueOfObjCType
)
953 // Check availability of the secure alternative:
954 // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
955 // FIXME: We probably shouldn't register the check if it's not available.
956 const TargetInfo
&TI
= AC
->getASTContext().getTargetInfo();
957 const llvm::Triple
&T
= TI
.getTriple();
958 const VersionTuple
&VT
= TI
.getPlatformMinVersion();
960 case llvm::Triple::IOS
:
961 if (VT
< VersionTuple(11, 0))
964 case llvm::Triple::MacOSX
:
965 if (VT
< VersionTuple(10, 13))
968 case llvm::Triple::WatchOS
:
969 if (VT
< VersionTuple(4, 0))
972 case llvm::Triple::TvOS
:
973 if (VT
< VersionTuple(11, 0))
980 PathDiagnosticLocation MELoc
=
981 PathDiagnosticLocation::createBegin(ME
, BR
.getSourceManager(), AC
);
983 AC
->getDecl(), filter
.checkName_decodeValueOfObjCType
,
984 "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
985 "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
986 "as it can lead to potential buffer overflows. Use the safer "
987 "'-decodeValueOfObjCType:at:size:' method.",
988 MELoc
, ME
->getSourceRange());
991 //===----------------------------------------------------------------------===//
992 // Check: The caller should always verify that the privileges
993 // were dropped successfully.
995 // Some library functions, like setuid() and setgid(), should always be used
996 // with a check of the return value to verify that the function completed
997 // successfully. If the drop fails, the software will continue to run
998 // with the raised privileges, which might provide additional access
999 // to unprivileged users.
1001 // (Note that this check predates __attribute__((warn_unused_result)).
1002 // Do we still need it now that we have a compiler warning for this?
1003 // Are these standard functions already annotated this way?)
1004 //===----------------------------------------------------------------------===//
1006 void WalkAST::checkUncheckedReturnValue(CallExpr
*CE
) {
1007 if (!filter
.check_UncheckedReturn
)
1010 const FunctionDecl
*FD
= CE
->getDirectCallee();
1014 if (II_setid
[0] == nullptr) {
1015 static const char * const identifiers
[num_setids
] = {
1016 "setuid", "setgid", "seteuid", "setegid",
1017 "setreuid", "setregid"
1020 for (size_t i
= 0; i
< num_setids
; i
++)
1021 II_setid
[i
] = &BR
.getContext().Idents
.get(identifiers
[i
]);
1024 const IdentifierInfo
*id
= FD
->getIdentifier();
1025 size_t identifierid
;
1027 for (identifierid
= 0; identifierid
< num_setids
; identifierid
++)
1028 if (id
== II_setid
[identifierid
])
1031 if (identifierid
>= num_setids
)
1034 const FunctionProtoType
*FTP
= FD
->getType()->getAs
<FunctionProtoType
>();
1038 // Verify that the function takes one or two arguments (depending on
1040 if (FTP
->getNumParams() != (identifierid
< 4 ? 1 : 2))
1043 // The arguments must be integers.
1044 for (unsigned i
= 0; i
< FTP
->getNumParams(); i
++)
1045 if (!FTP
->getParamType(i
)->isIntegralOrUnscopedEnumerationType())
1049 SmallString
<256> buf1
;
1050 llvm::raw_svector_ostream
os1(buf1
);
1051 os1
<< "Return value is not checked in call to '" << *FD
<< '\'';
1053 SmallString
<256> buf2
;
1054 llvm::raw_svector_ostream
os2(buf2
);
1055 os2
<< "The return value from the call to '" << *FD
1056 << "' is not checked. If an error occurs in '" << *FD
1057 << "', the following code may execute with unexpected privileges";
1059 PathDiagnosticLocation CELoc
=
1060 PathDiagnosticLocation::createBegin(CE
, BR
.getSourceManager(), AC
);
1061 BR
.EmitBasicReport(AC
->getDecl(), filter
.checkName_UncheckedReturn
, os1
.str(),
1062 "Security", os2
.str(), CELoc
,
1063 CE
->getCallee()->getSourceRange());
1066 //===----------------------------------------------------------------------===//
1067 // SecuritySyntaxChecker
1068 //===----------------------------------------------------------------------===//
1071 class SecuritySyntaxChecker
: public Checker
<check::ASTCodeBody
> {
1073 ChecksFilter filter
;
1075 void checkASTCodeBody(const Decl
*D
, AnalysisManager
& mgr
,
1076 BugReporter
&BR
) const {
1077 WalkAST
walker(BR
, mgr
.getAnalysisDeclContext(D
), filter
);
1078 walker
.Visit(D
->getBody());
1083 void ento::registerSecuritySyntaxChecker(CheckerManager
&mgr
) {
1084 mgr
.registerChecker
<SecuritySyntaxChecker
>();
1087 bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager
&mgr
) {
1091 #define REGISTER_CHECKER(name) \
1092 void ento::register##name(CheckerManager &mgr) { \
1093 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1094 checker->filter.check_##name = true; \
1095 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
1098 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1100 REGISTER_CHECKER(bcmp
)
1101 REGISTER_CHECKER(bcopy
)
1102 REGISTER_CHECKER(bzero
)
1103 REGISTER_CHECKER(gets
)
1104 REGISTER_CHECKER(getpw
)
1105 REGISTER_CHECKER(mkstemp
)
1106 REGISTER_CHECKER(mktemp
)
1107 REGISTER_CHECKER(strcpy
)
1108 REGISTER_CHECKER(rand
)
1109 REGISTER_CHECKER(vfork
)
1110 REGISTER_CHECKER(FloatLoopCounter
)
1111 REGISTER_CHECKER(UncheckedReturn
)
1112 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling
)
1113 REGISTER_CHECKER(decodeValueOfObjCType
)