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
62a30d2faa
commit
91488d6fc5
2 changed files with 85 additions and 25 deletions
|
@ -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
|
||||||
client->timeOutCallback();
|
// only if stop() returns true, i.e. it hasn't been stopped
|
||||||
|
// already.
|
||||||
|
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,9 +576,14 @@ 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;
|
||||||
success = true;
|
if (timer) {
|
||||||
|
timer->destroyLocked();
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue