From 46ebb76f14c9176e063b760dd22be114941af8a9 Mon Sep 17 00:00:00 2001 From: Dylan Pulver Date: Tue, 1 Sep 2026 12:27:57 +0300 Subject: [PATCH] Validate FC23 write count against MAX_WRITE_COUNT ReadWriteMultipleRegistersRequest.encode() checked the write count against MAX_READ_COUNT (125) rather than MAX_WRITE_COUNT (121), so the client could encode a request larger than the 253 byte MODBUS PDU maximum. MAX_WRITE_COUNT had no other reference in the repo. decode() keeps MAX_READ_COUNT on purpose: raising there makes DecodePDU.decode() drop the frame, so the server would answer nothing instead of ILLEGAL_VALUE. --- pymodbus/pdu/register_message.py | 2 +- test/pdu/test_register_read_messages.py | 29 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/pymodbus/pdu/register_message.py b/pymodbus/pdu/register_message.py index 3832728dc..6186a0af8 100644 --- a/pymodbus/pdu/register_message.py +++ b/pymodbus/pdu/register_message.py @@ -131,7 +131,7 @@ def encode(self) -> bytes: self.verifyAddress(address=self.read_address) self.verifyAddress(address=self.write_address) self.verifyCount(self.MAX_READ_COUNT, count=self.read_count) - self.verifyCount(self.MAX_READ_COUNT, count=self.write_count) + self.verifyCount(self.MAX_WRITE_COUNT, count=self.write_count) result = struct.pack( ">HHHHB", self.read_address, diff --git a/test/pdu/test_register_read_messages.py b/test/pdu/test_register_read_messages.py index 0d04056e4..24a57349b 100644 --- a/test/pdu/test_register_read_messages.py +++ b/test/pdu/test_register_read_messages.py @@ -1,5 +1,6 @@ """Test register read messages.""" +import struct from unittest import mock import pytest @@ -84,6 +85,34 @@ def test_register_read_response_decode_error(self): reg.decode(b"\x14\x00\x03\x00\x11") assert exc_info.value.fcode == reg.function_code + def test_readwrite_encode_write_count_limit(self): + """Encoding must reject a write count above the FC23 maximum of 121. + + 121 write registers is a 252 byte PDU, 122 is 254 and does not fit in + the 253 byte MODBUS PDU. + """ + + def build(count): + return ReadWriteMultipleRegistersRequest( + read_address=1, + read_count=1, + write_address=1, + write_registers=[0] * count, + ) + + assert len(build(121).encode()) + 1 <= 253 + with pytest.raises(ValueError): # noqa: PT011 + build(122).encode() + + def test_readwrite_decode_write_count_above_limit(self): + """Decoding must accept 122..125 so the server can answer ILLEGAL_VALUE. + + Raising here would make the server drop the frame without a response. + """ + request = ReadWriteMultipleRegistersRequest() + request.decode(struct.pack(">HHHHB", 1, 1, 1, 122, 244) + b"\x00\x00" * 122) + assert request.write_count == 122 + async def test_register_read_requests_count_errors(self, mock_server_context): """This tests that the register request messages.