Add static analyzer check for calls to 'pthread_once()' where the control-flow has

automatic storage.  This matches the corresponding check for 'dispatch_once()'.

llvm-svn: 100803
This commit is contained in:
Ted Kremenek 2010-04-08 19:53:31 +00:00
parent 7ffb294c5b
commit ea4a5abf61
2 changed files with 80 additions and 2 deletions

View File

@ -24,6 +24,7 @@ namespace {
class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> {
enum SubChecks {
OpenFn = 0,
PthreadOnceFn = 1,
NumChecks
};
@ -109,6 +110,49 @@ static void CheckOpen(CheckerContext &C, const CallExpr *CE, BugType *&BT) {
}
}
//===----------------------------------------------------------------------===//
// pthread_once
//===----------------------------------------------------------------------===//
static void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE,
BugType *&BT) {
// This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
// They can possibly be refactored.
LazyInitialize(BT, "Improper use of 'pthread_once'");
if (CE->getNumArgs() < 1)
return;
// Check if the first argument is stack allocated. If so, issue a warning
// because that's likely to be bad news.
const GRState *state = C.getState();
const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
return;
ExplodedNode *N = C.GenerateSink(state);
if (!N)
return;
llvm::SmallString<256> S;
llvm::raw_svector_ostream os(S);
os << "Call to 'pthread_once' uses";
if (const VarRegion *VR = dyn_cast<VarRegion>(R))
os << " the local variable '" << VR->getDecl()->getName() << '\'';
else
os << " stack allocated memory";
os << " for the \"control\" value. Using such transient memory for "
"the control value is potentially dangerous.";
if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
os << " Perhaps you intended to declare the variable as 'static'?";
EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.EmitReport(report);
}
//===----------------------------------------------------------------------===//
// Central dispatch function.
//===----------------------------------------------------------------------===//
@ -147,6 +191,7 @@ void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
const SubCheck &SC =
llvm::StringSwitch<SubCheck>(FI->getName())
.Case("open", SubCheck(CheckOpen, BTypes[OpenFn]))
.Case("pthread_once", SubCheck(CheckPthreadOnce, BTypes[PthreadOnceFn]))
.Default(SubCheck());
SC.run(C, CE);

View File

@ -1,11 +1,24 @@
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem %s -analyzer-store=region
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem %s -analyzer-store=basic
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-check-objc-mem %s -analyzer-store=region -fblocks -verify
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-check-objc-mem %s -analyzer-store=basic -fblocks -verify
struct _opaque_pthread_once_t {
long __sig;
char __opaque[8];
};
typedef struct _opaque_pthread_once_t __darwin_pthread_once_t;
typedef __darwin_pthread_once_t pthread_once_t;
int pthread_once(pthread_once_t *, void (*)(void));
typedef void (^dispatch_block_t)(void);
typedef long dispatch_once_t;
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
#ifndef O_CREAT
#define O_CREAT 0x0200
#define O_RDONLY 0x0000
#endif
int open(const char *, int, ...);
int close(int fildes);
void test_open(const char *path) {
int fd;
@ -17,3 +30,23 @@ void test_open(const char *path) {
if (!fd)
close(fd);
}
void test_dispatch_once() {
dispatch_once_t pred = 0;
do { if (__builtin_expect(*(&pred), ~0l) != ~0l) dispatch_once((&pred), (^() {})); } while (0); // expected-warning{{Call to 'dispatch_once' uses the local variable 'pred' for the predicate value}}
}
void test_dispatch_once_neg() {
static dispatch_once_t pred = 0;
do { if (__builtin_expect(*(&pred), ~0l) != ~0l) dispatch_once((&pred), (^() {})); } while (0); // no-warning
}
void test_pthread_once_aux();
void test_pthread_once() {
pthread_once_t pred = {0x30B1BCBA, {0}};
pthread_once(&pred, test_pthread_once_aux); // expected-warning{{Call to 'pthread_once' uses the local variable 'pred' for the "control" value}}
}
void test_pthread_once_neg() {
static pthread_once_t pred = {0x30B1BCBA, {0}};
pthread_once(&pred, test_pthread_once_aux); // no-warning
}