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