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:
Kevin Tang 2015-09-03 18:08:08 -07:00 committed by Vamana Murthi
parent 62a30d2faa
commit 91488d6fc5
2 changed files with 85 additions and 25 deletions

View file

@ -48,6 +48,20 @@
using namespace loc_core; 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: There are implementations of 5 classes in this file:
LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
@ -177,15 +191,16 @@ class LocTimerDelegate : public LocRankable {
friend class LocTimerContainer; friend class LocTimerContainer;
friend class LocTimer; friend class LocTimer;
LocTimer* mClient; LocTimer* mClient;
LocUtilSharedLock* mLock;
struct timespec mFutureTime; struct timespec mFutureTime;
LocTimerContainer* mContainer; LocTimerContainer* mContainer;
// not a complete obj, just ctor for LocRankable comparisons // not a complete obj, just ctor for LocRankable comparisons
inline LocTimerDelegate(struct timespec& delay) inline LocTimerDelegate(struct timespec& delay)
: mClient(NULL), mFutureTime(delay), mContainer(NULL) {} : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
inline ~LocTimerDelegate() {} inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
public: public:
LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire); LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire);
void destroy(); void destroyLocked();
// LocRankable virtual method // LocRankable virtual method
virtual int ranks(LocRankable& rankable); virtual int ranks(LocRankable& rankable);
void expire(); void expire();
@ -334,8 +349,9 @@ void LocTimerContainer::remove(LocTimerDelegate& timer) {
LocMsg(), mTimerContainer(&container), mTimer(&timer) {} LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
inline virtual void proc() const { inline virtual void proc() const {
LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer(); LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer); if (NULL != ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
mTimerContainer->updateSoonestTime(priorTop); mTimerContainer->updateSoonestTime(priorTop);
}
delete mTimer; delete mTimer;
} }
}; };
@ -363,6 +379,7 @@ void LocTimerContainer::expire() {
timer = mTimerContainer->popIfOutRanks(timerOfNow)) { timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
// the timer delegate obj will be deleted before the return of this call // the timer delegate obj will be deleted before the return of this call
timer->expire(); timer->expire();
delete timer;
} }
mTimerContainer->updateSoonestTime(NULL); mTimerContainer->updateSoonestTime(NULL);
} }
@ -467,19 +484,30 @@ bool LocTimerPollTask::run() {
inline inline
LocTimerDelegate::LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire) 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 // adding the timer into the container
mContainer->add(*this); mContainer->add(*this);
} }
inline 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) { if (mContainer) {
mContainer->remove(*this); LocTimerContainer* container = mContainer;
mContainer = NULL; mContainer = NULL;
} else { if (container) {
delete this; 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) { int LocTimerDelegate::ranks(LocRankable& rankable) {
@ -495,6 +523,7 @@ int LocTimerDelegate::ranks(LocRankable& rankable) {
inline inline
void LocTimerDelegate::expire() { void LocTimerDelegate::expire() {
mLock->lock();
// keeping a copy of client pointer to be safe // keeping a copy of client pointer to be safe
// when timeOutCallback() is called at the end of this // when timeOutCallback() is called at the end of this
// method, this obj is already deleted. // method, this obj is already deleted.
@ -502,14 +531,29 @@ void LocTimerDelegate::expire() {
// this obj is already removed from mContainer. // this obj is already removed from mContainer.
// NULL it here so that dtor won't try to call remove again // NULL it here so that dtor won't try to call remove again
mContainer = NULL; mContainer = NULL;
mClient = NULL;
mLock->unlock();
// force a stop, which will force a delete of this obj // 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 // 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(); client->timeOutCallback();
} }
}
/***************************LocTimer methods***************************/ /***************************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 LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
bool success = false; bool success = false;
@ -532,10 +576,15 @@ bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
bool LocTimer::stop() { bool LocTimer::stop() {
bool success = false; bool success = false;
if (mTimer) { if (mTimer) {
mTimer->destroy(); mLock->lock();
LocTimerDelegate* timer = mTimer;
mTimer = NULL; mTimer = NULL;
if (timer) {
timer->destroyLocked();
success = true; success = true;
} }
mLock->unlock();
}
return success; return success;
} }
@ -546,21 +595,26 @@ bool LocTimer::stop() {
class LocTimerWrapper : public LocTimer { class LocTimerWrapper : public LocTimer {
loc_timer_callback mCb; loc_timer_callback mCb;
void* mCallerData; void* mCallerData;
LocTimerWrapper* mMe;
static pthread_mutex_t mMutex; static pthread_mutex_t mMutex;
inline ~LocTimerWrapper() { mCb = NULL; } inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
public: public:
inline LocTimerWrapper(loc_timer_callback cb, void* callerData) : inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
mCb(cb), mCallerData(callerData) {} mCb(cb), mCallerData(callerData), mMe(this) {
inline bool isAlive() { return mCb != NULL; } }
void destroy() { void destroy() {
pthread_mutex_lock(&mMutex); pthread_mutex_lock(&mMutex);
if (isAlive()) { if (NULL != mCb && this == mMe) {
delete this; delete this;
} }
pthread_mutex_unlock(&mMutex); pthread_mutex_unlock(&mMutex);
} }
inline virtual void timeOutCallback() { virtual void timeOutCallback() {
mCb(mCallerData, 0); loc_timer_callback cb = mCb;
void* callerData = mCallerData;
if (cb) {
cb(callerData, 0);
}
destroy(); destroy();
} }
}; };
@ -587,7 +641,6 @@ void loc_timer_stop(void*& handle)
{ {
if (handle) { if (handle) {
LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle); LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
locTimerWrapper->stop();
locTimerWrapper->destroy(); locTimerWrapper->destroy();
handle = NULL; handle = NULL;
} }

View file

@ -35,15 +35,22 @@
// opaque class to provide service implementation. // opaque class to provide service implementation.
class LocTimerDelegate; class LocTimerDelegate;
class LocUtilSharedLock;
// LocTimer client must extend this class and implementthe callback. // LocTimer client must extend this class and implementthe callback.
// start() / stop() methods are to arm / disarm timer. // start() / stop() methods are to arm / disarm timer.
class LocTimer class LocTimer
{ {
LocTimerDelegate* mTimer; 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: public:
inline LocTimer() : mTimer(NULL) {} LocTimer();
inline virtual ~LocTimer() { stop(); } virtual ~LocTimer();
// timeOutInMs: timeout delay in ms // timeOutInMs: timeout delay in ms
// wakeOnExpire: true if to wake up CPU (if sleeping) upon timer // wakeOnExpire: true if to wake up CPU (if sleeping) upon timer