robotsfoundme
robotsfoundme

Reputation: 448

protobuf "oneof" sub-protobuf object pointer kills program

I have been using google protobufs successfully for a month and a half now and I have reached a problem I cannot surmount.

Basically, I am now trying to use the oneof feature in protobufs where I can have multiple types of messages inside of a single message. Basically, I need to be able to send only 1 message but contain multiple different options for submessages. I found many SO posts recommended the oneof feature, so now I am trying to use it.

https://developers.google.com/protocol-buffers/docs/proto#oneof

Okay, so what is the problem? Well, I get a huge stack dump with my code. Here are the multiple code files. This is the .cpp script that is crashing:

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <thread>
// First, we need to import the main message proto file.
#include "Robotmessage.pb.h"
// Then we include the sub-message
#include "SimpleController.pb.h"
// This is for cout.
#include <iostream>
// Google
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
// https://stackoverflow.com/questions/1641182/how-can-i-catch-a-ctrl-c-event
#include <signal.h>

using namespace google::protobuf::io;
using namespace std;

// Ctrl+C handler
void my_handler(int s){
    printf("Caught signal %d\n",s);
    exit(1); 
}

// simple wrapper for code cleanup 
void sleepApp(int ms)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}

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

    // https://developers.google.com/protocol-buffers/docs/cpptutorial
    // Verify that the version of the library that we linked against is
    // compatible with the version of the headers we compiled against.
    GOOGLE_PROTOBUF_VERIFY_VERSION;

    // SIGINT registration
    struct sigaction sigIntHandler;
    sigIntHandler.sa_handler = my_handler;
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;
    sigaction(SIGINT, &sigIntHandler, NULL);

    // If an event is found, allow for printing.
    bool eventupdate = false;

    // For debugging
    int printcounter = 0;

    // I would like to create the object here instead...
    //Robotmessage robotdata;

    // This is the joystick part of the message
    Simplecontroller payload;

    string joystickname = "joy_1";
    const int joystickid = 12345;

    // Joystick characteristics
    payload.set_name(joystickname);
    payload.set_id(joystickid);
    // Joystick axis doubles
    payload.set_leftstickx(0);
    payload.set_leftsticky(0);
    payload.set_rightstickx(0);
    payload.set_rightsticky(0);
    payload.set_lefttrigger(0);
    payload.set_righttrigger(0);
    // buttons
    payload.set_leftstickbutton(true);
    payload.set_rightstickbutton(true);
    //
    payload.set_xbutton(false);
    payload.set_abutton(false);
    payload.set_ybutton(true);
    payload.set_bbutton(true);
    //
    payload.set_rightbutton(false);
    payload.set_leftbutton(false);
    //
    payload.set_startbutton(true);
    payload.set_backbutton(false);
    payload.set_centbutton(true);
    //
    payload.set_pov(11);

    // Keep reading the state of the joystick in a loop
    while (true) {

        // Create the new payload object
        Robotmessage robotdata;
        Simplecontroller * pointeddata = &payload;

        // Now we add the joystick payload to the full data holder.
        robotdata.Clear();
        robotdata.set_msgheader("robot_drive");
        robotdata.set_allocated_heartbt(NULL);
        // This line works
        robotdata.set_allocated_control(NULL);
        // THIS LINE DOES NOT WORK!!!!!!!
        robotdata.set_allocated_control(pointeddata);
        // Show that the script worked.
        if (printcounter++ > 2000)
        {
            cout << "I worked." << endl;
        }
        // Now we clear the object
        //payload.Clear();
        // Delay so that the socket does not die for some odd reason.
        sleepApp(9); // milliseconds
    }

    google::protobuf::ShutdownProtobufLibrary();
    return 0;
}

Here are the .proto files that I am using: Heartbeat.proto:


syntax = "proto3";

// protoc --cpp_out=./ SimpleController.proto

message Heartbeat {
    // Identifiers
    int32 beat = 1;
}

Robotmessage.proto:


syntax = "proto3";

// I need to import the other message definitions
import "Heartbeat.proto";
import "SimpleController.proto";

// protoc --cpp_out=./ SimpleController.proto

message Robotmessage {
    // Identifiers
    string msgheader = 1;
    oneof robmessage {
        Heartbeat heartbt = 2;  
        Simplecontroller control = 3;
    }
}

SimpleController.proto:


syntax = "proto3";

// protoc --cpp_out=./ SimpleController.proto

message Simplecontroller {
    // https://stackoverflow.com/questions/9496101/protocol-buffer-over-socket-in-c\

    // Identifiers
    string name = 1;
    int32 id = 2;
    // 2 axis throttles with integrated buttons
    double leftstickX = 3;
    double leftstickY = 4;
    bool leftstickbutton = 5;
    double rightstickX = 6;
    double rightstickY = 7;
    bool rightstickbutton = 8;
    // 2 extra throtle axis
    double lefttrigger = 9;
    double righttrigger = 10;
    // grid of 4 buttons
    bool xbutton = 11;
    bool abutton = 12;
    bool bbutton = 13;
    bool ybutton = 14;
    // buttons next to triggers.
    bool leftbutton  = 15;
    bool rightbutton = 16;
    // butons next to center logo.
    bool startbutton = 17;
    bool backbutton = 18;
    // The center button
    bool centbutton = 19;
    // The hat on the left
    int32 pov = 20;
}

Here is the CMakeLists.txt file that I am using to build the code:

set(PROJECT_NAME test_protobuf)
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
PROJECT( ${PROJECT_NAME} LANGUAGES C CXX )
message(STATUS "Is the C++ compiler loaded? ${CMAKE_CXX_COMPILER_LOADED}")
if(CMAKE_CXX_COMPILER_LOADED)
message(STATUS "The C++ compiler ID is: ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Is the C++ from GNU: ${CMAKE_COMPILER_IS_GNUCCX}")
message(STATUS "The C++ compiler version is: ${CMAKE_CXX_COMPILER_Version}")
endif()

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_definitions(-std=c++11)
# CMAKE_CURRENT_SOURCE_DIR - This is the directory where CMake is running.
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

find_package( Boost REQUIRED system thread timer chrono)
if(Boost_FOUND)
    message("Boost was found.")
    message(${Boost_INCLUDE_DIR})
endif()

set(THE_USER $ENV{USER})
message("This is the com user_:" ${THE_USER})

set(PROGRAMS_TO_COMPILE 
demobrokeprotobuf
)

set(PROTO_MESSAGES
Robotmessage.proto
SimpleController.proto
Heartbeat.proto
)

# These lines are for autogenerating code from the *.proto files with CMake.
find_package(Protobuf REQUIRED)
if(PROTOBUF_FOUND)
    message("Google Protobuf has been found.")
endif()

# Make sure protoc is present, as apparently the above find_package() doesn't check that.
find_program(Protobuf_PROTOC_LOC NAMES protoc)
if (NOT Protobuf_PROTOC_LOC)
  message(FATAL_ERROR "Cannot find required 'protoc', cannot process Protobuf files without it. Aborting.")
endif()

# https://cmake.org/cmake/help/git-master/module/FindProtobuf.html
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_MESSAGES})

foreach(aprogram ${PROGRAMS_TO_COMPILE})
    add_executable(${aprogram} ${PROTO_SRCS} ${PROTO_HDRS} ${PROJECT_SOURCE_DIR}/${aprogram}.cpp)
    target_link_libraries(${aprogram} ${Boost_LIBRARIES})
    target_link_libraries(${aprogram} ${PROTOBUF_LIBRARIES})
    # include directories
    # We only include the directories for these specific executables.
    target_include_directories(${aprogram} PRIVATE ${Protobuf_INCLUDE_DIRS})
    target_include_directories(${aprogram} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endforeach()

# Links I have refered to
# https://cmake.org/examples/
# https://stackoverflow.com/questions/20824194/cmake-with-google-protocol-buffers
# https://stackoverflow.com/questions/32647517/protocol-buffer-with-cmakelists
# https://stackoverflow.com/questions/38408486/how-to-build-google-protobuf-environment-with-cmake-on-windows

And the build.sh file that I use to compile everything:

#!/bin/bash
cmake -H. -Bbuild
availablethreads=`nproc`
cmake --build build -- -j$availablethreads -l$availablethreads

Then, finally, the error:

*** Error in `./build/demobrokeprotobuf': double free or corruption (out): 0x00007ffdad290950 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fd30e5267e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fd30e52f37a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fd30e53353c]
./build/demobrokeprotobuf[0x40733d]
./build/demobrokeprotobuf[0x403ce0]
./build/demobrokeprotobuf[0x4062fd]
./build/demobrokeprotobuf[0x403bbe]
./build/demobrokeprotobuf[0x40d114]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fd30e4cf830]
./build/demobrokeprotobuf[0x4035c9]
======= Memory map: ========
00400000-00413000 r-xp 00000000 103:03 13111091                          /home/$USER/demobrokenprotobuf/build/demobrokeprotobuf
00612000-00613000 r--p 00012000 103:03 13111091                          /home/$USER/demobrokenprotobuf/build/demobrokeprotobuf
00613000-00614000 rw-p 00013000 103:03 13111091                          /home/$USER/demobrokenprotobuf/build/demobrokeprotobuf
0148d000-014bf000 rw-p 00000000 00:00 0                                  [heap]
7fd308000000-7fd308021000 rw-p 00000000 00:00 0 
7fd308021000-7fd30c000000 ---p 00000000 00:00 0 
7fd30df8c000-7fd30e094000 r-xp 00000000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e094000-7fd30e293000 ---p 00108000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e293000-7fd30e294000 r--p 00107000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e294000-7fd30e295000 rw-p 00108000 103:03 34879                     /lib/x86_64-linux-gnu/libm-2.23.so
7fd30e295000-7fd30e2ae000 r-xp 00000000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e2ae000-7fd30e4ad000 ---p 00019000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e4ad000-7fd30e4ae000 r--p 00018000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e4ae000-7fd30e4af000 rw-p 00019000 103:03 35030                     /lib/x86_64-linux-gnu/libz.so.1.2.8
7fd30e4af000-7fd30e66f000 r-xp 00000000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e66f000-7fd30e86f000 ---p 001c0000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e86f000-7fd30e873000 r--p 001c0000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e873000-7fd30e875000 rw-p 001c4000 103:03 34772                     /lib/x86_64-linux-gnu/libc-2.23.so
7fd30e875000-7fd30e879000 rw-p 00000000 00:00 0 
7fd30e879000-7fd30e891000 r-xp 00000000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30e891000-7fd30ea90000 ---p 00018000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30ea90000-7fd30ea91000 r--p 00017000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30ea91000-7fd30ea92000 rw-p 00018000 103:03 31752                     /lib/x86_64-linux-gnu/libpthread-2.23.so
7fd30ea92000-7fd30ea96000 rw-p 00000000 00:00 0 
7fd30ea96000-7fd30eaac000 r-xp 00000000 103:03 34896                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd30eaac000-7fd30ecab000 ---p 00016000 103:03 34896                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd30ecab000-7fd30ecac000 rw-p 00015000 103:03 34896                     /lib/x86_64-linux-gnu/libgcc_s.so.1
7fd30ecac000-7fd30ee1e000 r-xp 00000000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30ee1e000-7fd30f01e000 ---p 00172000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30f01e000-7fd30f028000 r--p 00172000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30f028000-7fd30f02a000 rw-p 0017c000 103:03 8388796                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fd30f02a000-7fd30f02e000 rw-p 00000000 00:00 0 
7fd30f02e000-7fd30f2cd000 r-xp 00000000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f2cd000-7fd30f4cd000 ---p 0029f000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f4cd000-7fd30f4d6000 r--p 0029f000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f4d6000-7fd30f4dc000 rw-p 002a8000 103:03 8922983                   /usr/local/lib/libprotobuf.so.19.0.0
7fd30f4dc000-7fd30f4dd000 rw-p 00000000 00:00 0 
7fd30f4dd000-7fd30f503000 r-xp 00000000 103:03 31751                     /lib/x86_64-linux-gnu/ld-2.23.so
7fd30f6b5000-7fd30f6bb000 rw-p 00000000 00:00 0 
7fd30f700000-7fd30f702000 rw-p 00000000 00:00 0 
7fd30f702000-7fd30f703000 r--p 00025000 103:03 31751                     /lib/x86_64-linux-gnu/ld-2.23.so
7fd30f703000-7fd30f704000 rw-p 00026000 103:03 31751                     /lib/x86_64-linux-gnu/ld-2.23.so
7fd30f704000-7fd30f705000 rw-p 00000000 00:00 0 
7ffdad271000-7ffdad293000 rw-p 00000000 00:00 0                          [stack]
7ffdad306000-7ffdad309000 r--p 00000000 00:00 0                          [vvar]
7ffdad309000-7ffdad30b000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

I replaced my computer user account with $USER. I have narrowed the problem down to this line:

// THIS LINE DOES NOT WORK!!!!!!!
robotdata.set_allocated_control(pointeddata);

I am trying to pass in a protobuf object into another protobuf pointer object and that is totally crashing the program. I do not have any idea why this is crashing. I have gotten rid of all the extra code from my program: this is the minimum to demo my error. I have no idea how to fix this.

I should also mention: if you have not figured out how to do CMake with google protobuf, this is the general format that I always use. It is a pretty good resource IMHO. :)

Upvotes: 1

Views: 1049

Answers (1)

florgeng
florgeng

Reputation: 886

If you want to use set_allocated_XXX you cant use objects with local scope because you pass the ownership of your object with that function. At the end of your first iteration robdata goes out of scope. So it will be destroyed. Since it is the owner of payload, it will also destroy/delete payload.

Instead of:

Simplecontroller payload;
// Keep reading the state of the joystick in a loop
while (true) 
{
   // Create the new payload object
   Robotmessage robotdata;
   Simplecontroller * pointeddata = &payload;
   // THIS LINE DOES NOT WORK!!!!!!!
   robotdata.set_allocated_control(pointeddata);
}

Try:

// Keep reading the state of the joystick in a loop
while (true) 
{
   Simplecontroller* payload = new Simplecontroller();
   // Create the new payload object
   Robotmessage robotdata;
   // THIS LINE SHOULD WORK!!!!!!!
   robotdata.set_allocated_control(payload);
}

Upvotes: 1

Related Questions