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