libmodbus icon indicating copy to clipboard operation
libmodbus copied to clipboard

Buffer overflow after several repeated connections between master and slave using the tcp protocol.

Open GodLiu1 opened this issue 1 year ago • 2 comments

libmodbus version

3.1.10

OS and/or distribution

Ubuntu20.04

Environment

arm64

Description

Jetson Xavier NX

Actual behavior if applicable

The client connection from 127.0.0.1 is accepted Waiting for an indication... <03><F7><00><00><00><06><FF><03><00><00><00><07> [03][F7][00][00][00][11][FF][03][0E][00][00][00][02][00][03][00][04][00][05][00][14][00][01] cnt=1015 The client connection from 127.0.0.1 is accepted Waiting for an indication... *** buffer overflow detected ***: terminated Aborted (core dumped)

Expected behavior or suggestion

Using tcp communication, after the master and slave have repeatedly reconnected more than 1015 times, a memory overflow interrupt occurs on the slave side, interrupted at rc = modbus_receive(ctx, query); function

Steps to reproduce the behavior (commands or source code)

slave code:

ctx = modbus_new_tcp("127.0.0.1", 1502);
if (ctx == NULL) {
    fprintf(stderr, "Failed to create the libmodbus context\n");
    return -1;
}

query = (uint8_t *)malloc(MODBUS_TCP_MAX_ADU_LENGTH);
if (query == NULL) {
    fprintf(stderr, "Failed to allocate memory for query\n");
    modbus_free(ctx);
    return -1;
}

header_length = modbus_get_header_length(ctx);
modbus_set_debug(ctx, TRUE);

mb_mapping = modbus_mapping_new_start_address(0, 64,
                                              0, 64,
                                              0, 64,
                                              0, 64);
if (mb_mapping == NULL) {
    fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    free(query);
    return -1;
}

mb_mapping->tab_registers[0] = 0;
mb_mapping->tab_registers[1] = 2;
mb_mapping->tab_registers[2] = 3;
mb_mapping->tab_registers[3] = 4;
mb_mapping->tab_registers[4] = 5;
mb_mapping->tab_registers[5] = 20;
mb_mapping->tab_registers[6] = 1;

s = modbus_tcp_listen(ctx, 1);
if (s == -1) {
    fprintf(stderr, "Failed to listen: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    modbus_mapping_free(mb_mapping);
    free(query);
    return -1;
}
int cnt=1;
while (true) {
    modbus_tcp_accept(ctx, &s);

    rc = modbus_receive(ctx, query);
    if (rc == -1) {
        fprintf(stderr, "Receive failed: %s\n", modbus_strerror(errno));
        break;
    }

    rc = modbus_reply(ctx, query, rc, mb_mapping);
    if (rc == -1) {
        fprintf(stderr, "Reply failed: %s\n", modbus_strerror(errno));
        break;
    }
    std::cout<<"cnt="<<cnt++<<std::endl;
}

close(s);
modbus_mapping_free(mb_mapping);
free(query);
modbus_free(ctx);

return 0;

master code:

// 创建 Modbus 上下文
ctx = modbus_new_tcp("127.0.0.1", 1502); // IP地址和端口

if (ctx == NULL)
{
    std::cerr << "Unable to allocate libmodbus context\n";
    return -1;
}

// 创建从站的寄存器表
modbus_mapping_t *mb_mapping;
mb_mapping = modbus_mapping_new_start_address(0, 64, 0, 64, 0, 64, 0, 64);

if (mb_mapping == NULL)
{
    std::cerr << "Failed to allocate the mapping: " << modbus_strerror(errno) << "\n";
    modbus_free(ctx);
    return -1;
}

while (true)
{
    // 连接到 Modbus 从站
    if (modbus_connect(ctx) == -1)
    {
        std::cerr << "Connection failed: " << modbus_strerror(errno) << "\n";
        break;
    }

    // 读取保持寄存器(示例:寄存器地址0,读取7个寄存器)
    rc = modbus_read_registers(ctx, 0, 7, query);

    if (rc == -1)
    {
        std::cerr << "Read failed: " << modbus_strerror(errno) << "\n";
        modbus_close(ctx); // 关闭连接
        break;
    }

    // 存储读取到的寄存器值到 Modbus 寄存器表
    for (int i = 0; i < rc; i++)
    {
        mb_mapping->tab_registers[i] = query[i];
    }

    // 打印 Modbus 寄存器表中的值
    std::cout << "Modbus register values: ";
    for (int i = 0; i < rc; i++)
    {
        std::cout << mb_mapping->tab_registers[i] << " ";
    }
    std::cout << std::endl;

    modbus_close(ctx); // 关闭连接
    usleep(50000); // 等待200毫秒后再进行下一次读取
}

// 清理资源
modbus_free(ctx);
modbus_mapping_free(mb_mapping);

return 0;

GodLiu1 avatar Jul 22 '24 10:07 GodLiu1

The problem is easy to reproduce, that is, repeatedly disconnecting and reconnecting will result in a memory overflow 1015 times on time, it should be that the modbus_receive() function internally shifted the pointer, I have looked at the source code, the problem should be found in line 350 of modbus.c in int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type), but unfortunately I haven't found the exact problem so far!

GodLiu1 avatar Jul 22 '24 11:07 GodLiu1

I turned these code snippets into runnable programs in the quickest possible way (= do not expect anything pretty)

master.cpp:

#define BE_FASTER
#include <modbus.h>
#include <cerrno>
#include <iostream>
int main() {
// 创建 Modbus 上下文
modbus_t *ctx = modbus_new_tcp("127.0.0.1", 1502); // IP地址和端口

if (ctx == NULL)
{
    std::cerr << "Unable to allocate libmodbus context\n";
    return -1;
}

// 创建从站的寄存器表
modbus_mapping_t *mb_mapping;
mb_mapping = modbus_mapping_new_start_address(0, 64, 0, 64, 0, 64, 0, 64);

if (mb_mapping == NULL)
{
    std::cerr << "Failed to allocate the mapping: " << modbus_strerror(errno) << "\n";
    modbus_free(ctx);
    return -1;
}

while (true)
{
    // 连接到 Modbus 从站
    if (modbus_connect(ctx) == -1)
    {
        std::cerr << "Connection failed: " << modbus_strerror(errno) << "\n";
        break;
    }

    // 读取保持寄存器(示例:寄存器地址0,读取7个寄存器)
    uint16_t query[1234];
    int rc = modbus_read_registers(ctx, 0, 7, query);

    if (rc == -1)
    {
        std::cerr << "Read failed: " << modbus_strerror(errno) << "\n";
        modbus_close(ctx); // 关闭连接
        break;
    }

    // 存储读取到的寄存器值到 Modbus 寄存器表
    for (int i = 0; i < rc; i++)
    {
        mb_mapping->tab_registers[i] = query[i];
    }

    // 打印 Modbus 寄存器表中的值
#ifndef BE_FASTER
    std::cout << "Modbus register values: ";
    for (int i = 0; i < rc; i++)
    {
        std::cout << mb_mapping->tab_registers[i] << " ";
    }
    std::cout << std::endl;
#endif

    modbus_close(ctx); // 关闭连接
#ifndef BE_FASTER
    usleep(50000); // 等待200毫秒后再进行下一次读取
#endif
}

// 清理资源
modbus_free(ctx);
modbus_mapping_free(mb_mapping);

return 0;
}

slave.cpp:

#include <modbus.h>
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <iostream>
int main() {
modbus_t *ctx = modbus_new_tcp("127.0.0.1", 1502);
if (ctx == NULL) {
    fprintf(stderr, "Failed to create the libmodbus context\n");
    return -1;
}

uint8_t* query = (uint8_t *)malloc(MODBUS_TCP_MAX_ADU_LENGTH);
if (query == NULL) {
    fprintf(stderr, "Failed to allocate memory for query\n");
    modbus_free(ctx);
    return -1;
}

int header_length = modbus_get_header_length(ctx);
modbus_set_debug(ctx, TRUE);

modbus_mapping_t *mb_mapping = modbus_mapping_new_start_address(0, 64,
                                              0, 64,
                                              0, 64,
                                              0, 64);
if (mb_mapping == NULL) {
    fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    free(query);
    return -1;
}

mb_mapping->tab_registers[0] = 0;
mb_mapping->tab_registers[1] = 2;
mb_mapping->tab_registers[2] = 3;
mb_mapping->tab_registers[3] = 4;
mb_mapping->tab_registers[4] = 5;
mb_mapping->tab_registers[5] = 20;
mb_mapping->tab_registers[6] = 1;

int s = modbus_tcp_listen(ctx, 1);
if (s == -1) {
    fprintf(stderr, "Failed to listen: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    modbus_mapping_free(mb_mapping);
    free(query);
    return -1;
}
int cnt=1;
while (true) {
    modbus_tcp_accept(ctx, &s);

    int rc = modbus_receive(ctx, query);
    if (rc == -1) {
        fprintf(stderr, "Receive failed: %s\n", modbus_strerror(errno));
        break;
    }

    rc = modbus_reply(ctx, query, rc, mb_mapping);
    if (rc == -1) {
        fprintf(stderr, "Reply failed: %s\n", modbus_strerror(errno));
        break;
    }
    std::cout<<"cnt="<<cnt++<<std::endl;
}

close(s);
modbus_mapping_free(mb_mapping);
free(query);
modbus_free(ctx);

return 0;
}

Running these programs (against Debian's libmodbus5 3.1.10-2) ends up with slave.cpp eventually failing:

Waiting for an indication...
ERROR The connection is not established.
Receive failed: Too many open files

After adding the missing modbus_close(ctx); to slave.cpp, things just work for me. I aborted things at cnt=805733.

Edit: I did another attempt without my BE_FASTER define. Waiting for this to count to 1015 is not soooo bad. Still no crash.

psychon avatar Sep 22 '24 13:09 psychon

Thank you @psychon for cathing the missing modbus_close(ctx) in @GodLiu1 application.

stephane avatar Oct 22 '24 08:10 stephane