Skip to content

Using reference for output parameters reports double free #670

Description

I got crazy by tracking down a double free issues with CPPUtest and I could narrow down the problem. The problem seems to come when I use a reference as an output parameter, it complains about double freeing the field in the referenced object.
As always, an example is clearer than too many words:

#include <string>

#include <CppUTest/CommandLineTestRunner.h>
#include <CppUTest/TestHarness.h>
#include <CppUTestExt/MockSupport.h>

//--------------------------------------------------------------------------------------------
class MyReference
{
public:
    MyReference():
      m_name("Hello")
    {
    }
    ~MyReference(){}

private:
    // If I remove this field, it works as expected
    std::string m_name; // Double freed!!!??
};

//--------------------------------------------------------------------------------------------
class ReturnReference
{
public:
    ReturnReference(){}
    virtual ~ReturnReference(){}

    virtual bool returnReference(MyReference& ref)
    {
        ref = m_ref;
        return true;
    }
private:
    MyReference m_ref;
};

//--------------------------------------------------------------------------------------------
class ReturnReferenceMock:
        public ReturnReference
{
public:
    ReturnReferenceMock(){}
    ~ReturnReferenceMock(){}

    bool returnReference(MyReference& ref)
    {
        return mock().actualCall("returnReference").onObject(this).
                withOutputParameter("ref", &ref).returnIntValue();
    }
};

//--------------------------------------------------------------------------------------------
class UseReturnReference
{
public:
    UseReturnReference(ReturnReference* returnReference) :
        m_pReturnReference(returnReference)
    {
    }

    ~UseReturnReference()
    {
        delete m_pReturnReference;
    }

    void useReturnReference()
    {
        MyReference ref;
        m_pReturnReference->returnReference(ref);
    }

private:
    ReturnReference* m_pReturnReference;
};

//--------------------------------------------------------------------------------------------
TEST_GROUP(TestReferenceMock)
{

    TEST_SETUP()
    {
        // XXX: Double free!
        //MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
    }

    TEST_TEARDOWN()
    {
        mock().clear();
        MemoryLeakWarningPlugin::turnOnNewDeleteOverloads();
    }
};

TEST(TestReferenceMock, willItFail)
{
    ReturnReferenceMock* retRefMock = new ReturnReferenceMock();
    UseReturnReference useRef(retRefMock);
    MyReference* ref = new MyReference();

    mock().expectOneCall("returnReference").onObject(retRefMock).
            withOutputParameterReturning("ref", ref, sizeof(*ref)).
            andReturnValue(true);

    useRef.useReturnReference();

    mock().checkExpectations();

    // Not doing this reports a memory leak as expected!
    delete ref;
}

TEST(TestReferenceMock, willItFail2)
{
    ReturnReferenceMock* retRefMock = new ReturnReferenceMock();
    UseReturnReference useRef(retRefMock);
    MyReference ref;

    mock().expectOneCall("returnReference").onObject(retRefMock).
            withOutputParameterReturning("ref", &ref, sizeof(ref)).
            andReturnValue(true);

    useRef.useReturnReference();

    mock().checkExpectations();
}

//--------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
    // Run the tests
    return CommandLineTestRunner::RunAllTests(argc, argv);
}

The previous snippet reports the following error:

main.cpp:112: error: Failure in TEST(TestReferenceMock, willItFail2)
willItFail2:112: error:
    Deallocating non-allocated memory
   allocated at file: <unknown> line: 0 size: 0 type: unknown
   deallocated at file: <unknown> line: 0 type: delete


.
main.cpp:94: error: Failure in TEST(TestReferenceMock, willItFail)
willItFail:94: error:
    Deallocating non-allocated memory
   allocated at file: <unknown> line: 0 size: 0 type: unknown
   deallocated at file: <unknown> line: 0 type: delete
.
Errors (2 failures, 2 tests, 2 ran, 6 checks, 0 ignored, 0 filtered out, 0 ms)

This seems a bug in CPPUnit to me. However, the bug might probably come from a missuse of the library, but what am I doing wrong? Is it really possible to pass a reference as output parameter?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions