libumem complains on redzone violation in std::deque even with latest patch
HW: e2900 (USIV+ cpus), 5.10 Generic_118833-24
I've applied the latest libCstd patch 119963-08
SW:
CC +w -fast -erroff=hidef -V -PIC -mt -xtarget=ultra3 -xarch=v9a sunBugReportDeque.cc
CC: Sun C++ 5.8 Patch 121017-08 2006/12/06
cg: Sun Compiler Common 11 Patch 120760-11 2006/10/18
The attached code fails, when running under libumem on said machine
If I change the deque to a list, everything works smoothly, suggesting there is still something fishy inside the deque allocate/deallocate functions. I could not reproduce the fault on my (single-CPU) workstation, but on the 2900 it crashes immediately.
// file: sunBugReportDeque.cc
[code]
#ifndef DEPEND
#include <deque>
#include <exception>
#include <functional>
#include <iostream>
#include <list>
#include <sstream>
#include <stdexcept>
#include <stdio.h>// sprintf
#include <synch.h>
#include <sys/errno.h> // ETIME
#include <sys/time.h> // gettimeofday
#include <thread.h>
#include <time.h>
#include <unistd.h>
#include <vector>
#endif
using namespace std;
#define ENSURE(expr) do { int rc = expr ; \
if (rc != 0) \
{ \
char buf[800]; \
snprintf(buf, sizeof(buf), "%s: unexpected rc: %d", #expr, rc); \
buf[sizeof(buf)-1] = '\0'; \
throw std::logic_error(buf);\
} \
} while(0)
/** A simple mutex implementation. */
class Mutex
{
friend class Condition; // needs access to my mutex
public:
Mutex() {
ENSURE(::mutex_init(&_impl, 0, 0));
}
/**
* Destructor.
*/
~Mutex() {
ENSURE(::mutex_destroy(&_impl));
}
void acquire() {
ENSURE(::mutex_lock(&_impl));
}
void acquireRead() { this->acquire(); }
void acquireWrite() { this->acquire(); }
void release() {
ENSURE(::mutex_unlock(&_impl));
}
void releaseRead() { this->release(); }
void releaseWrite() { this->release(); }
inline bool tryAcquire();
bool tryAcquireRead() { return this->tryAcquire(); }
bool tryAcquireWrite() { return this->tryAcquire(); }
private:
mutex_t _impl;
};
bool
Mutex::tryAcquire()
{
while(true) {
int rc = ::mutex_trylock(&_impl);
switch (rc)
{
case 0: // ok, we got it...
return true;
case EBUSY: // nope, it was already taken...
return false;
case EINTR: // fork or signal, try again...
continue;
case EINVAL: // The rest is faults, should not happend...
case EFAULT:
throw std::invalid_argument("Internal error, illegal adress");
default:
char msg[800];
::sprintf(msg, "::mutex_trylock: Unrecognized rc: %d", rc);
throw std::logic_error(msg);
}
}
}
/** A simple condition variable implementation. */
class Condition
{
public:
enum WaitStatus { TIMEOUT, SIGNALED };
Condition(Mutex &m) : _m(m) { ENSURE(::cond_init(&_impl, 0, 0)); }
~Condition() { ENSURE(::cond_destroy(&_impl)); }
void signal() { ENSURE(::cond_signal(&_impl)); }
void signalAll() { ENSURE(::cond_broadcast(&_impl)); }
inline void wait();
inline WaitStatus wait(unsigned long ms);
private:
Condition(const Condition&);
const Condition& operator=(const Condition&);
cond_t _impl;
Mutex &_m;
};
void
Condition::wait()
{
while(true) {
int rc = ::cond_wait(&_impl, &_m._impl);
switch (rc)
{
case 0: // NoOp, all is well...
return;
case EFAULT:
throw std::invalid_argument("Internal error, illegal adress");
case EINTR: // fork or signal, we should still be sleeping
continue;
default:
char msg[50];
sprintf(msg, "::cond_wait: Unrecognized rc: %d", rc);
throw std::logic_error(msg);
}
}
}
Condition::WaitStatus
Condition::wait(unsigned long millis)
{
struct timeval tp;
timestruc_t to;
if(gettimeofday(&tp, 0))
{ // failed, use time() instead
to.tv_sec = ::time(NULL) + millis / 1000;
to.tv_nsec = (millis % 1000) * 1000000; // 1e6 nanos-per-milli
}
else
{ // Ok, calculate when to wake up..
to.tv_sec = tp.tv_sec + millis/1000;
to.tv_nsec = tp.tv_usec*1000 + (millis%1000)*1000000;
if(to.tv_nsec >= 1000000000)
{
to.tv_nsec -= 1000000000;
to.tv_sec++;
}
}
while(true) {
int rc = ::cond_timedwait(&_impl, &_m._impl, &to);
switch (rc)
{
case 0: // NoOp, all is well... Someone told ut to wake up before timeout...
return SIGNALED;
case EFAULT:
throw std::invalid_argument("Internal error, illegal adress");
case EINTR: // fork or signal, we should still be sleeping
continue;
case ETIME:
case ETIMEDOUT:
return TIMEOUT;
default:
char msg[50];
sprintf(msg, "::cond_timedwait: Unrecognized rc: %d", rc);
throw std::logic_error(msg);
}
}
}
/** Suitable for grabbing a mutex exclusively. The mutex will be
* released when the object dies.
*/
template <class Lock>
class Guard
{
public:
Guard(Lock &l) : _l(l) { _l.acquire(); }
~Guard() { _l.release(); }
private:
Guard(const Guard&);
const Guard& operator=(const Guard&);
Lock &_l;
};
class Timer
{
public:
Timer() : _cond(_mutex), _isCancelled(false) { }
~Timer() { }
/** Sleeps the specified no of secs, or until the timer is cancelled. */
inline void sleep(const int millis);
/** Cancels the timer. Ongoing sleeps will wakeup, new ones will not block.
*/
inline void cancel();
inline bool isCancelled();
protected:
private:
Timer(const Timer& aTimer);
Timer& operator=(const Timer& aTimer);
Mutex_mutex;
Condition _cond;
bool _isCancelled;
};
void Timer::sleep(const int millis)
{
Guard<Mutex> lock(_mutex);
if (! _isCancelled) // only wait one turn
_cond.wait(millis);
}
void Timer::cancel()
{
Guard<Mutex> lock(_mutex);
_isCancelled = true;
_cond.signalAll();
}
bool Timer::isCancelled()
{
Guard<Mutex> lock(_mutex);
return _isCancelled;
}
// shouldn't this be available in STL somewhere?
template <class T>
struct Predicate : public std::unary_function<T, bool>
{
virtual bool operator()(const T &x) const = 0;
};
/** A simple Producer Consumer Queue implementation. */
template <class T>
class PCQueue
{
public:
PCQueue(size_t aMaxLenght)
: myMaxlength(aMaxLenght),
myQNotEmpty(myQLock),
myQNotFull(myQLock){ }
void push(const T& aT);
bool tryPush(const T& aT);
T pop();
bool tryPop(T& retVal, const unsigned int millis = 0);
size_t size() const;
bool isFull() const;
/**
* Atomically purges (removes) the FIRST element for which the supplied
* predicate returns true.
*/
bool purge(const Predicate<T>& pred, T& theItem);
protected:
private:
typedef Guard<Mutex> MutexGuard;
std::deque<T>myQueue;
mutable MutexmyQLock;
ConditionmyQNotEmpty;
ConditionmyQNotFull;
size_tmyMaxlength;
};
template <class T>
size_t PCQueue<T>::size() const
{
MutexGuard g(myQLock);
return myQueue.size();
}
template <class T>
void PCQueue<T>::push(const T& aT)
{
MutexGuard g(myQLock);
while (myMaxlength && myQueue.size() >= myMaxlength)
myQNotFull.wait();
myQueue.push_back(aT);
myQNotEmpty.signal();
}
template <class T>
bool PCQueue<T>::tryPush(const T& aT)
{
MutexGuard g(myQLock);
while (myMaxlength && myQueue.size() >= myMaxlength)
return false;
myQueue.push_back(aT);
myQNotEmpty.signal();
return true;
}
template <class T>
T PCQueue<T>::pop()
{
MutexGuard g(myQLock);
T entry;
while (myQueue.empty())
myQNotEmpty.wait();
entry = myQueue.front();
myQueue.pop_front();
myQNotFull.signal();
return entry;
}
template <class T>
bool PCQueue<T>::tryPop(T& retVal, const unsigned int millis)
{
MutexGuard g(myQLock);
long long start = ::gethrtime();
long remainder = millis;
while (remainder > 0 && myQueue.empty())
{
myQNotEmpty.wait(remainder);
remainder = millis - (long)((::gethrtime() - start) / 1000000LL);
}
if (myQueue.empty()) // timed out
return false;
retVal = myQueue.front();
myQueue.pop_front();
myQNotFull.signal();
return true;
}
template <class T>
bool PCQueue<T>::isFull() const
{
MutexGuard g(myQLock);
if (myMaxlength == 0) // No limit on the queue
return false;
return (myQueue.size() >= myMaxlength) ? true : false;
}
template <class T>
bool PCQueue<T>::purge(const Predicate<T> &pred, T& theItem)
{
MutexGuard g(myQLock);
for (std::deque<T>::iterator i = myQueue.begin(); i != myQueue.end(); ++i)
{
if (pred(*i))
{
theItem = *i;
myQueue.erase(i);
myQNotFull.signal();
return true;
}
}
return false;
}
struct fifthBitSet : public Predicate<hrtime_t *>
{
bool operator()(hrtime_t * const &i) const { return (bool) ((*i) & (0x1L << 4)); }
};
class StressTest
{
public:
StressTest(int consumers, int producers);
~StressTest();
void start();
void stop();
void sleep(int seconds) { timer_.sleep(seconds * 1000); }
private:
void consume();
void produce();
static void * consumer(void *arg);
static void * producer(void *arg);
static void joinThread(thread_t tid);
Timer timer_;
PCQueue<hrtime_t*> queue_;
vector<thread_t> consumers_;
vector<thread_t> producers_;
};
StressTest::StressTest(int c, int p) : queue_(501), consumers_(c, 0L), producers_(p, 0L)
{
}
StressTest::~StressTest()
{
hrtime_t *val = NULL;
while (queue_.tryPop(val, 0))
delete val;
}
void
StressTest::joinThread(thread_t tid)
{
void * status;
int rc = thr_join(tid,
NULL,
&status);
if (rc != 0)
{
char buf[80];
snprintf(buf, sizeof(buf), "thr_join: unexpected rc: %d", rc);
throw std::logic_error(buf);
}
}
void
StressTest::start()
{
for (int i = 0; i < consumers_.size(); ++i)
{
thread_t tid = 0L;
thr_create(NULL, NULL, &consumer, this, NULL, &tid);
consumers_[i] = tid;
}
for (int i = 0; i < producers_.size(); ++i)
{
thread_t tid = 0L;
thr_create(NULL, NULL, &producer, this, NULL, &tid);
producers_[i] = tid;
}
}
void
StressTest::stop()
{
timer_.cancel();
for (int i = 0; i < consumers_.size(); ++i)
queue_.push(NULL);
for_each(producers_.begin(), producers_.end(), joinThread);
for_each(consumers_.begin(), consumers_.end(), joinThread);
}
void *
StressTest::consumer(void *arg)
{
StressTest * test = reinterpret_cast<StressTest *>(arg);
test->consume();
return NULL;
}
void *
StressTest::producer(void *arg)
{
StressTest * test = reinterpret_cast<StressTest *>(arg);
test->produce();
return NULL;
}
void
StressTest::consume()
{
while (! timer_.isCancelled())
{
hrtime_t *ptime = queue_.pop();
while (ptime != NULL)
{
hrtime_t now = ::gethrtime();
if((now - *ptime) > 1000000000)
{
ostringstream os;
os << "Too old request: " << ((double)(now - *ptime)) / 1.0e9 << endl;
cerr << os.str() << flush;
}
delete ptime;
ptime = queue_.pop();
}
}
}
void
StressTest::produce()
{
while (! timer_.isCancelled())
{
hrtime_t *pnow = new hrtime_t;
*pnow = ::gethrtime();
bool qIsFull = ! queue_.tryPush(pnow);
while (qIsFull)
{
hrtime_t *pToRemove = NULL;
if (queue_.purge(fifthBitSet(), pToRemove) == false)
{
ostringstream os;
os << "Queue full, failed to make room, rejected call:" << *pnow << endl;
cerr << os.str() << flush;
delete pnow;
return;
}
if (pToRemove)
{
ostringstream os;
os << "Queue full, removed 1 item: " << *pToRemove << endl;
cerr << os.str() << flush;
delete pToRemove;
}
qIsFull = ! queue_.tryPush(pnow);
}
}
}
int
main(const int argc, char *argv[])
{
StressTest test(atoi(argv[1]), atoi(argv[2]));
test.start();
test.sleep(atoi(argv[3]));
test.stop();
}
[/code]
Message was edited by:
anderso
Forgot to include the libumem log:
%>env | fgrep UMEM
UMEM_DEBUG=default,verbose
UMEM_LOGGING=transaction,contents,fail
%>(setenv LD_PRELOAD /usr/lib/64/libumem.so ; ./a.out 8 8 400)
umem allocator: redzone violation: write past end of buffer
buffer=1007c3aa0 bufctl=1007c81f0 cache: umem_alloc_48
previous transaction on buffer 1007c3aa0:
thread=d time=T-0.000430640 slab=100799e10 cache: umem_alloc_48
libumem.so.1'? (0xffffffff7f216278)
libumem.so.1'? (0xffffffff7f2166d0)
libumem.so.1'? (0xffffffff7f2130ec)
libCrun.so.1'? (0xffffffff7ec08810)
a.out'? (0x100006df4)
a.out'? (0x1000046ec)
a.out'? (0x100003c04)
libc.so.1'? (0xffffffff7e7cd2f8)
umem: heap corruption detected
stack trace:
libumem.so.1'? (0xffffffff7f21471c)
libumem.so.1'? (0xffffffff7f213574)
libCrun.so.1'? (0xffffffff7ec0786c)
a.out'? (0x100006e70)
a.out'? (0x1000046ec)
a.out'? (0x100003c04)
libc.so.1'? (0xffffffff7e7cd2f8)
Abort (core dumped)
%>mdb core
> ::umem_verify
Cache Name Addr Cache Integrity
umem_magazine_1100720028 clean
umem_magazine_3100722028 clean
umem_magazine_7100724028 clean
umem_magazine_15100728028 clean
umem_magazine_3110072a028 clean
umem_magazine_4710072c028 clean
umem_magazine_63100730028 clean
umem_magazine_95100732028 clean
umem_magazine_143 100734028 clean
umem_slab_cache100738028 clean
umem_bufctl_cache 10073a028 clean
umem_bufctl_audit_cache10073c028 clean
umem_alloc_8100742028 clean
umem_alloc_16 100748028 clean
umem_alloc_32 10074a028 clean
umem_alloc_48 10074c028 1 corrupt buffer
umem_alloc_64 100750028 clean
:
:
> 10074c028::umem_verify
Summary for cache 'umem_alloc_48'
buffer 1007c3aa0 (allocated) has a corrupt redzone size encoding

