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