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

[16133 byte] By [andersoa] at [2007-11-26 15:22:17]
# 1
Please file a bug report at bugs.sun.com
clamage45a at 2007-7-8 21:37:20 > top of Java-index,Development Tools,Solaris and Linux Development Tools...
# 2
This issue has been filed as bug 6514832, and will be visible at bugs.sun.com in a day or two.
clamage45a at 2007-7-8 21:37:20 > top of Java-index,Development Tools,Solaris and Linux Development Tools...