blob: 213e7980053774425329f19407e0a0c34e44d9e1 [file] [log] [blame]
Ted Kremenek381d1bf2010-02-25 00:20:35 +00001//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This defines UnixAPIChecker, which is an assortment of checks on calls
11// to various, widely used UNIX/Posix functions.
12//
13//===----------------------------------------------------------------------===//
14
Ted Kremenek381d1bf2010-02-25 00:20:35 +000015#include "GRExprEngineInternalChecks.h"
Benjamin Kramer5e2d2c22010-03-27 21:19:47 +000016#include "clang/Checker/PathSensitive/CheckerVisitor.h"
17#include "clang/Checker/BugReporter/BugType.h"
Ted Kremenek5d074012010-04-08 21:54:13 +000018#include "clang/Analysis/Support/Optional.h"
Benjamin Kramer5e2d2c22010-03-27 21:19:47 +000019#include "llvm/ADT/StringSwitch.h"
Ted Kremenek381d1bf2010-02-25 00:20:35 +000020#include <fcntl.h>
21
Ted Kremenek5d074012010-04-08 21:54:13 +000022#include "clang/Basic/TargetInfo.h"
23
24
Ted Kremenek381d1bf2010-02-25 00:20:35 +000025using namespace clang;
26
27namespace {
28class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> {
29 enum SubChecks {
30 OpenFn = 0,
Ted Kremenek99d98382010-04-08 19:53:31 +000031 PthreadOnceFn = 1,
Ted Kremenek381d1bf2010-02-25 00:20:35 +000032 NumChecks
33 };
34
35 BugType *BTypes[NumChecks];
36
37public:
38 UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); }
39 static void *getTag() { static unsigned tag = 0; return &tag; }
40
41 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
42};
43} //end anonymous namespace
44
45void clang::RegisterUnixAPIChecker(GRExprEngine &Eng) {
46 Eng.registerCheck(new UnixAPIChecker());
47}
48
49//===----------------------------------------------------------------------===//
50// Utility functions.
51//===----------------------------------------------------------------------===//
52
53static inline void LazyInitialize(BugType *&BT, const char *name) {
54 if (BT)
55 return;
56 BT = new BugType(name, "Unix API");
57}
58
59//===----------------------------------------------------------------------===//
60// "open" (man 2 open)
61//===----------------------------------------------------------------------===//
62
63static void CheckOpen(CheckerContext &C, const CallExpr *CE, BugType *&BT) {
Ted Kremenek5d074012010-04-08 21:54:13 +000064 if (C.getASTContext().Target.getTriple().getVendor() != llvm::Triple::Apple)
65 return;
66
Ted Kremenek381d1bf2010-02-25 00:20:35 +000067 LazyInitialize(BT, "Improper use of 'open'");
68
69 // Look at the 'oflags' argument for the O_CREAT flag.
70 const GRState *state = C.getState();
71
72 if (CE->getNumArgs() < 2) {
73 // The frontend should issue a warning for this case, so this is a sanity
74 // check.
75 return;
76 }
77
78 // Now check if oflags has O_CREAT set.
79 const Expr *oflagsEx = CE->getArg(1);
80 const SVal V = state->getSVal(oflagsEx);
81 if (!isa<NonLoc>(V)) {
82 // The case where 'V' can be a location can only be due to a bad header,
83 // so in this case bail out.
84 return;
85 }
86 NonLoc oflags = cast<NonLoc>(V);
87 NonLoc ocreateFlag =
88 cast<NonLoc>(C.getValueManager().makeIntVal((uint64_t) O_CREAT,
89 oflagsEx->getType()));
90 SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BinaryOperator::And,
91 oflags, ocreateFlag,
92 oflagsEx->getType());
93 if (maskedFlagsUC.isUnknownOrUndef())
94 return;
95 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
96
97 // Check if maskedFlags is non-zero.
98 const GRState *trueState, *falseState;
99 llvm::tie(trueState, falseState) = state->Assume(maskedFlags);
100
101 // Only emit an error if the value of 'maskedFlags' is properly
102 // constrained;
103 if (!(trueState && !falseState))
104 return;
105
106 if (CE->getNumArgs() < 3) {
107 ExplodedNode *N = C.GenerateSink(trueState);
Ted Kremenekc757d792010-02-25 05:44:05 +0000108 if (!N)
109 return;
110
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000111 EnhancedBugReport *report =
112 new EnhancedBugReport(*BT,
113 "Call to 'open' requires a third argument when "
114 "the 'O_CREAT' flag is set", N);
115 report->addRange(oflagsEx->getSourceRange());
116 C.EmitReport(report);
117 }
118}
119
120//===----------------------------------------------------------------------===//
Ted Kremenek99d98382010-04-08 19:53:31 +0000121// pthread_once
122//===----------------------------------------------------------------------===//
123
124static void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE,
125 BugType *&BT) {
126
127 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
128 // They can possibly be refactored.
129
130 LazyInitialize(BT, "Improper use of 'pthread_once'");
131
132 if (CE->getNumArgs() < 1)
133 return;
134
135 // Check if the first argument is stack allocated. If so, issue a warning
136 // because that's likely to be bad news.
137 const GRState *state = C.getState();
138 const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
139 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
140 return;
141
142 ExplodedNode *N = C.GenerateSink(state);
143 if (!N)
144 return;
145
146 llvm::SmallString<256> S;
147 llvm::raw_svector_ostream os(S);
148 os << "Call to 'pthread_once' uses";
149 if (const VarRegion *VR = dyn_cast<VarRegion>(R))
150 os << " the local variable '" << VR->getDecl()->getName() << '\'';
151 else
152 os << " stack allocated memory";
153 os << " for the \"control\" value. Using such transient memory for "
154 "the control value is potentially dangerous.";
155 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
156 os << " Perhaps you intended to declare the variable as 'static'?";
157
158 EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N);
159 report->addRange(CE->getArg(0)->getSourceRange());
160 C.EmitReport(report);
161}
162
163//===----------------------------------------------------------------------===//
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000164// Central dispatch function.
165//===----------------------------------------------------------------------===//
166
167typedef void (*SubChecker)(CheckerContext &C, const CallExpr *CE, BugType *&BT);
168namespace {
169 class SubCheck {
170 SubChecker SC;
171 BugType **BT;
172 public:
173 SubCheck(SubChecker sc, BugType *& bt) : SC(sc), BT(&bt) {}
174 SubCheck() : SC(NULL), BT(NULL) {}
175
176 void run(CheckerContext &C, const CallExpr *CE) const {
177 if (SC)
178 SC(C, CE, *BT);
179 }
180 };
181} // end anonymous namespace
182
183void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
184 // Get the callee. All the functions we care about are C functions
185 // with simple identifiers.
186 const GRState *state = C.getState();
187 const Expr *Callee = CE->getCallee();
188 const FunctionTextRegion *Fn =
189 dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
190
191 if (!Fn)
192 return;
193
194 const IdentifierInfo *FI = Fn->getDecl()->getIdentifier();
195 if (!FI)
196 return;
197
198 const SubCheck &SC =
199 llvm::StringSwitch<SubCheck>(FI->getName())
200 .Case("open", SubCheck(CheckOpen, BTypes[OpenFn]))
Ted Kremenek99d98382010-04-08 19:53:31 +0000201 .Case("pthread_once", SubCheck(CheckPthreadOnce, BTypes[PthreadOnceFn]))
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000202 .Default(SubCheck());
203
204 SC.run(C, CE);
205}