Buffer overflow after several repeated connections between master and slave using the tcp protocol.
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;
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!
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.
Thank you @psychon for cathing the missing modbus_close(ctx) in @GodLiu1 application.