blob: 0c9a45f06f04bff5545a28d3c8a0d7644c2f516a [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"
18#include "llvm/ADT/StringSwitch.h"
Ted Kremenek381d1bf2010-02-25 00:20:35 +000019#include <fcntl.h>
20
21using namespace clang;
22
23namespace {
24class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> {
25 enum SubChecks {
26 OpenFn = 0,
Ted Kremenek99d98382010-04-08 19:53:31 +000027 PthreadOnceFn = 1,
Ted Kremenek381d1bf2010-02-25 00:20:35 +000028 NumChecks
29 };
30
31 BugType *BTypes[NumChecks];
32
33public:
34 UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); }
35 static void *getTag() { static unsigned tag = 0; return &tag; }
36
37 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
38};
39} //end anonymous namespace
40
41void clang::RegisterUnixAPIChecker(GRExprEngine &Eng) {
42 Eng.registerCheck(new UnixAPIChecker());
43}
44
45//===----------------------------------------------------------------------===//
46// Utility functions.
47//===----------------------------------------------------------------------===//
48
49static inline void LazyInitialize(BugType *&BT, const char *name) {
50 if (BT)
51 return;
52 BT = new BugType(name, "Unix API");
53}
54
55//===----------------------------------------------------------------------===//
56// "open" (man 2 open)
57//===----------------------------------------------------------------------===//
58
59static void CheckOpen(CheckerContext &C, const CallExpr *CE, BugType *&BT) {
60 LazyInitialize(BT, "Improper use of 'open'");
61
62 // Look at the 'oflags' argument for the O_CREAT flag.
63 const GRState *state = C.getState();
64
65 if (CE->getNumArgs() < 2) {
66 // The frontend should issue a warning for this case, so this is a sanity
67 // check.
68 return;
69 }
70
71 // Now check if oflags has O_CREAT set.
72 const Expr *oflagsEx = CE->getArg(1);
73 const SVal V = state->getSVal(oflagsEx);
74 if (!isa<NonLoc>(V)) {
75 // The case where 'V' can be a location can only be due to a bad header,
76 // so in this case bail out.
77 return;
78 }
79 NonLoc oflags = cast<NonLoc>(V);
80 NonLoc ocreateFlag =
81 cast<NonLoc>(C.getValueManager().makeIntVal((uint64_t) O_CREAT,
82 oflagsEx->getType()));
83 SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BinaryOperator::And,
84 oflags, ocreateFlag,
85 oflagsEx->getType());
86 if (maskedFlagsUC.isUnknownOrUndef())
87 return;
88 DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
89
90 // Check if maskedFlags is non-zero.
91 const GRState *trueState, *falseState;
92 llvm::tie(trueState, falseState) = state->Assume(maskedFlags);
93
94 // Only emit an error if the value of 'maskedFlags' is properly
95 // constrained;
96 if (!(trueState && !falseState))
97 return;
98
99 if (CE->getNumArgs() < 3) {
100 ExplodedNode *N = C.GenerateSink(trueState);
Ted Kremenekc757d792010-02-25 05:44:05 +0000101 if (!N)
102 return;
103
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000104 EnhancedBugReport *report =
105 new EnhancedBugReport(*BT,
106 "Call to 'open' requires a third argument when "
107 "the 'O_CREAT' flag is set", N);
108 report->addRange(oflagsEx->getSourceRange());
109 C.EmitReport(report);
110 }
111}
112
113//===----------------------------------------------------------------------===//
Ted Kremenek99d98382010-04-08 19:53:31 +0000114// pthread_once
115//===----------------------------------------------------------------------===//
116
117static void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE,
118 BugType *&BT) {
119
120 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
121 // They can possibly be refactored.
122
123 LazyInitialize(BT, "Improper use of 'pthread_once'");
124
125 if (CE->getNumArgs() < 1)
126 return;
127
128 // Check if the first argument is stack allocated. If so, issue a warning
129 // because that's likely to be bad news.
130 const GRState *state = C.getState();
131 const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
132 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
133 return;
134
135 ExplodedNode *N = C.GenerateSink(state);
136 if (!N)
137 return;
138
139 llvm::SmallString<256> S;
140 llvm::raw_svector_ostream os(S);
141 os << "Call to 'pthread_once' uses";
142 if (const VarRegion *VR = dyn_cast<VarRegion>(R))
143 os << " the local variable '" << VR->getDecl()->getName() << '\'';
144 else
145 os << " stack allocated memory";
146 os << " for the \"control\" value. Using such transient memory for "
147 "the control value is potentially dangerous.";
148 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
149 os << " Perhaps you intended to declare the variable as 'static'?";
150
151 EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N);
152 report->addRange(CE->getArg(0)->getSourceRange());
153 C.EmitReport(report);
154}
155
156//===----------------------------------------------------------------------===//
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000157// Central dispatch function.
158//===----------------------------------------------------------------------===//
159
160typedef void (*SubChecker)(CheckerContext &C, const CallExpr *CE, BugType *&BT);
161namespace {
162 class SubCheck {
163 SubChecker SC;
164 BugType **BT;
165 public:
166 SubCheck(SubChecker sc, BugType *& bt) : SC(sc), BT(&bt) {}
167 SubCheck() : SC(NULL), BT(NULL) {}
168
169 void run(CheckerContext &C, const CallExpr *CE) const {
170 if (SC)
171 SC(C, CE, *BT);
172 }
173 };
174} // end anonymous namespace
175
176void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
177 // Get the callee. All the functions we care about are C functions
178 // with simple identifiers.
179 const GRState *state = C.getState();
180 const Expr *Callee = CE->getCallee();
181 const FunctionTextRegion *Fn =
182 dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
183
184 if (!Fn)
185 return;
186
187 const IdentifierInfo *FI = Fn->getDecl()->getIdentifier();
188 if (!FI)
189 return;
190
191 const SubCheck &SC =
192 llvm::StringSwitch<SubCheck>(FI->getName())
193 .Case("open", SubCheck(CheckOpen, BTypes[OpenFn]))
Ted Kremenek99d98382010-04-08 19:53:31 +0000194 .Case("pthread_once", SubCheck(CheckPthreadOnce, BTypes[PthreadOnceFn]))
Ted Kremenek381d1bf2010-02-25 00:20:35 +0000195 .Default(SubCheck());
196
197 SC.run(C, CE);
198}