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