forked from OSchip/llvm-project
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:
parent
7ffb294c5b
commit
ea4a5abf61
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue