Search code examples
c++delete-operatormemory-corruptionflatbuffersdouble-free

Use of Flatbuffer Union caused double free or corruption error


I'm getting the following error at the end of my program execution:

* Error in `./bin/test': double free or corruption (out): 0x00007ffd34dab0d0 * Aborted

I am currently testing the object-based API (--gen-object-api) from Flatbuffer for C++. My main-file looks like this:

int main(int argc, char* argv[]) {

    flatbuffers::FlatBufferBuilder builder;
    auto dataTest = event::ByteBufferT();
    dataTest.Bytes.push_back(1);
    dataTest.Bytes.push_back(2);

    event::EventDataUnion unionD;
    unionD.type = event::EventData_ByteBuffer;
    unionD.value = &dataTest;

    auto eventOffset = event::CreateEvent(builder,
                       builder.CreateString("byteDataEvent"), 10,
                       event::EventData_ByteBuffer, unionD.Pack(builder));

    builder.Finish(eventOffset);

    auto eventOutput = event::GetEvent(builder.GetBufferPointer());
    auto vec(eventOutput->data_as_ByteBuffer()->Bytes());

    for (auto it = vec->begin(); it != vec->end(); ++it) {
        std::cout << "ByteData: " << int(*it) << std::endl;
    }

    return 0;
}

I know where the error occurs, but I do not know how I can avoid it. The valgrind-output looks like this:

==6242== Invalid free() / delete / delete[] / realloc()
==6242==    at 0x4C2A360: operator delete(void*) (vg_replace_malloc.c:507)
==6242==    by 0x4031C3: event::EventDataUnion::Reset() (event_generated.h:725)
==6242==    by 0x4028F5: event::EventDataUnion::~EventDataUnion() (event_generated.h:78)
==6242==    by 0x401320: main (main.cpp:118)
==6242==  Address 0xfff0000d0 is on thread 1's stack
==6242==  in frame #3, created by main (main.cpp:8)
==6242== 
==6242== Invalid free() / delete / delete[] / realloc()
==6242==    at 0x4C2A360: operator delete(void*) (vg_replace_malloc.c:507)
==6242==    by 0x406373: __gnu_cxx::new_allocator<signed char>::deallocate(signed char*, unsigned long) (new_allocator.h:110)
==6242==    by 0x405FC6: std::allocator_traits<std::allocator<signed char> >::deallocate(std::allocator<signed char>&, signed char*, unsigned long) (alloc_traits.h:383)
==6242==    by 0x40585D: std::_Vector_base<signed char, std::allocator<signed char> >::_M_deallocate(signed char*, unsigned long) (stl_vector.h:178)
==6242==    by 0x404DA4: std::_Vector_base<signed char, std::allocator<signed char> >::~_Vector_base() (stl_vector.h:160)
==6242==    by 0x403C5E: std::vector<signed char, std::allocator<signed char> >::~vector() (stl_vector.h:425)
==6242==    by 0x403109: event::ByteBufferT::~ByteBufferT() (event_generated.h:260)
==6242==    by 0x40132C: main (main.cpp:114)
==6242==  Address 0x5a021d0 is 0 bytes inside a block of size 2 free'd
==6242==    at 0x4C2A360: operator delete(void*) (vg_replace_malloc.c:507)
==6242==    by 0x406373: __gnu_cxx::new_allocator<signed char>::deallocate(signed char*, unsigned long) (new_allocator.h:110)
==6242==    by 0x405FC6: std::allocator_traits<std::allocator<signed char> >::deallocate(std::allocator<signed char>&, signed char*, unsigned long) (alloc_traits.h:383)
==6242==    by 0x40585D: std::_Vector_base<signed char, std::allocator<signed char> >::_M_deallocate(signed char*, unsigned long) (stl_vector.h:178)
==6242==    by 0x404DA4: std::_Vector_base<signed char, std::allocator<signed char> >::~_Vector_base() (stl_vector.h:160)
==6242==    by 0x403C5E: std::vector<signed char, std::allocator<signed char> >::~vector() (stl_vector.h:425)
==6242==    by 0x403109: event::ByteBufferT::~ByteBufferT() (event_generated.h:260)
==6242==    by 0x4031BB: event::EventDataUnion::Reset() (event_generated.h:725)
==6242==    by 0x4028F5: event::EventDataUnion::~EventDataUnion() (event_generated.h:78)
==6242==    by 0x401320: main (main.cpp:118)
==6242== 
==6242== 
==6242== HEAP SUMMARY:
==6242==     in use at exit: 0 bytes in 0 blocks
==6242==   total heap usage: 5 allocs, 7 frees, 1,219 bytes allocated
==6242== 
==6242== All heap blocks were freed -- no leaks are possible
==6242== 
==6242== For counts of detected and suppressed errors, rerun with: -v
==6242== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 1)

My flatbuffer-file (event.fbs) looks like this:

// event.fbs   
namespace event;

table TableData {
    param1:string;
    param2:uint;
}

table ByteBuffer {
    Bytes:[byte];
}

struct StructData {
    x:float;
    y:float;
}

union EventData {
    TableData,
    StructData,
    ByteBuffer,
    String:string,
}

table Event {
  name:string (key);
  timestamp:ulong = -1;
  data:EventData;
}

root_type Event;

My Output is correct, except for the error:

 ByteData: 1
 ByteData: 2
 *** Error in `./bin/test': double free or corruption (out): 0x00007ffd34dab0d0 ***
Aborted

Update:

Without the object API would it look like this?:

flatbuffers::FlatBufferBuilder builder;
signed char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto byteBuffer = event::CreateByteBuffer(builder, builder.CreateVector(inv_data, 10));
auto event = event::CreateEvent(builder, builder.CreateString("byteDataEvent"), 10, event::EventData_ByteBuffer, byteBuffer.Union());
builder.Finish(event);

And then I would pass the builder?


Solution

  • The error is likely here:

    unionD.value = &dataTest;
    

    The union object expects to own the value it points to, and will delete it at the end. But it is also deleted as a local variable.

    You are mixing the usage of two APIs, the object API (for ByteBuffer and EventData) and the standard API (for Event). This is perfectly fine, but may not be what you intended (the former is used for convenience/storage, the latter for speed). The object API in general is a self-contained tree of objects that owns its children.