/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 144 | static bool isArc4RandomAvailable(const ASTContext &Ctx) { |
28 | 144 | const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); |
29 | 144 | return T.getVendor() == llvm::Triple::Apple || |
30 | 144 | T.isOSFreeBSD()1 || |
31 | 144 | T.isOSNetBSD()1 || |
32 | 144 | T.isOSOpenBSD()1 || |
33 | 144 | T.isOSDragonFly()1 ; |
34 | 144 | } |
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 | 144 | : BR(br), AC(ac), II_setid(), |
82 | 144 | CheckRand(isArc4RandomAvailable(BR.getContext())), |
83 | 144 | 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 | 3.34k | 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 | 3.75k | void WalkAST::VisitChildren(Stmt *S) { |
126 | 3.75k | for (Stmt *Child : S->children()) |
127 | 3.21k | if (Child) |
128 | 3.17k | Visit(Child); |
129 | 3.75k | } |
130 | | |
131 | 366 | void WalkAST::VisitCallExpr(CallExpr *CE) { |
132 | | // Get the callee. |
133 | 366 | const FunctionDecl *FD = CE->getDirectCallee(); |
134 | | |
135 | 366 | if (!FD) |
136 | 1 | return; |
137 | | |
138 | | // Get the name of the callee. If it's a builtin, strip off the prefix. |
139 | 365 | IdentifierInfo *II = FD->getIdentifier(); |
140 | 365 | if (!II) // if no identifier, not a simple C function |
141 | 0 | return; |
142 | 365 | StringRef Name = II->getName(); |
143 | 365 | if (Name.startswith("__builtin_")) |
144 | 27 | Name = Name.substr(10); |
145 | | |
146 | | // Set the evaluation function by switching on the callee name. |
147 | 365 | FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) |
148 | 365 | .Case("bcmp", &WalkAST::checkCall_bcmp) |
149 | 365 | .Case("bcopy", &WalkAST::checkCall_bcopy) |
150 | 365 | .Case("bzero", &WalkAST::checkCall_bzero) |
151 | 365 | .Case("gets", &WalkAST::checkCall_gets) |
152 | 365 | .Case("getpw", &WalkAST::checkCall_getpw) |
153 | 365 | .Case("mktemp", &WalkAST::checkCall_mktemp) |
154 | 365 | .Case("mkstemp", &WalkAST::checkCall_mkstemp) |
155 | 365 | .Case("mkdtemp", &WalkAST::checkCall_mkstemp) |
156 | 365 | .Case("mkstemps", &WalkAST::checkCall_mkstemp) |
157 | 365 | .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) |
158 | 365 | .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) |
159 | 365 | .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", |
160 | 365 | "vscanf", "vwscanf", "vfscanf", "vfwscanf", |
161 | 365 | &WalkAST::checkDeprecatedOrUnsafeBufferHandling) |
162 | 365 | .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", |
163 | 365 | "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", |
164 | 365 | &WalkAST::checkDeprecatedOrUnsafeBufferHandling) |
165 | 365 | .Cases("strncpy", "strncat", "memset", |
166 | 365 | &WalkAST::checkDeprecatedOrUnsafeBufferHandling) |
167 | 365 | .Case("drand48", &WalkAST::checkCall_rand) |
168 | 365 | .Case("erand48", &WalkAST::checkCall_rand) |
169 | 365 | .Case("jrand48", &WalkAST::checkCall_rand) |
170 | 365 | .Case("lrand48", &WalkAST::checkCall_rand) |
171 | 365 | .Case("mrand48", &WalkAST::checkCall_rand) |
172 | 365 | .Case("nrand48", &WalkAST::checkCall_rand) |
173 | 365 | .Case("lcong48", &WalkAST::checkCall_rand) |
174 | 365 | .Case("rand", &WalkAST::checkCall_rand) |
175 | 365 | .Case("rand_r", &WalkAST::checkCall_rand) |
176 | 365 | .Case("random", &WalkAST::checkCall_random) |
177 | 365 | .Case("vfork", &WalkAST::checkCall_vfork) |
178 | 365 | .Default(nullptr); |
179 | | |
180 | | // If the callee isn't defined, it is not of security concern. |
181 | | // Check and evaluate the call. |
182 | 365 | if (evalFunction) |
183 | 275 | (this->*evalFunction)(CE, FD); |
184 | | |
185 | | // Recurse and check children. |
186 | 365 | VisitChildren(CE); |
187 | 365 | } |
188 | | |
189 | 16 | void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) { |
190 | 16 | MsgCheck evalFunction = |
191 | 16 | llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString()) |
192 | 16 | .Case("decodeValueOfObjCType:at:", |
193 | 16 | &WalkAST::checkMsg_decodeValueOfObjCType) |
194 | 16 | .Default(nullptr); |
195 | | |
196 | 16 | if (evalFunction) |
197 | 8 | (this->*evalFunction)(ME); |
198 | | |
199 | | // Recurse and check children. |
200 | 16 | VisitChildren(ME); |
201 | 16 | } |
202 | | |
203 | 214 | void WalkAST::VisitCompoundStmt(CompoundStmt *S) { |
204 | 214 | for (Stmt *Child : S->children()) |
205 | 652 | if (Child) { |
206 | 652 | if (CallExpr *CE = dyn_cast<CallExpr>(Child)) |
207 | 304 | checkUncheckedReturnValue(CE); |
208 | 652 | Visit(Child); |
209 | 652 | } |
210 | 214 | } |
211 | | |
212 | 32 | void WalkAST::VisitForStmt(ForStmt *FS) { |
213 | 32 | checkLoopConditionForFloat(FS); |
214 | | |
215 | | // Recurse and check children. |
216 | 32 | VisitChildren(FS); |
217 | 32 | } |
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 | 68 | getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { |
228 | 68 | expr = expr->IgnoreParenCasts(); |
229 | | |
230 | 68 | if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { |
231 | 16 | if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp()4 || |
232 | 16 | B->getOpcode() == BO_Comma4 )) |
233 | 0 | return nullptr; |
234 | | |
235 | 16 | if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y)) |
236 | 16 | return lhs; |
237 | | |
238 | 0 | if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y)) |
239 | 0 | return rhs; |
240 | | |
241 | 0 | return nullptr; |
242 | 0 | } |
243 | | |
244 | 52 | if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { |
245 | 32 | const NamedDecl *ND = DR->getDecl(); |
246 | 32 | return ND == x || ND == y4 ? DR : nullptr0 ; |
247 | 32 | } |
248 | | |
249 | 20 | if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) |
250 | 20 | return U->isIncrementDecrementOp() |
251 | 20 | ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr0 ; |
252 | | |
253 | 0 | return nullptr; |
254 | 20 | } |
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 | 32 | void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { |
261 | 32 | if (!filter.check_FloatLoopCounter) |
262 | 0 | return; |
263 | | |
264 | | // Does the loop have a condition? |
265 | 32 | const Expr *condition = FS->getCond(); |
266 | | |
267 | 32 | if (!condition) |
268 | 0 | return; |
269 | | |
270 | | // Does the loop have an increment? |
271 | 32 | const Expr *increment = FS->getInc(); |
272 | | |
273 | 32 | if (!increment) |
274 | 0 | return; |
275 | | |
276 | | // Strip away '()' and casts. |
277 | 32 | condition = condition->IgnoreParenCasts(); |
278 | 32 | increment = increment->IgnoreParenCasts(); |
279 | | |
280 | | // Is the loop condition a comparison? |
281 | 32 | const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); |
282 | | |
283 | 32 | if (!B) |
284 | 0 | return; |
285 | | |
286 | | // Is this a comparison? |
287 | 32 | if (!(B->isRelationalOp() || B->isEqualityOp()0 )) |
288 | 0 | return; |
289 | | |
290 | | // Are we comparing variables? |
291 | 32 | const DeclRefExpr *drLHS = |
292 | 32 | dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); |
293 | 32 | const DeclRefExpr *drRHS = |
294 | 32 | dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); |
295 | | |
296 | | // Does at least one of the variables have a floating point type? |
297 | 32 | drLHS = drLHS && drLHS->getType()->isRealFloatingType()28 ? drLHS28 : nullptr4 ; |
298 | 32 | drRHS = drRHS && drRHS->getType()->isRealFloatingType()4 ? drRHS4 : nullptr28 ; |
299 | | |
300 | 32 | if (!drLHS && !drRHS4 ) |
301 | 0 | return; |
302 | | |
303 | 32 | const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl())28 : nullptr4 ; |
304 | 32 | const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl())4 : nullptr28 ; |
305 | | |
306 | 32 | if (!vdLHS && !vdRHS4 ) |
307 | 0 | return; |
308 | | |
309 | | // Does either variable appear in increment? |
310 | 32 | const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS); |
311 | 32 | if (!drInc) |
312 | 0 | return; |
313 | | |
314 | 32 | const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl()); |
315 | 32 | 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 | 32 | const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS28 : drRHS4 ; |
320 | | |
321 | 32 | SmallVector<SourceRange, 2> ranges; |
322 | 32 | SmallString<256> sbuf; |
323 | 32 | llvm::raw_svector_ostream os(sbuf); |
324 | | |
325 | 32 | os << "Variable '" << drCond->getDecl()->getName() |
326 | 32 | << "' with floating point type '" << drCond->getType() |
327 | 32 | << "' should not be used as a loop counter"; |
328 | | |
329 | 32 | ranges.push_back(drCond->getSourceRange()); |
330 | 32 | ranges.push_back(drInc->getSourceRange()); |
331 | | |
332 | 32 | const char *bugType = "Floating point variable used as loop counter"; |
333 | | |
334 | 32 | PathDiagnosticLocation FSLoc = |
335 | 32 | PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); |
336 | 32 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter, |
337 | 32 | bugType, "Security", os.str(), |
338 | 32 | FSLoc, ranges); |
339 | 32 | } |
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 | 4 | void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) { |
348 | 4 | if (!filter.check_bcmp) |
349 | 0 | return; |
350 | | |
351 | 4 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
352 | 4 | if (!FPT) |
353 | 0 | return; |
354 | | |
355 | | // Verify that the function takes three arguments. |
356 | 4 | if (FPT->getNumParams() != 3) |
357 | 0 | return; |
358 | | |
359 | 12 | for (int i = 0; 4 i < 2; i++8 ) { |
360 | | // Verify the first and second argument type is void*. |
361 | 8 | const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); |
362 | 8 | if (!PT) |
363 | 0 | return; |
364 | | |
365 | 8 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) |
366 | 0 | return; |
367 | 8 | } |
368 | | |
369 | | // Verify the third argument type is integer. |
370 | 4 | if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) |
371 | 0 | return; |
372 | | |
373 | | // Issue a warning. |
374 | 4 | PathDiagnosticLocation CELoc = |
375 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
376 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp, |
377 | 4 | "Use of deprecated function in call to 'bcmp()'", |
378 | 4 | "Security", |
379 | 4 | "The bcmp() function is obsoleted by memcmp().", |
380 | 4 | CELoc, CE->getCallee()->getSourceRange()); |
381 | 4 | } |
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 | 4 | void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) { |
390 | 4 | if (!filter.check_bcopy) |
391 | 0 | return; |
392 | | |
393 | 4 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
394 | 4 | if (!FPT) |
395 | 0 | return; |
396 | | |
397 | | // Verify that the function takes three arguments. |
398 | 4 | if (FPT->getNumParams() != 3) |
399 | 0 | return; |
400 | | |
401 | 12 | for (int i = 0; 4 i < 2; i++8 ) { |
402 | | // Verify the first and second argument type is void*. |
403 | 8 | const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); |
404 | 8 | if (!PT) |
405 | 0 | return; |
406 | | |
407 | 8 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) |
408 | 0 | return; |
409 | 8 | } |
410 | | |
411 | | // Verify the third argument type is integer. |
412 | 4 | if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) |
413 | 0 | return; |
414 | | |
415 | | // Issue a warning. |
416 | 4 | PathDiagnosticLocation CELoc = |
417 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
418 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy, |
419 | 4 | "Use of deprecated function in call to 'bcopy()'", |
420 | 4 | "Security", |
421 | 4 | "The bcopy() function is obsoleted by memcpy() " |
422 | 4 | "or memmove().", |
423 | 4 | CELoc, CE->getCallee()->getSourceRange()); |
424 | 4 | } |
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 | 6 | void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) { |
433 | 6 | if (!filter.check_bzero) |
434 | 0 | return; |
435 | | |
436 | 6 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
437 | 6 | if (!FPT) |
438 | 0 | return; |
439 | | |
440 | | // Verify that the function takes two arguments. |
441 | 6 | if (FPT->getNumParams() != 2) |
442 | 0 | return; |
443 | | |
444 | | // Verify the first argument type is void*. |
445 | 6 | const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); |
446 | 6 | if (!PT) |
447 | 0 | return; |
448 | | |
449 | 6 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) |
450 | 0 | return; |
451 | | |
452 | | // Verify the second argument type is integer. |
453 | 6 | if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType()) |
454 | 0 | return; |
455 | | |
456 | | // Issue a warning. |
457 | 6 | PathDiagnosticLocation CELoc = |
458 | 6 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
459 | 6 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero, |
460 | 6 | "Use of deprecated function in call to 'bzero()'", |
461 | 6 | "Security", |
462 | 6 | "The bzero() function is obsoleted by memset().", |
463 | 6 | CELoc, CE->getCallee()->getSourceRange()); |
464 | 6 | } |
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 | 4 | void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { |
475 | 4 | if (!filter.check_gets) |
476 | 0 | return; |
477 | | |
478 | 4 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
479 | 4 | if (!FPT) |
480 | 0 | return; |
481 | | |
482 | | // Verify that the function takes a single argument. |
483 | 4 | if (FPT->getNumParams() != 1) |
484 | 0 | return; |
485 | | |
486 | | // Is the argument a 'char*'? |
487 | 4 | const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); |
488 | 4 | if (!PT) |
489 | 0 | return; |
490 | | |
491 | 4 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) |
492 | 0 | return; |
493 | | |
494 | | // Issue a warning. |
495 | 4 | PathDiagnosticLocation CELoc = |
496 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
497 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets, |
498 | 4 | "Potential buffer overflow in call to 'gets'", |
499 | 4 | "Security", |
500 | 4 | "Call to function 'gets' is extremely insecure as it can " |
501 | 4 | "always result in a buffer overflow", |
502 | 4 | CELoc, CE->getCallee()->getSourceRange()); |
503 | 4 | } |
504 | | |
505 | | //===----------------------------------------------------------------------===// |
506 | | // Check: Any use of 'getpwd' is insecure. |
507 | | // CWE-477: Use of Obsolete Functions |
508 | | //===----------------------------------------------------------------------===// |
509 | | |
510 | 4 | void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { |
511 | 4 | if (!filter.check_getpw) |
512 | 0 | return; |
513 | | |
514 | 4 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
515 | 4 | if (!FPT) |
516 | 0 | return; |
517 | | |
518 | | // Verify that the function takes two arguments. |
519 | 4 | if (FPT->getNumParams() != 2) |
520 | 0 | return; |
521 | | |
522 | | // Verify the first argument type is integer. |
523 | 4 | if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType()) |
524 | 0 | return; |
525 | | |
526 | | // Verify the second argument type is char*. |
527 | 4 | const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>(); |
528 | 4 | if (!PT) |
529 | 0 | return; |
530 | | |
531 | 4 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) |
532 | 0 | return; |
533 | | |
534 | | // Issue a warning. |
535 | 4 | PathDiagnosticLocation CELoc = |
536 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
537 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw, |
538 | 4 | "Potential buffer overflow in call to 'getpw'", |
539 | 4 | "Security", |
540 | 4 | "The getpw() function is dangerous as it may overflow the " |
541 | 4 | "provided buffer. It is obsoleted by getpwuid().", |
542 | 4 | CELoc, CE->getCallee()->getSourceRange()); |
543 | 4 | } |
544 | | |
545 | | //===----------------------------------------------------------------------===// |
546 | | // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). |
547 | | // CWE-377: Insecure Temporary File |
548 | | //===----------------------------------------------------------------------===// |
549 | | |
550 | 4 | void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { |
551 | 4 | 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 | 0 | checkCall_mkstemp(CE, FD); |
555 | 0 | return; |
556 | 0 | } |
557 | | |
558 | 4 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
559 | 4 | if(!FPT) |
560 | 0 | return; |
561 | | |
562 | | // Verify that the function takes a single argument. |
563 | 4 | if (FPT->getNumParams() != 1) |
564 | 0 | return; |
565 | | |
566 | | // Verify that the argument is Pointer Type. |
567 | 4 | const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); |
568 | 4 | if (!PT) |
569 | 0 | return; |
570 | | |
571 | | // Verify that the argument is a 'char*'. |
572 | 4 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) |
573 | 0 | return; |
574 | | |
575 | | // Issue a warning. |
576 | 4 | PathDiagnosticLocation CELoc = |
577 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
578 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp, |
579 | 4 | "Potential insecure temporary file in call 'mktemp'", |
580 | 4 | "Security", |
581 | 4 | "Call to function 'mktemp' is insecure as it always " |
582 | 4 | "creates or uses insecure temporary file. Use 'mkstemp' " |
583 | 4 | "instead", |
584 | 4 | CELoc, CE->getCallee()->getSourceRange()); |
585 | 4 | } |
586 | | |
587 | | //===----------------------------------------------------------------------===// |
588 | | // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. |
589 | | //===----------------------------------------------------------------------===// |
590 | | |
591 | 36 | void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { |
592 | 36 | if (!filter.check_mkstemp) |
593 | 0 | return; |
594 | | |
595 | 36 | StringRef Name = FD->getIdentifier()->getName(); |
596 | 36 | std::pair<signed, signed> ArgSuffix = |
597 | 36 | llvm::StringSwitch<std::pair<signed, signed> >(Name) |
598 | 36 | .Case("mktemp", std::make_pair(0,-1)) |
599 | 36 | .Case("mkstemp", std::make_pair(0,-1)) |
600 | 36 | .Case("mkdtemp", std::make_pair(0,-1)) |
601 | 36 | .Case("mkstemps", std::make_pair(0,1)) |
602 | 36 | .Default(std::make_pair(-1, -1)); |
603 | | |
604 | 36 | assert(ArgSuffix.first >= 0 && "Unsupported function"); |
605 | | |
606 | | // Check if the number of arguments is consistent with out expectations. |
607 | 36 | unsigned numArgs = CE->getNumArgs(); |
608 | 36 | if ((signed) numArgs <= ArgSuffix.first) |
609 | 0 | return; |
610 | | |
611 | 36 | const StringLiteral *strArg = |
612 | 36 | dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) |
613 | 36 | ->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 | 36 | if (!strArg || strArg->getCharByteWidth() != 1) |
619 | 0 | return; |
620 | | |
621 | | // Count the number of X's, taking into account a possible cutoff suffix. |
622 | 36 | StringRef str = strArg->getString(); |
623 | 36 | unsigned numX = 0; |
624 | 36 | unsigned n = str.size(); |
625 | | |
626 | | // Take into account the suffix. |
627 | 36 | unsigned suffix = 0; |
628 | 36 | if (ArgSuffix.second >= 0) { |
629 | 12 | const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); |
630 | 12 | Expr::EvalResult EVResult; |
631 | 12 | if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext())) |
632 | 0 | return; |
633 | 12 | llvm::APSInt Result = EVResult.Val.getInt(); |
634 | | // FIXME: Issue a warning. |
635 | 12 | if (Result.isNegative()) |
636 | 0 | return; |
637 | 12 | suffix = (unsigned) Result.getZExtValue(); |
638 | 12 | n = (n > suffix) ? n - suffix : 00 ; |
639 | 12 | } |
640 | | |
641 | 192 | for (unsigned i = 0; 36 i < n; ++i156 ) |
642 | 156 | if (str[i] == 'X') ++numX; |
643 | | |
644 | 36 | if (numX >= 6) |
645 | 16 | return; |
646 | | |
647 | | // Issue a warning. |
648 | 20 | PathDiagnosticLocation CELoc = |
649 | 20 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
650 | 20 | SmallString<512> buf; |
651 | 20 | llvm::raw_svector_ostream out(buf); |
652 | 20 | out << "Call to '" << Name << "' should have at least 6 'X's in the" |
653 | 20 | " format string to be secure (" << numX << " 'X'"; |
654 | 20 | if (numX != 1) |
655 | 16 | out << 's'; |
656 | 20 | out << " seen"; |
657 | 20 | if (suffix) { |
658 | 8 | out << ", " << suffix << " character"; |
659 | 8 | if (suffix > 1) |
660 | 4 | out << 's'; |
661 | 8 | out << " used as a suffix"; |
662 | 8 | } |
663 | 20 | out << ')'; |
664 | 20 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp, |
665 | 20 | "Insecure temporary file creation", "Security", |
666 | 20 | out.str(), CELoc, strArg->getSourceRange()); |
667 | 20 | } |
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 | 16 | void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { |
677 | 16 | if (!filter.check_strcpy) |
678 | 0 | return; |
679 | | |
680 | 16 | if (!checkCall_strCommon(CE, FD)) |
681 | 0 | return; |
682 | | |
683 | 16 | const auto *Target = CE->getArg(0)->IgnoreImpCasts(), |
684 | 16 | *Source = CE->getArg(1)->IgnoreImpCasts(); |
685 | | |
686 | 16 | if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) { |
687 | 16 | uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8; |
688 | 16 | if (const auto *String = dyn_cast<StringLiteral>(Source)) { |
689 | 12 | if (ArraySize >= String->getLength() + 1) |
690 | 8 | return; |
691 | 12 | } |
692 | 16 | } |
693 | | |
694 | | // Issue a warning. |
695 | 8 | PathDiagnosticLocation CELoc = |
696 | 8 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
697 | 8 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, |
698 | 8 | "Potential insecure memory buffer bounds restriction in " |
699 | 8 | "call 'strcpy'", |
700 | 8 | "Security", |
701 | 8 | "Call to function 'strcpy' is insecure as it does not " |
702 | 8 | "provide bounding of the memory buffer. Replace " |
703 | 8 | "unbounded copy functions with analogous functions that " |
704 | 8 | "support length arguments such as 'strlcpy'. CWE-119.", |
705 | 8 | CELoc, CE->getCallee()->getSourceRange()); |
706 | 8 | } |
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 | 4 | void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { |
716 | 4 | if (!filter.check_strcpy) |
717 | 0 | return; |
718 | | |
719 | 4 | if (!checkCall_strCommon(CE, FD)) |
720 | 0 | return; |
721 | | |
722 | | // Issue a warning. |
723 | 4 | PathDiagnosticLocation CELoc = |
724 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
725 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, |
726 | 4 | "Potential insecure memory buffer bounds restriction in " |
727 | 4 | "call 'strcat'", |
728 | 4 | "Security", |
729 | 4 | "Call to function 'strcat' is insecure as it does not " |
730 | 4 | "provide bounding of the memory buffer. Replace " |
731 | 4 | "unbounded copy functions with analogous functions that " |
732 | 4 | "support length arguments such as 'strlcat'. CWE-119.", |
733 | 4 | CELoc, CE->getCallee()->getSourceRange()); |
734 | 4 | } |
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 | | // is deprecated since C11. |
742 | | // |
743 | | // Use of 'sprintf', '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 | 125 | const FunctionDecl *FD) { |
754 | 125 | if (!filter.check_DeprecatedOrUnsafeBufferHandling) |
755 | 0 | return; |
756 | | |
757 | 125 | if (!BR.getContext().getLangOpts().C11) |
758 | 3 | return; |
759 | | |
760 | | // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size |
761 | | // restrictions). |
762 | 122 | enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; |
763 | | |
764 | 122 | StringRef Name = FD->getIdentifier()->getName(); |
765 | 122 | if (Name.startswith("__builtin_")) |
766 | 6 | Name = Name.substr(10); |
767 | | |
768 | 122 | int ArgIndex = |
769 | 122 | llvm::StringSwitch<int>(Name) |
770 | 122 | .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) |
771 | 122 | .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", |
772 | 122 | "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) |
773 | 122 | .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", |
774 | 122 | "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) |
775 | 122 | .Default(UNKNOWN_CALL); |
776 | | |
777 | 122 | assert(ArgIndex != UNKNOWN_CALL && "Unsupported function"); |
778 | 122 | bool BoundsProvided = ArgIndex == DEPR_ONLY; |
779 | | |
780 | 122 | 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 | 80 | auto FormatString = |
785 | 80 | dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts()); |
786 | 80 | if (FormatString && !FormatString->getString().contains("%s")40 && |
787 | 80 | !FormatString->getString().contains("%[")28 ) |
788 | 28 | BoundsProvided = true; |
789 | 80 | } |
790 | | |
791 | 122 | SmallString<128> Buf1; |
792 | 122 | SmallString<512> Buf2; |
793 | 122 | llvm::raw_svector_ostream Out1(Buf1); |
794 | 122 | llvm::raw_svector_ostream Out2(Buf2); |
795 | | |
796 | 122 | Out1 << "Potential insecure memory buffer bounds restriction in call '" |
797 | 122 | << Name << "'"; |
798 | 122 | Out2 << "Call to function '" << Name |
799 | 122 | << "' is insecure as it does not provide "; |
800 | | |
801 | 122 | if (!BoundsProvided) { |
802 | 52 | Out2 << "bounding of the memory buffer or "; |
803 | 52 | } |
804 | | |
805 | 122 | Out2 << "security checks introduced " |
806 | 122 | "in the C11 standard. Replace with analogous functions that " |
807 | 122 | "support length arguments or provides boundary checks such as '" |
808 | 122 | << Name << "_s' in case of C11"; |
809 | | |
810 | 122 | PathDiagnosticLocation CELoc = |
811 | 122 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
812 | 122 | BR.EmitBasicReport(AC->getDecl(), |
813 | 122 | filter.checkName_DeprecatedOrUnsafeBufferHandling, |
814 | 122 | Out1.str(), "Security", Out2.str(), CELoc, |
815 | 122 | CE->getCallee()->getSourceRange()); |
816 | 122 | } |
817 | | |
818 | | //===----------------------------------------------------------------------===// |
819 | | // Common check for str* functions with no bounds parameters. |
820 | | //===----------------------------------------------------------------------===// |
821 | | |
822 | 20 | bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { |
823 | 20 | const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); |
824 | 20 | if (!FPT) |
825 | 0 | return false; |
826 | | |
827 | | // Verify the function takes two arguments, three in the _chk version. |
828 | 20 | int numArgs = FPT->getNumParams(); |
829 | 20 | if (numArgs != 2 && numArgs != 310 ) |
830 | 0 | return false; |
831 | | |
832 | | // Verify the type for both arguments. |
833 | 60 | for (int i = 0; 20 i < 2; i++40 ) { |
834 | | // Verify that the arguments are pointers. |
835 | 40 | const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); |
836 | 40 | if (!PT) |
837 | 0 | return false; |
838 | | |
839 | | // Verify that the argument is a 'char*'. |
840 | 40 | if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) |
841 | 0 | return false; |
842 | 40 | } |
843 | | |
844 | 20 | return true; |
845 | 20 | } |
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 | 45 | void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { |
859 | 45 | if (!filter.check_rand || !CheckRand) |
860 | 9 | return; |
861 | | |
862 | 36 | const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); |
863 | 36 | if (!FTP) |
864 | 0 | return; |
865 | | |
866 | 36 | if (FTP->getNumParams() == 1) { |
867 | | // Is the argument an 'unsigned short *'? |
868 | | // (Actually any integer type is allowed.) |
869 | 20 | const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>(); |
870 | 20 | if (!PT) |
871 | 0 | return; |
872 | | |
873 | 20 | if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) |
874 | 0 | return; |
875 | 20 | } else if (16 FTP->getNumParams() != 016 ) |
876 | 0 | return; |
877 | | |
878 | | // Issue a warning. |
879 | 36 | SmallString<256> buf1; |
880 | 36 | llvm::raw_svector_ostream os1(buf1); |
881 | 36 | os1 << '\'' << *FD << "' is a poor random number generator"; |
882 | | |
883 | 36 | SmallString<256> buf2; |
884 | 36 | llvm::raw_svector_ostream os2(buf2); |
885 | 36 | os2 << "Function '" << *FD |
886 | 36 | << "' is obsolete because it implements a poor random number generator." |
887 | 36 | << " Use 'arc4random' instead"; |
888 | | |
889 | 36 | PathDiagnosticLocation CELoc = |
890 | 36 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
891 | 36 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(), |
892 | 36 | "Security", os2.str(), CELoc, |
893 | 36 | CE->getCallee()->getSourceRange()); |
894 | 36 | } |
895 | | |
896 | | // See justification for rand(). |
897 | 5 | void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { |
898 | 5 | if (!CheckRand || !filter.check_rand4 ) |
899 | 1 | return; |
900 | | |
901 | 4 | const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); |
902 | 4 | if (!FTP) |
903 | 0 | return; |
904 | | |
905 | | // Verify that the function takes no argument. |
906 | 4 | if (FTP->getNumParams() != 0) |
907 | 0 | return; |
908 | | |
909 | | // Issue a warning. |
910 | 4 | PathDiagnosticLocation CELoc = |
911 | 4 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
912 | 4 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, |
913 | 4 | "'random' is not a secure random number generator", |
914 | 4 | "Security", |
915 | 4 | "The 'random' function produces a sequence of values that " |
916 | 4 | "an adversary may be able to predict. Use 'arc4random' " |
917 | 4 | "instead", CELoc, CE->getCallee()->getSourceRange()); |
918 | 4 | } |
919 | | |
920 | | //===----------------------------------------------------------------------===// |
921 | | // Check: 'vfork' should not be used. |
922 | | // POS33-C: Do not use vfork(). |
923 | | //===----------------------------------------------------------------------===// |
924 | | |
925 | 18 | void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { |
926 | 18 | if (!filter.check_vfork) |
927 | 0 | return; |
928 | | |
929 | | // All calls to vfork() are insecure, issue a warning. |
930 | 18 | PathDiagnosticLocation CELoc = |
931 | 18 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
932 | 18 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork, |
933 | 18 | "Potential insecure implementation-specific behavior in " |
934 | 18 | "call 'vfork'", |
935 | 18 | "Security", |
936 | 18 | "Call to function 'vfork' is insecure as it can lead to " |
937 | 18 | "denial of service situations in the parent process. " |
938 | 18 | "Replace calls to vfork with calls to the safer " |
939 | 18 | "'posix_spawn' function", |
940 | 18 | CELoc, CE->getCallee()->getSourceRange()); |
941 | 18 | } |
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 | 8 | void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) { |
950 | 8 | if (!filter.check_decodeValueOfObjCType) |
951 | 0 | 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 | 8 | const TargetInfo &TI = AC->getASTContext().getTargetInfo(); |
957 | 8 | const llvm::Triple &T = TI.getTriple(); |
958 | 8 | const VersionTuple &VT = TI.getPlatformMinVersion(); |
959 | 8 | switch (T.getOS()) { |
960 | 2 | case llvm::Triple::IOS: |
961 | 2 | if (VT < VersionTuple(11, 0)) |
962 | 1 | return; |
963 | 1 | break; |
964 | 2 | case llvm::Triple::MacOSX: |
965 | 2 | if (VT < VersionTuple(10, 13)) |
966 | 1 | return; |
967 | 1 | break; |
968 | 2 | case llvm::Triple::WatchOS: |
969 | 2 | if (VT < VersionTuple(4, 0)) |
970 | 1 | return; |
971 | 1 | break; |
972 | 2 | case llvm::Triple::TvOS: |
973 | 2 | if (VT < VersionTuple(11, 0)) |
974 | 1 | return; |
975 | 1 | break; |
976 | 1 | default: |
977 | 0 | return; |
978 | 8 | } |
979 | | |
980 | 4 | PathDiagnosticLocation MELoc = |
981 | 4 | PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC); |
982 | 4 | BR.EmitBasicReport( |
983 | 4 | AC->getDecl(), filter.checkName_decodeValueOfObjCType, |
984 | 4 | "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security", |
985 | 4 | "Deprecated method '-decodeValueOfObjCType:at:' is insecure " |
986 | 4 | "as it can lead to potential buffer overflows. Use the safer " |
987 | 4 | "'-decodeValueOfObjCType:at:size:' method.", |
988 | 4 | MELoc, ME->getSourceRange()); |
989 | 4 | } |
990 | | |
991 | | //===----------------------------------------------------------------------===// |
992 | | // Check: The caller should always verify that the privileges |
993 | | // were dropped successfully. |
994 | | // |
995 | | // Some library functions, like setuid() and setgid(), should always be used |
996 | | // with a check of the return value to verify that the function completed |
997 | | // successfully. If the drop fails, the software will continue to run |
998 | | // with the raised privileges, which might provide additional access |
999 | | // to unprivileged users. |
1000 | | // |
1001 | | // (Note that this check predates __attribute__((warn_unused_result)). |
1002 | | // Do we still need it now that we have a compiler warning for this? |
1003 | | // Are these standard functions already annotated this way?) |
1004 | | //===----------------------------------------------------------------------===// |
1005 | | |
1006 | 304 | void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { |
1007 | 304 | if (!filter.check_UncheckedReturn) |
1008 | 14 | return; |
1009 | | |
1010 | 290 | const FunctionDecl *FD = CE->getDirectCallee(); |
1011 | 290 | if (!FD) |
1012 | 1 | return; |
1013 | | |
1014 | 289 | if (II_setid[0] == nullptr) { |
1015 | 77 | static const char * const identifiers[num_setids] = { |
1016 | 77 | "setuid", "setgid", "seteuid", "setegid", |
1017 | 77 | "setreuid", "setregid" |
1018 | 77 | }; |
1019 | | |
1020 | 539 | for (size_t i = 0; i < num_setids; i++462 ) |
1021 | 462 | II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); |
1022 | 77 | } |
1023 | | |
1024 | 289 | const IdentifierInfo *id = FD->getIdentifier(); |
1025 | 289 | size_t identifierid; |
1026 | | |
1027 | 1.96k | for (identifierid = 0; identifierid < num_setids; identifierid++1.67k ) |
1028 | 1.69k | if (id == II_setid[identifierid]) |
1029 | 16 | break; |
1030 | | |
1031 | 289 | if (identifierid >= num_setids) |
1032 | 273 | return; |
1033 | | |
1034 | 16 | const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); |
1035 | 16 | if (!FTP) |
1036 | 0 | return; |
1037 | | |
1038 | | // Verify that the function takes one or two arguments (depending on |
1039 | | // the function). |
1040 | 16 | if (FTP->getNumParams() != (identifierid < 4 ? 18 : 28 )) |
1041 | 0 | return; |
1042 | | |
1043 | | // The arguments must be integers. |
1044 | 40 | for (unsigned i = 0; 16 i < FTP->getNumParams(); i++24 ) |
1045 | 24 | if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType()) |
1046 | 0 | return; |
1047 | | |
1048 | | // Issue a warning. |
1049 | 16 | SmallString<256> buf1; |
1050 | 16 | llvm::raw_svector_ostream os1(buf1); |
1051 | 16 | os1 << "Return value is not checked in call to '" << *FD << '\''; |
1052 | | |
1053 | 16 | SmallString<256> buf2; |
1054 | 16 | llvm::raw_svector_ostream os2(buf2); |
1055 | 16 | os2 << "The return value from the call to '" << *FD |
1056 | 16 | << "' is not checked. If an error occurs in '" << *FD |
1057 | 16 | << "', the following code may execute with unexpected privileges"; |
1058 | | |
1059 | 16 | PathDiagnosticLocation CELoc = |
1060 | 16 | PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); |
1061 | 16 | BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(), |
1062 | 16 | "Security", os2.str(), CELoc, |
1063 | 16 | CE->getCallee()->getSourceRange()); |
1064 | 16 | } |
1065 | | |
1066 | | //===----------------------------------------------------------------------===// |
1067 | | // SecuritySyntaxChecker |
1068 | | //===----------------------------------------------------------------------===// |
1069 | | |
1070 | | namespace { |
1071 | | class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { |
1072 | | public: |
1073 | | ChecksFilter filter; |
1074 | | |
1075 | | void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, |
1076 | 144 | BugReporter &BR) const { |
1077 | 144 | WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); |
1078 | 144 | walker.Visit(D->getBody()); |
1079 | 144 | } |
1080 | | }; |
1081 | | } |
1082 | | |
1083 | 50 | void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { |
1084 | 50 | mgr.registerChecker<SecuritySyntaxChecker>(); |
1085 | 50 | } |
1086 | | |
1087 | 742 | bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) { |
1088 | 742 | return true; |
1089 | 742 | } |
1090 | | |
1091 | | #define REGISTER_CHECKER(name) \ |
1092 | 350 | void ento::register##name(CheckerManager &mgr) { \ |
1093 | 350 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ |
1094 | 350 | checker->filter.check_##name = true; \ |
1095 | 350 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ |
1096 | 350 | } \ clang::ento::registerbcmp(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 9 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 9 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 9 | checker->filter.check_##name = true; \ | 1095 | 9 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 9 | } \ |
clang::ento::registerbcopy(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 9 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 9 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 9 | checker->filter.check_##name = true; \ | 1095 | 9 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 9 | } \ |
clang::ento::registerbzero(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 9 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 9 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 9 | checker->filter.check_##name = true; \ | 1095 | 9 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 9 | } \ |
clang::ento::registergets(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 40 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 40 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 40 | checker->filter.check_##name = true; \ | 1095 | 40 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 40 | } \ |
clang::ento::registergetpw(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 40 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 40 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 40 | checker->filter.check_##name = true; \ | 1095 | 40 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 40 | } \ |
clang::ento::registermkstemp(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 40 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 40 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 40 | checker->filter.check_##name = true; \ | 1095 | 40 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 40 | } \ |
clang::ento::registermktemp(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 40 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 40 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 40 | checker->filter.check_##name = true; \ | 1095 | 40 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 40 | } \ |
clang::ento::registerstrcpy(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 9 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 9 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 9 | checker->filter.check_##name = true; \ | 1095 | 9 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 9 | } \ |
clang::ento::registerrand(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 9 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 9 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 9 | checker->filter.check_##name = true; \ | 1095 | 9 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 9 | } \ |
clang::ento::registervfork(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 42 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 42 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 42 | checker->filter.check_##name = true; \ | 1095 | 42 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 42 | } \ |
clang::ento::registerFloatLoopCounter(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 6 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 6 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 6 | checker->filter.check_##name = true; \ | 1095 | 6 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 6 | } \ |
clang::ento::registerUncheckedReturn(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 40 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 40 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 40 | checker->filter.check_##name = true; \ | 1095 | 40 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 40 | } \ |
clang::ento::registerDeprecatedOrUnsafeBufferHandling(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 9 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 9 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 9 | checker->filter.check_##name = true; \ | 1095 | 9 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 9 | } \ |
clang::ento::registerdecodeValueOfObjCType(clang::ento::CheckerManager&) Line | Count | Source | 1092 | 48 | void ento::register##name(CheckerManager &mgr) { \ | 1093 | 48 | SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ | 1094 | 48 | checker->filter.check_##name = true; \ | 1095 | 48 | checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ | 1096 | 48 | } \ |
|
1097 | | \ |
1098 | 724 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } clang::ento::shouldRegisterbcmp(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 18 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterbcopy(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 18 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterbzero(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 18 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegistergets(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 84 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegistergetpw(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 84 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegistermkstemp(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 84 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegistermktemp(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 84 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterstrcpy(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 18 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterrand(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 18 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegistervfork(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 88 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterFloatLoopCounter(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 12 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterUncheckedReturn(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 84 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterDeprecatedOrUnsafeBufferHandling(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 18 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
clang::ento::shouldRegisterdecodeValueOfObjCType(clang::ento::CheckerManager const&) Line | Count | Source | 1098 | 96 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } |
|
1099 | | |
1100 | | REGISTER_CHECKER(bcmp) |
1101 | | REGISTER_CHECKER(bcopy) |
1102 | | REGISTER_CHECKER(bzero) |
1103 | | REGISTER_CHECKER(gets) |
1104 | | REGISTER_CHECKER(getpw) |
1105 | | REGISTER_CHECKER(mkstemp) |
1106 | | REGISTER_CHECKER(mktemp) |
1107 | | REGISTER_CHECKER(strcpy) |
1108 | | REGISTER_CHECKER(rand) |
1109 | | REGISTER_CHECKER(vfork) |
1110 | | REGISTER_CHECKER(FloatLoopCounter) |
1111 | | REGISTER_CHECKER(UncheckedReturn) |
1112 | | REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) |
1113 | | REGISTER_CHECKER(decodeValueOfObjCType) |