blob: 31864e74550dec660558146988248bf2108dbdbd [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"
Ted Kremenek5d074012010-04-08 21:54:13 +000016#include "clang/Analysis/Support/Optional.h"
Ted Kremenekbace4ba2010-04-08 22:15:34 +000017#include "clang/Basic/TargetInfo.h"
18#include "clang/Checker/BugReporter/BugType.h"
19#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Benjamin Kramer5e2d2c22010-03-27 21:19:47 +000020#include "llvm/ADT/StringSwitch.h"
Ted Kremenek381d1bf2010-02-25 00:20:35 +000021#include <fcntl.h>
22
23using namespace clang;
24
25namespace {
26class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> {
27 enum SubChecks {
28 OpenFn = 0,
Ted Kremenek99d98382010-04-08 19:53:31 +000029 PthreadOnceFn = 1,
Ted Kremenek381d1bf2010-02-25 00:20:35 +000030 NumChecks
31 };
32
33 BugType *BTypes[NumChecks];
34
35public:
Ted Kremenekbace4ba2010-04-08 22:15:34 +000036 Optional<uint64_t> Val_O_CREAT;
37
38public:
Ted Kremenek381d1bf2010-02-25 00:20:35 +000039 UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); }
40 static void *getTag() { static unsigned tag = 0; return &tag; }
41
42 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
43};
44} //end anonymous namespace
45
46void clang::RegisterUnixAPIChecker(GRExprEngine &Eng) {
47 Eng.registerCheck(new UnixAPIChecker());
48}
49
50//===----------------------------------------------------------------------===//
51// Utility functions.
52//===----------------------------------------------------------------------===//
53
54static inline void LazyInitialize(BugType *&BT, const char *name) {
55 if (BT)
56 return;
57 BT = new BugType(name, "Unix API");
58}
59
60//===----------------------------------------------------------------------===//
61// "open" (man 2 open)
62//===----------------------------------------------------------------------===//
63
Ted Kremenekbace4ba2010-04-08 22:15:34 +000064static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC,
65 const CallExpr *CE, BugType *&BT) {
66 // The definition of O_CREAT is platform specific. We need a better way
67 // of querying this information from the checking environment.
68 if (!UC.Val_O_CREAT.hasValue()) {
69 if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple)
70 UC.Val_O_CREAT = 0x0200;
71 else {
72 // FIXME: We need a more general way of getting the O_CREAT value.
73 // We could possibly grovel through the preprocessor state, but
74 // that would require passing the Preprocessor object to the GRExprEngine.
75 return;
76 }
77 }
78
Ted Kremenek381d1bf2010-02-25 00:20:35 +000079 LazyInitialize(BT, "Improper use of 'open'");
80
81 // Look at the 'oflags' argument for the O_CREAT flag.
82 const GRState *state = C.getState();
83
84 if (CE->getNumArgs() < 2) {
85 // The frontend should issue a warning for this case, so this is a sanity
86 // check.
87 return;
88 }
89
90 // Now check if oflags has O_CREAT set.
91 const Expr *oflagsEx = CE->getArg(1);
92 const SVal V = state->getSVal(oflagsEx);
93 if (!isa<NonLoc>(V)) {
94 // The case where 'V' can be a location can only be due to a bad header,
95 // so in this case bail out.
96 return;
97 }
98 NonLoc oflags = cast<NonLoc>(V);
99 NonLoc ocreateFlag =
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000100 cast<NonLoc>(C.getValueManager().makeIntVal(UC.Val_O_CREAT.getValue(),
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000101 oflagsEx->getType()));
102 SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BinaryOperator::And,
103 oflags, ocreateFlag,
104 oflagsEx->getType());
105 if (maskedFlagsUC.isUnknownOrUndef())
106 return;
107 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
108
109 // Check if maskedFlags is non-zero.
110 const GRState *trueState, *falseState;
111 llvm::tie(trueState, falseState) = state->Assume(maskedFlags);
112
113 // Only emit an error if the value of 'maskedFlags' is properly
114 // constrained;
115 if (!(trueState && !falseState))
116 return;
117
118 if (CE->getNumArgs() < 3) {
119 ExplodedNode *N = C.GenerateSink(trueState);
Ted Kremenekc757d792010-02-25 05:44:05 +0000120 if (!N)
121 return;
122
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000123 EnhancedBugReport *report =
124 new EnhancedBugReport(*BT,
125 "Call to 'open' requires a third argument when "
126 "the 'O_CREAT' flag is set", N);
127 report->addRange(oflagsEx->getSourceRange());
128 C.EmitReport(report);
129 }
130}
131
132//===----------------------------------------------------------------------===//
Ted Kremenek99d98382010-04-08 19:53:31 +0000133// pthread_once
134//===----------------------------------------------------------------------===//
135
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000136static void CheckPthreadOnce(CheckerContext &C, UnixAPIChecker &,
137 const CallExpr *CE, BugType *&BT) {
Ted Kremenek99d98382010-04-08 19:53:31 +0000138
139 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
140 // They can possibly be refactored.
141
142 LazyInitialize(BT, "Improper use of 'pthread_once'");
143
144 if (CE->getNumArgs() < 1)
145 return;
146
147 // Check if the first argument is stack allocated. If so, issue a warning
148 // because that's likely to be bad news.
149 const GRState *state = C.getState();
150 const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
151 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
152 return;
153
154 ExplodedNode *N = C.GenerateSink(state);
155 if (!N)
156 return;
157
158 llvm::SmallString<256> S;
159 llvm::raw_svector_ostream os(S);
160 os << "Call to 'pthread_once' uses";
161 if (const VarRegion *VR = dyn_cast<VarRegion>(R))
162 os << " the local variable '" << VR->getDecl()->getName() << '\'';
163 else
164 os << " stack allocated memory";
165 os << " for the \"control\" value. Using such transient memory for "
166 "the control value is potentially dangerous.";
167 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
168 os << " Perhaps you intended to declare the variable as 'static'?";
169
170 EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N);
171 report->addRange(CE->getArg(0)->getSourceRange());
172 C.EmitReport(report);
173}
174
175//===----------------------------------------------------------------------===//
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000176// Central dispatch function.
177//===----------------------------------------------------------------------===//
178
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000179typedef void (*SubChecker)(CheckerContext &C, UnixAPIChecker &UC,
180 const CallExpr *CE, BugType *&BT);
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000181namespace {
182 class SubCheck {
183 SubChecker SC;
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000184 UnixAPIChecker *UC;
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000185 BugType **BT;
186 public:
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000187 SubCheck(SubChecker sc, UnixAPIChecker *uc, BugType *& bt) : SC(sc), UC(uc),
188 BT(&bt) {}
189 SubCheck() : SC(NULL), UC(NULL), BT(NULL) {}
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000190
191 void run(CheckerContext &C, const CallExpr *CE) const {
192 if (SC)
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000193 SC(C, *UC, CE, *BT);
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000194 }
195 };
196} // end anonymous namespace
197
198void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
199 // Get the callee. All the functions we care about are C functions
200 // with simple identifiers.
201 const GRState *state = C.getState();
202 const Expr *Callee = CE->getCallee();
203 const FunctionTextRegion *Fn =
204 dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
205
206 if (!Fn)
207 return;
208
209 const IdentifierInfo *FI = Fn->getDecl()->getIdentifier();
210 if (!FI)
211 return;
212
213 const SubCheck &SC =
214 llvm::StringSwitch<SubCheck>(FI->getName())
Ted Kremenekbace4ba2010-04-08 22:15:34 +0000215 .Case("open", SubCheck(CheckOpen, this, BTypes[OpenFn]))
216 .Case("pthread_once", SubCheck(CheckPthreadOnce, this,
217 BTypes[PthreadOnceFn]))
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000218 .Default(SubCheck());
219
220 SC.run(C, CE);
221}