fixing a crash vulnerability due to race condition
LocTimer::stop() can be called from different threads, which must be protected. Currently there is a race condition between back to back stop()'s or expire() + stop() events. Change-Id: Iae80b78f049a32da87639f813c6f5126b4ccd072 CRs-Fixed: 904627
This commit is contained in:
parent
f2f492bd5b
commit
9957a19f5f
2 changed files with 85 additions and 25 deletions
|
@ -48,6 +48,20 @@
|
|||
|
||||
using namespace loc_core;
|
||||
|
||||
// a shared lock until, place it here for now.
|
||||
class LocUtilSharedLock {
|
||||
uint32_t mRef;
|
||||
pthread_mutex_t mMutex;
|
||||
inline ~LocUtilSharedLock() { pthread_mutex_destroy(&mMutex); }
|
||||
public:
|
||||
inline LocUtilSharedLock() : mRef(1) { pthread_mutex_init(&mMutex, NULL); }
|
||||
inline LocUtilSharedLock* share() { mRef++; return this; }
|
||||
inline void drop() { if (0 == --mRef) delete this; }
|
||||
inline void lock() { pthread_mutex_lock(&mMutex); }
|
||||
inline void unlock() { pthread_mutex_unlock(&mMutex); }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
There are implementations of 5 classes in this file:
|
||||
LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
|
||||
|
@ -177,15 +191,16 @@ class LocTimerDelegate : public LocRankable {
|
|||
friend class LocTimerContainer;
|
||||
friend class LocTimer;
|
||||
LocTimer* mClient;
|
||||
LocUtilSharedLock* mLock;
|
||||
struct timespec mFutureTime;
|
||||
LocTimerContainer* mContainer;
|
||||
// not a complete obj, just ctor for LocRankable comparisons
|
||||
inline LocTimerDelegate(struct timespec& delay)
|
||||
: mClient(NULL), mFutureTime(delay), mContainer(NULL) {}
|
||||
inline ~LocTimerDelegate() {}
|
||||
: mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
|
||||
inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
|
||||
public:
|
||||
LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire);
|
||||
void destroy();
|
||||
void destroyLocked();
|
||||
// LocRankable virtual method
|
||||
virtual int ranks(LocRankable& rankable);
|
||||
void expire();
|
||||
|
@ -327,8 +342,9 @@ void LocTimerContainer::remove(LocTimerDelegate& timer) {
|
|||
LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
|
||||
inline virtual void proc() const {
|
||||
LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
|
||||
((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer);
|
||||
if (NULL != ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
|
||||
mTimerContainer->updateSoonestTime(priorTop);
|
||||
}
|
||||
delete mTimer;
|
||||
}
|
||||
};
|
||||
|
@ -356,6 +372,7 @@ void LocTimerContainer::expire() {
|
|||
timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
|
||||
// the timer delegate obj will be deleted before the return of this call
|
||||
timer->expire();
|
||||
delete timer;
|
||||
}
|
||||
mTimerContainer->updateSoonestTime(NULL);
|
||||
}
|
||||
|
@ -460,19 +477,30 @@ bool LocTimerPollTask::run() {
|
|||
|
||||
inline
|
||||
LocTimerDelegate::LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire)
|
||||
: mClient(&client), mFutureTime(futureTime), mContainer(LocTimerContainer::get(wakeOnExpire)) {
|
||||
: mClient(&client),
|
||||
mLock(mClient->mLock->share()),
|
||||
mFutureTime(futureTime),
|
||||
mContainer(LocTimerContainer::get(wakeOnExpire)) {
|
||||
// adding the timer into the container
|
||||
mContainer->add(*this);
|
||||
}
|
||||
|
||||
inline
|
||||
void LocTimerDelegate::destroy() {
|
||||
void LocTimerDelegate::destroyLocked() {
|
||||
// client handle will likely be deleted soon after this
|
||||
// method returns. Nulling this handle so that expire()
|
||||
// won't call the callback on the dead handle any more.
|
||||
mClient = NULL;
|
||||
|
||||
if (mContainer) {
|
||||
mContainer->remove(*this);
|
||||
LocTimerContainer* container = mContainer;
|
||||
mContainer = NULL;
|
||||
} else {
|
||||
delete this;
|
||||
if (container) {
|
||||
container->remove(*this);
|
||||
}
|
||||
} // else we do not do anything, as *this* will be deleted either
|
||||
// after it expires or stop is first called (if case above),
|
||||
// where container->remove() will have it deleted.
|
||||
}
|
||||
|
||||
int LocTimerDelegate::ranks(LocRankable& rankable) {
|
||||
|
@ -488,6 +516,7 @@ int LocTimerDelegate::ranks(LocRankable& rankable) {
|
|||
|
||||
inline
|
||||
void LocTimerDelegate::expire() {
|
||||
mLock->lock();
|
||||
// keeping a copy of client pointer to be safe
|
||||
// when timeOutCallback() is called at the end of this
|
||||
// method, this obj is already deleted.
|
||||
|
@ -495,14 +524,29 @@ void LocTimerDelegate::expire() {
|
|||
// this obj is already removed from mContainer.
|
||||
// NULL it here so that dtor won't try to call remove again
|
||||
mContainer = NULL;
|
||||
mClient = NULL;
|
||||
mLock->unlock();
|
||||
// force a stop, which will force a delete of this obj
|
||||
mClient->stop();
|
||||
if (client && client->stop()) {
|
||||
// calling client callback with a pointer save on the stack
|
||||
// only if stop() returns true, i.e. it hasn't been stopped
|
||||
// already.
|
||||
client->timeOutCallback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************LocTimer methods***************************/
|
||||
LocTimer::LocTimer() : mTimer(NULL), mLock(new LocUtilSharedLock()) {
|
||||
}
|
||||
|
||||
LocTimer::~LocTimer() {
|
||||
stop();
|
||||
if (mLock) {
|
||||
mLock->drop();
|
||||
mLock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
|
||||
bool success = false;
|
||||
|
@ -525,10 +569,15 @@ bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
|
|||
bool LocTimer::stop() {
|
||||
bool success = false;
|
||||
if (mTimer) {
|
||||
mTimer->destroy();
|
||||
mLock->lock();
|
||||
LocTimerDelegate* timer = mTimer;
|
||||
mTimer = NULL;
|
||||
if (timer) {
|
||||
timer->destroyLocked();
|
||||
success = true;
|
||||
}
|
||||
mLock->unlock();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -539,21 +588,26 @@ bool LocTimer::stop() {
|
|||
class LocTimerWrapper : public LocTimer {
|
||||
loc_timer_callback mCb;
|
||||
void* mCallerData;
|
||||
LocTimerWrapper* mMe;
|
||||
static pthread_mutex_t mMutex;
|
||||
inline ~LocTimerWrapper() { mCb = NULL; }
|
||||
inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
|
||||
public:
|
||||
inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
|
||||
mCb(cb), mCallerData(callerData) {}
|
||||
inline bool isAlive() { return mCb != NULL; }
|
||||
mCb(cb), mCallerData(callerData), mMe(this) {
|
||||
}
|
||||
void destroy() {
|
||||
pthread_mutex_lock(&mMutex);
|
||||
if (isAlive()) {
|
||||
if (NULL != mCb && this == mMe) {
|
||||
delete this;
|
||||
}
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
}
|
||||
inline virtual void timeOutCallback() {
|
||||
mCb(mCallerData, 0);
|
||||
virtual void timeOutCallback() {
|
||||
loc_timer_callback cb = mCb;
|
||||
void* callerData = mCallerData;
|
||||
if (cb) {
|
||||
cb(callerData, 0);
|
||||
}
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
|
@ -580,7 +634,6 @@ void loc_timer_stop(void*& handle)
|
|||
{
|
||||
if (handle) {
|
||||
LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
|
||||
locTimerWrapper->stop();
|
||||
locTimerWrapper->destroy();
|
||||
handle = NULL;
|
||||
}
|
||||
|
|
|
@ -35,15 +35,22 @@
|
|||
|
||||
// opaque class to provide service implementation.
|
||||
class LocTimerDelegate;
|
||||
class LocUtilSharedLock;
|
||||
|
||||
// LocTimer client must extend this class and implementthe callback.
|
||||
// start() / stop() methods are to arm / disarm timer.
|
||||
class LocTimer
|
||||
{
|
||||
LocTimerDelegate* mTimer;
|
||||
LocUtilSharedLock* mLock;
|
||||
// don't really want mLock to be manipulated by clients, yet LocTimer
|
||||
// has to have a reference to the lock so that the delete of LocTimer
|
||||
// and LocTimerDelegate can work together on their share resources.
|
||||
friend class LocTimerDelegate;
|
||||
|
||||
public:
|
||||
inline LocTimer() : mTimer(NULL) {}
|
||||
inline virtual ~LocTimer() { stop(); }
|
||||
LocTimer();
|
||||
virtual ~LocTimer();
|
||||
|
||||
// timeOutInMs: timeout delay in ms
|
||||
// wakeOnExpire: true if to wake up CPU (if sleeping) upon timer
|
||||
|
|
Loading…
Reference in a new issue