Corrupted double-linked list error in C/C++ application compiled with Legato toolchain

I have a C/C++ application which uses libmodbus (https://github.com/stephane/libmodbus) to read Modbus TCP inputs.

I read the values of each of the 4 Modbus input types (coils, discrete inputs, holding registers, input registers) each into their own vector of a Data wrapper class that I use to package the raw Modbus input values and add things like timestamp and quality.

I then try to concatenate the 4 separate vectors into 1 vector containing all of the Data to return from from my function, but I am getting the error:

myapplication[2066] | | *** Error in `myapplication’: corrupted double-linked list: 0x000702b0 ***

My function looks like this:

std::vector<Data> ModbusTcpClient::GetData ()
{
    // Read all of the Modbus input types
    std::vector<Data> coils = ReadCoils();
    std::vector<Data> discreteInputs = ReadDiscreteInputs();
    std::vector<Data> holdingRegisters = ReadHoldingRegisters();
    std::vector<Data> inputRegisters = ReadInputRegisters();

    // Package all of the data into a single vector to return
    std::vector<Data> all;

    // The corrupted double-linked list error is thrown after this call to reserve
    all.reserve(coils.size() + discreteInputs.size() + holdingRegisters.size() + inputRegisters.size());

    // Never get here
    all.insert(all.end(), coils.begin(), coils.end());
    all.insert(all.end(), discreteInputs.begin(), discreteInputs.end());
    all.insert(all.end(), holdingRegisters.begin(), holdingRegisters.end());
    all.insert(all.end(), inputRegisters.begin(), inputRegisters.end());

    return all;
}

So I figured I would remove the call to reserve the vector space in advance, and see if I could get the code to work with just the inserts.

So I changed it to look like this:

std::vector<Data> ModbusTcpClient::GetData ()
{
    // Read all of the Modbus input types
    std::vector<Data> coils = ReadCoils();
    std::vector<Data> discreteInputs = ReadDiscreteInputs();
    std::vector<Data> holdingRegisters = ReadHoldingRegisters();
    std::vector<Data> inputRegisters = ReadInputRegisters();

    // Package all of the data into a single vector to return
    std::vector<Data> all;

    //all.reserve(coils.size() + discreteInputs.size() + holdingRegisters.size() + inputRegisters.size());

    // The first call to insert executes just fine
    all.insert(all.end(), coils.begin(), coils.end());

    // The corrupted double-linked list error is thrown after this second call to insert
    all.insert(all.end(), discreteInputs.begin(), discreteInputs.end());

    // Never get here
    all.insert(all.end(), holdingRegisters.begin(), holdingRegisters.end());
    all.insert(all.end(), inputRegisters.begin(), inputRegisters.end());

    return all;
}

I have tried a number of things, such as changing the function to manually iterate over each vector and push_back each Data object one at a time, rather than insert, same problem though (not surprisingly).

I also tried to change all of the functions to return Data* arrays instead of std::vector, and tried manually copying the arrays into one big destination array, but I also got the same error.

I am a bit at a loss now, because there isn’t much information out there on this corrupted double-linked list error.

All of the references I can find on it suggest to look for double free / delete statements on the same pointer in your code, but that doesn’t apply here.

Also, my Data class is very simple with all value type members, and no dynamic allocation, if that matters.

Here is a snippet which contains the Data class and the Modbus read functions for each input type:

#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <sys/time.h>
#include <string>
#include <vector>

#include <modbus.h>
class Data
{
public:
    enum InputType
    {
        Coil = 0,
        DiscreteInput = 1,
        HoldingRegister = 2,
        InputRegister = 3,
        None = 4
    };

    enum Quality
    {
        Good = 0,
        Bad = 1,
        Unset = 2
    };

    Data(InputType clientInputType,
            int clientIndex,
            std::string timestamp,
            double value,
            Quality quality);

    InputType GetClientInputType () const;
    int GetClientIndex () const;

    std::string GetTimestamp () const;
    long GetTimestampTicks () const;
    double GetValue () const;
    int GetQuality () const;

private:
    InputType m_clientInputType = InputType::None;
    int m_clientIndex = -1;

    std::string m_timestamp = "";
    double m_value = -1;
    Quality m_quality = Quality::Unset;
};

std::vector<Data> GetData();

std::vector<Data> ReadCoils();
std::vector<Data> ReadDiscreteInputs();
std::vector<Data> ReadHoldingRegisters();
std::vector<Data> ReadInputRegisters();

const int NUM_COILS = 8;
const int NUM_DISCRETE_INPUTS = 8;
const int NUM_HOLDING_REGISTERS = 8;
const int NUM_INPUT_REGISTERS = 8;

static modbus_t* ctx;

int main()
{
    std::vector<Data> data = GetData();
    return 0;
}

Data::Data(Data::InputType clientInputType,
            int clientIndex,
            std::string timestamp,
            double value,
            Data::Quality quality)
{
    m_clientInputType = clientInputType;
    m_clientIndex = clientIndex;
    m_timestamp = timestamp;
    m_value = value;
    m_quality = quality;
}

Data::InputType Data::GetClientInputType() const
{
    return m_clientInputType;
}

int Data::GetClientIndex() const
{
    return m_clientIndex;
}

std::string Data::GetTimestamp() const
{
    return m_timestamp;
}

double Data::GetValue() const
{
    return m_value;
}

int Data::GetQuality() const
{
    return m_quality;
}

std::string ToString(int x)
{
    int length = snprintf(NULL, 0, "%d", x);
    char* buf = new char[length + 1];
    snprintf(buf, length + 1, "%d", x);
    std::string str( buf );
    delete[] buf;
    return str;
}

std::string GetSystemTimestamp()
{
    const int count = 24;
    const char* format = "%Y-%m-%dT%H:%M:%S";
    char buffer[count];

    int millisec;
    struct tm* tm_info;
    struct timeval tv;

    gettimeofday(&tv, NULL);

    // Round to nearest millisec
    millisec = lrint(tv.tv_usec / 1000.0);
    // Allow for rounding up to nearest second
    if (millisec >= 1000)
    {
        millisec -= 1000;
        tv.tv_sec++;
    }
    tm_info = localtime(&tv.tv_sec);

    std::strftime(buffer, count, format, tm_info);

    std::string timestamp = std::string(buffer) + std::string(".");
    if(millisec < 100)
        timestamp += std::string("0");
    timestamp += ToString(millisec) + std::string("Z");
    return timestamp;
}

std::vector<Data> GetData ()
{
    // Read all of the Modbus input types
    std::vector<Data> coils = ReadCoils();
    std::vector<Data> discreteInputs = ReadDiscreteInputs();
    std::vector<Data> holdingRegisters = ReadHoldingRegisters();
    std::vector<Data> inputRegisters = ReadInputRegisters();

    // Package all of the data into a single vector to return
    std::vector<Data> all;
    all.reserve(coils.size() + discreteInputs.size() + holdingRegisters.size() + inputRegisters.size());

    all.insert(all.end(), coils.begin(), coils.end());
    all.insert(all.end(), discreteInputs.begin(), discreteInputs.end());
    all.insert(all.end(), holdingRegisters.begin(), holdingRegisters.end());
    all.insert(all.end(), inputRegisters.begin(), inputRegisters.end());

    return all;
}

std::vector<Data> ReadCoils()
{
    uint8_t coils[NUM_COILS];
    std::memset(coils, 0, NUM_COILS * sizeof(uint8_t));

    // Make sure Modbus TCP context is connected
    int rc = modbus_connect(ctx);
    if(rc == -1)
        return std::vector<Data>();

    int rc = modbus_read_bits(ctx, 0, NUM_COILS, coils);
    if(rc == -1)
        return std::vector<Data>();
    else if(rc != NUM_COILS)
        return std::vector<Data>();

    std::string timestamp = GetSystemTimestamp();
    std::vector<Data> data;
    data.reserve(NUM_COILS);
    for(int i = 0; i < NUM_COILS; i++)
    {
        double value = modbus_get_byte_from_bits(coils, i, 1);
        data.push_back(Data(Data::InputType::Coil, i, timestamp, value, Data::Quality::Good));
    }
    return data;
}

std::vector<Data> ReadDiscreteInputs()
{
    uint8_t discreteInputs[NUM_DISCRETE_INPUTS];
    std::memset(discreteInputs, 0, NUM_DISCRETE_INPUTS * sizeof(uint8_t));

    // Make sure Modbus TCP context is connected
    int rc = modbus_connect(ctx);
    if(rc == -1)
        return std::vector<Data>();

    rc = modbus_read_input_bits(ctx, 0, NUM_DISCRETE_INPUTS, discreteInputs);
    if(rc == -1)
        return std::vector<Data>();
    else if(rc != NUM_DISCRETE_INPUTS)
        return std::vector<Data>();

    std::string timestamp = GetSystemTimestamp();
    std::vector<Data> data;
    data.reserve(NUM_DISCRETE_INPUTS);
    for(int i = 0; i < NUM_DISCRETE_INPUTS; i++)
    {
        double value = modbus_get_byte_from_bits(discreteInputs, i, 1);
        data.push_back(Data(Data::InputType::DiscreteInput, i, timestamp, value, Data::Quality::Good));
    }

    return data;
}

std::vector<Data> ReadHoldingRegisters()
{
    uint16_t holdingRegisters[NUM_HOLDING_REGISTERS];
    std::memset(holdingRegisters, 0, NUM_HOLDING_REGISTERS * sizeof(uint16_t));

    // Make sure Modbus TCP context is connected
    int rc = modbus_connect(ctx);
    if(rc == -1)
        return std::vector<Data>();

    rc = modbus_read_registers(ctx, 0, NUM_HOLDING_REGISTERS, holdingRegisters);
    if(rc == -1)
        return std::vector<Data>();
    else if(rc != NUM_HOLDING_REGISTERS)
        return std::vector<Data>();

    std::string timestamp = GetSystemTimestamp();
    std::vector<Data> data;
    data.reserve(NUM_HOLDING_REGISTERS);
    for(int i = 0; i < NUM_HOLDING_REGISTERS; i++)
    {
        data.push_back(Data(Data::InputType::HoldingRegister, i, timestamp, holdingRegisters[i], Data::Quality::Good));
    }

    return data;
}

std::vector<Data> ReadInputRegisters()
{
    uint16_t inputRegisters[NUM_INPUT_REGISTERS];
    std::memset(inputRegisters, 0, NUM_INPUT_REGISTERS * sizeof(uint16_t));

    // Make sure Modbus TCP context is connected
    int rc = modbus_connect(ctx);
    if(rc == -1)
        return std::vector<Data>();

    rc = modbus_read_input_registers(ctx, 0, NUM_INPUT_REGISTERS, inputRegisters);
    if(rc == -1)
        return std::vector<Data>();
    else if(rc != NUM_INPUT_REGISTERS)
        return std::vector<Data>();

    std::string timestamp = GetSystemTimestamp();
    std::vector<Data> data;
    data.reserve(NUM_INPUT_REGISTERS);
    for(int i = 0; i < NUM_INPUT_REGISTERS; i++)
    {
        data.push_back(Data(Data::InputType::InputRegister, i, timestamp, inputRegisters[i], Data::Quality::Good));
    }

    return data;
}

The only other requirement for the snippet above to run should be to compile and link to the libmodbus library.

I circumvented this problem by switching to using Date** dynamic arrays instead of std::vector, however I have also encountered this problem a couple other times doing seemingly unrelated things such as writing to a log file (maybe the logging API I am using uses a standard data structure which has similar problems to what it appears vector has).

I have ran the same exact code (sans Legato proprietary stuff like COMPONENT_INIT) with zero issues on both Windows and Ubuntu as simple executable applications.

Has anyone experienced behavior like this?

Hi @cmccarthy,
I built the code provided by you as a legato application and was able to run the application successfully without any errors on the device with few minor modifications like removing the modbus calls(as this error is not related to modbus) for the compilation to go through, removed the main() function and added the COMPONENT_INIT to build as legato application.

See that i have attached full legato applicationdoubleLinked.zip (116.3 KB) which is built using your code few modifications and run successfully on the device.

FYI we used WP8548 device and legato-16.10.4 framework to build and test the application.

One question, How did you build the modbus library? Using which toolchain?

FYI below is the link for suggestions to resolve the corrupted double-linked list if you are still facing the same error and not gone through this link:

Regards,
Muralidhara N.