0

Somehow, accessing a shared_ptr without dereferencing it is causing a Signal 11 (SIGSEGV) on Android.

I have a run() function in A that acquires a lock for it's instance of B and calls B::top(). There is only one instance of A. A has other public methods that other threads might call to modify mB (thus the mutex), but they are not being called by anything yet.

LogCat Error:

04-17 15:15:16.903: A/libc(11591): Fatal signal 11 (SIGSEGV) at 0x00000024 (code=1)

In class A:

std::thread mControllerThread;
std::mutex mBMutex;
shared_ptr<B> mB;

A() {  
    mB.reset( new B() ); 
    mControllerThread = std::thread( std::bind(&A::run, this) );       
}

//...

void run() {
    std::unique_lock<std::mutex > lk(mBMutex);
    shared_ptr<const Event> event = mB->top(B::Scope::FUTURE);
}

In class B:

shared_ptr<EventHeap> mFuture;

B() {
    mFuture.reset( new EventHeap() );
}

//...

shared_ptr<const Event> top(Scope scope, int mask=EVENT_MASK_SUPPORTED) const {
    shared_ptr<const Event> event;

    if(scope == Scope::PAST) {
        //...
    } else if(scope == Scope::FUTURE) {
        LOGD(LOG_TAG, "Testing mFuture ptr");
        // Fails here with any of these versions
        if(mFuture) {
        // if(mFuture.get() != NULL) {
        // if(mFuture != nullptr) {
            LOGD(LOG_TAG, "Getting top from FUTURE");
            event = mFuture->top(mask);
        } else {
            LOGE(LOG_TAG, "mFuture is null");
        }
    }
    return event;
}

So how can accessing a smart pointer without dereferencing it possibly cause a segfault? Thanks!

mxdubois
  • 605
  • 8
  • 14
  • What happens at the call site for A? And also what is the destructor for A doing. If your test code is just int main() { A a; } right now it is very likely to crash (with an empty dtor). – Joky Apr 18 '14 at 00:43
  • @Joky Hmm. Well this is sort of a dumbed down example. `A` is actually a state machine, but I don't want to add all the complexity to the question. `A` isn't destroyed in the process that causes the crash, it's managed (through JNI) by a subclass of Android's `Service`. I guess I'm mostly curious if what's seen here could cause the problem, or if anyone knows situations off-hand that could cause it... because I don't understand how it's possible. Sorry if that's not terribly helpful. – mxdubois Apr 18 '14 at 01:21
  • Well short answer no, it is not possible. Long answer: everything is possible, especially since Android support for C++11 is so-so. Now just an experiment: try to add an integer field before or after your shared_ptr, and try LOGD(LOG_TAG, "%p %p %d", &mFuture, &mInteger, mInteger); This will for to dereference "this", just as it is implicitely done when you do if(mFuture). – Joky Apr 18 '14 at 02:08
  • @Joky That results in `Fatal signal 11 (SIGSEGV) at 0x0000002c (code=1)`. Notably a different address. – mxdubois Apr 18 '14 at 02:21
  • @Joky I also tried this: `LOGD(LOG_TAG, "%p", this);` which prints `0x0`. That's pretty weird. – mxdubois Apr 18 '14 at 02:28
  • Now you can try before the call `mB->top` to log: `LOGD(LOG_TAG, "%p", this, mB->get());` and also the same before running the thread. You may also try to force to wait for the thread immediately `mControllerThread.join()` – Joky Apr 18 '14 at 04:34
  • @Joky Oh okay. So `mB.get()` returns null in `A`. But how come I'm able to call `mB->top()` if `mB` points to null? Shouldn't _that_ cause the segfault? – mxdubois Apr 18 '14 at 06:22
  • No, mB->top() is a simple function call, there is no need to dereference mB (as long as the function is not virtual). – Joky Apr 18 '14 at 22:32
  • Wow that is screwy. Thanks for helping me track it down! If you want to turn it into an answer, I'd be happy to accept. I found a related answer that goes into more detail: http://stackoverflow.com/a/3826144/1599617 – mxdubois Apr 19 '14 at 06:46
  • Ok I provided an answer. – Joky Apr 19 '14 at 06:56

1 Answers1

0

The statement you point test if the shared_ptr is initialized with a non-null pointer.

if(mFuture) // can be seen as if(mFuture.privateMember_Ptr != nullptr)

It is pretty clear that the pointer itself is not dereferenced, but the value of the pointer is accessed. So this memory location seems invalid. Now where is this memory location? It is part of mFuture which is itself part of B. Let's rewrite the mFuture to show what we really dereference:

if(this->mFuture.privateMember_Ptr != nullptr)

It seems that "this" is invalid, you can start by printing it in the top method, and "unwind" the stack with the same debugging. Looking at the source, "this" should correspond to mB in class A. So you can print mB in A::run() before calling B::top(). mB is initialized in A's ctor with a "new B()". Do you check somewhere that this memory allocation succeed? (exceptions are disabled by default on Android AFAIK, so new can return nullptr)

Joky
  • 1,608
  • 11
  • 15