Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
35266 views
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
24
using namespace clang;
25
using namespace ento;
26
27
static 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
36
namespace {
37
struct 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
69
class 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
78
public:
79
WalkAST(BugReporter &br, AnalysisDeclContext* ac,
80
const ChecksFilter &f)
81
: BR(br), AC(ac), II_setid(),
82
CheckRand(isArc4RandomAvailable(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
125
void WalkAST::VisitChildren(Stmt *S) {
126
for (Stmt *Child : S->children())
127
if (Child)
128
Visit(Child);
129
}
130
131
void 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("__builtin_");
144
145
// Set the evaluation function by switching on the callee name.
146
FnCheck evalFunction =
147
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", "fprintf",
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)
178
.Default(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(CE);
187
}
188
189
void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
190
MsgCheck evalFunction =
191
llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
192
.Case("decodeValueOfObjCType:at:",
193
&WalkAST::checkMsg_decodeValueOfObjCType)
194
.Default(nullptr);
195
196
if (evalFunction)
197
(this->*evalFunction)(ME);
198
199
// Recurse and check children.
200
VisitChildren(ME);
201
}
202
203
void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
204
for (Stmt *Child : S->children())
205
if (Child) {
206
if (CallExpr *CE = dyn_cast<CallExpr>(Child))
207
checkUncheckedReturnValue(CE);
208
Visit(Child);
209
}
210
}
211
212
void WalkAST::VisitForStmt(ForStmt *FS) {
213
checkLoopConditionForFloat(FS);
214
215
// Recurse and check children.
216
VisitChildren(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.
226
static const DeclRefExpr*
227
getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
228
expr = expr->IgnoreParenCasts();
229
230
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
231
if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
232
B->getOpcode() == BO_Comma))
233
return nullptr;
234
235
if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
236
return lhs;
237
238
if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
239
return rhs;
240
241
return nullptr;
242
}
243
244
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(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>(expr))
250
return U->isIncrementDecrementOp()
251
? getIncrementedVar(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
///
260
void 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>(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>(B->getLHS()->IgnoreParenLValueCasts());
293
const DeclRefExpr *drRHS =
294
dyn_cast<DeclRefExpr>(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>(drLHS->getDecl()) : nullptr;
304
const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
305
306
if (!vdLHS && !vdRHS)
307
return;
308
309
// Does either variable appear in increment?
310
const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
311
if (!drInc)
312
return;
313
314
const VarDecl *vdInc = cast<VarDecl>(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(drCond->getSourceRange());
330
ranges.push_back(drInc->getSourceRange());
331
332
const char *bugType = "Floating point variable used as loop counter";
333
334
PathDiagnosticLocation FSLoc =
335
PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
336
BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
337
bugType, "Security", os.str(),
338
FSLoc, 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
347
void 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(2)->isIntegralOrUnscopedEnumerationType())
371
return;
372
373
// Issue a warning.
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()'",
378
"Security",
379
"The bcmp() function is obsoleted by memcmp().",
380
CELoc, 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
389
void 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(2)->isIntegralOrUnscopedEnumerationType())
413
return;
414
415
// Issue a warning.
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()'",
420
"Security",
421
"The bcopy() function is obsoleted by memcpy() "
422
"or memmove().",
423
CELoc, 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
432
void 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(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(1)->isIntegralOrUnscopedEnumerationType())
454
return;
455
456
// Issue a warning.
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()'",
461
"Security",
462
"The bzero() function is obsoleted by memset().",
463
CELoc, 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
474
void 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(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(CE, BR.getSourceManager(), AC);
497
BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
498
"Potential buffer overflow in call to 'gets'",
499
"Security",
500
"Call to function 'gets' is extremely insecure as it can "
501
"always result in a buffer overflow",
502
CELoc, CE->getCallee()->getSourceRange());
503
}
504
505
//===----------------------------------------------------------------------===//
506
// Check: Any use of 'getpwd' is insecure.
507
// CWE-477: Use of Obsolete Functions
508
//===----------------------------------------------------------------------===//
509
510
void 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(0)->isIntegralOrUnscopedEnumerationType())
524
return;
525
526
// Verify the second argument type is char*.
527
const PointerType *PT = FPT->getParamType(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(CE, BR.getSourceManager(), AC);
537
BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
538
"Potential buffer overflow in call to 'getpw'",
539
"Security",
540
"The getpw() function is dangerous as it may overflow the "
541
"provided buffer. It is obsoleted by getpwuid().",
542
CELoc, 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
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);
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(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(CE, BR.getSourceManager(), AC);
578
BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
579
"Potential insecure temporary file in call 'mktemp'",
580
"Security",
581
"Call to function 'mktemp' is insecure as it always "
582
"creates or uses insecure temporary file. Use 'mkstemp' "
583
"instead",
584
CELoc, CE->getCallee()->getSourceRange());
585
}
586
587
//===----------------------------------------------------------------------===//
588
// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
589
//===----------------------------------------------------------------------===//
590
591
void 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("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));
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>(CE->getArg((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((unsigned)ArgSuffix.second);
630
Expr::EvalResult EVResult;
631
if (!suffixEx->EvaluateAsInt(EVResult, 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(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'";
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(AC->getDecl(), filter.checkName_mkstemp,
665
"Insecure temporary file creation", "Security",
666
out.str(), CELoc, 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
676
void 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(0)->IgnoreImpCasts(),
684
*Source = CE->getArg(1)->IgnoreImpCasts();
685
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)
690
return;
691
}
692
}
693
694
// Issue a warning.
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 "
699
"call 'strcpy'",
700
"Security",
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());
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
715
void 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(CE, BR.getSourceManager(), AC);
725
BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
726
"Potential insecure memory buffer bounds restriction in "
727
"call 'strcat'",
728
"Security",
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());
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
752
void 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("__builtin_");
766
767
int ArgIndex =
768
llvm::StringSwitch<int>(Name)
769
.Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
770
.Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
771
"swscanf", "vsscanf", "vswscanf", 1)
772
.Cases("sprintf", "vsprintf", "fprintf", 1)
773
.Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
774
"memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
775
.Default(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>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
786
if (FormatString && !FormatString->getString().contains("%s") &&
787
!FormatString->getString().contains("%["))
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(CE, BR.getSourceManager(), AC);
812
BR.EmitBasicReport(AC->getDecl(),
813
filter.checkName_DeprecatedOrUnsafeBufferHandling,
814
Out1.str(), "Security", Out2.str(), CELoc,
815
CE->getCallee()->getSourceRange());
816
}
817
818
//===----------------------------------------------------------------------===//
819
// Common check for str* functions with no bounds parameters.
820
//===----------------------------------------------------------------------===//
821
822
bool 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
858
void 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(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(CE, BR.getSourceManager(), AC);
891
BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
892
"Security", os2.str(), CELoc,
893
CE->getCallee()->getSourceRange());
894
}
895
896
// See justification for rand().
897
void 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(CE, BR.getSourceManager(), AC);
912
BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
913
"'random' is not a secure random number generator",
914
"Security",
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());
918
}
919
920
//===----------------------------------------------------------------------===//
921
// Check: 'vfork' should not be used.
922
// POS33-C: Do not use vfork().
923
//===----------------------------------------------------------------------===//
924
925
void 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(CE, BR.getSourceManager(), AC);
932
BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
933
"Potential insecure implementation-specific behavior in "
934
"call 'vfork'",
935
"Security",
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());
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
949
void 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(ME, BR.getSourceManager(), AC);
984
BR.EmitBasicReport(
985
AC->getDecl(), filter.checkName_decodeValueOfObjCType,
986
"Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
987
"Deprecated method '-decodeValueOfObjCType:at:' is insecure "
988
"as it can lead to potential buffer overflows. Use the safer "
989
"'-decodeValueOfObjCType:at:size:' method.",
990
MELoc, 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
1008
void 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(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(CE, BR.getSourceManager(), AC);
1063
BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1064
"Security", os2.str(), CELoc,
1065
CE->getCallee()->getSourceRange());
1066
}
1067
1068
//===----------------------------------------------------------------------===//
1069
// SecuritySyntaxChecker
1070
//===----------------------------------------------------------------------===//
1071
1072
namespace {
1073
class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1074
public:
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(D->getBody());
1081
}
1082
};
1083
}
1084
1085
void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1086
mgr.registerChecker<SecuritySyntaxChecker>();
1087
}
1088
1089
bool 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
1102
REGISTER_CHECKER(bcmp)
1103
REGISTER_CHECKER(bcopy)
1104
REGISTER_CHECKER(bzero)
1105
REGISTER_CHECKER(gets)
1106
REGISTER_CHECKER(getpw)
1107
REGISTER_CHECKER(mkstemp)
1108
REGISTER_CHECKER(mktemp)
1109
REGISTER_CHECKER(strcpy)
1110
REGISTER_CHECKER(rand)
1111
REGISTER_CHECKER(vfork)
1112
REGISTER_CHECKER(FloatLoopCounter)
1113
REGISTER_CHECKER(UncheckedReturn)
1114
REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1115
REGISTER_CHECKER(decodeValueOfObjCType)
1116
1117