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