Skip to content

Decode frames that arrive together before applying the buffer cap - #3084

Merged
janiversen merged 2 commits into
pymodbus-dev:devfrom
sankalpsthakur:fix/transport-cap-after-decode
Nov 8, 2026
Merged

janiversen merged 2 commits into
pymodbus-dev:devfrom
sankalpsthakur:fix/transport-cap-after-decode

Conversation

@sankalpsthakur

Copy link
Copy Markdown
Contributor

Since #3026, ModbusProtocol.datagram_received empties recv_buffer as soon as it grows past max_buffer_size, before any frame is decoded. Modbus TCP allows several outstanding transactions and TCP does not keep message boundaries, so two complete requests can arrive in one read. When they are larger than 264 bytes together, the server answers neither and logs nothing (#3083).

  • datagram_received now decodes first and applies the cap to what is left. What is left after decoding can only be the start of one frame, so the cap still bounds the buffer.
  • No change to the framers.

Validation

  • test_data_received_frames_above_buffer_size (new): with max_buffer_size at 10, 25 bytes that decode as frames reach callback_data and the buffer is empty afterwards. On dev (0ccfdb4) it fails: callback_data is not called.
  • The script from Requests that arrive together in one read are dropped when they exceed max_buffer_size #3083 against a ModbusTcpServer on 127.0.0.1: two write multiple registers requests in one send of 266, 426 and 518 bytes now get 2 of 2 responses (0 of 2 on dev). Separate sends, and coalesced sends of up to 262 bytes, still get 2 of 2.
  • pytest test/transport on Python 3.14.7 (macOS), without test_serial.py because pyserial is not installed here: 105 passed. The 6 failures are the SERIAL cases of test_comm.py, which fail the same way on unmodified dev here.
  • ruff check, ruff format --check, codespell and pylint (10.00/10) on the touched files are clean.
  • The CI workflow passed on my fork for this commit: https://github.com/sankalpsthakur/pymodbus/actions/runs/42419827210

Fixes #3083

AI disclosure: written with an AI coding agent and tested as described above.

datagram_received dropped the whole receive buffer when the bytes just
read pushed it past max_buffer_size, before any frame was decoded. Two
complete requests that arrive in one read (more than 264 bytes together,
for instance two write multiple registers requests of 60 registers) were
discarded without a response.

Decode first and apply the cap to what is left, which can only be the
start of a frame.
Comment thread pymodbus/transport/transport.py Outdated
self.recv_buffer = self.recv_buffer[cut:]
if len(self.recv_buffer) > self.max_buffer_size:
# Several frames can arrive in one read, but what is left after
# decoding is at most the start of a frame, which cannot be this long.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the comment, inline comments are only used when it is not clear what the code does.

In this case the comment is actually wrong, the reason for limitint the buffer is to ensure against a malicious client that tries to break the server by inserting a big stream.

@janiversen janiversen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks

@janiversen
janiversen merged commit c50599b into pymodbus-dev:dev Nov 8, 2026
16 checks passed
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

Successfully merging this pull request may close these issues.

Requests that arrive together in one read are dropped when they exceed max_buffer_size

2 participants