Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
GodLiu1 opened this issue Jul 22, 2024 · 3 comments

Comments

@GodLiu1
Copy link

GodLiu1 commented Jul 22, 2024

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><00><00><00><06><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
Copy link
Author

GodLiu1 commented Jul 22, 2024

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!

@psychon
Copy link

psychon commented Sep 22, 2024

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.

@stephane
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants