Validate FC23 write count against MAX_WRITE_COUNT, not MAX_READ_COUNT - #3016
Conversation
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.
|
While checking that this was the only place a count limit disagrees with the PDU size, I found two more in the same family. Both are behaviour changes rather than a slipped constant, so I left them out of this PR — happy to add either here or in a separate one, whichever you prefer. Measured on 3.15.0, PDU size and the RTU frame
The FC15 change would start rejecting 1969..2000, which currently works against permissive devices, so it is your call rather than mine. |
janiversen
left a comment
There was a problem hiding this comment.
Good catch (copy/paste is nice, but a bit of a devil).
Thanks.
|
Please add the others in another PR or 2 to make it easy to see what happens in each PR. |
ReadWriteMultipleRegistersRequest.encode()checks the write count againstMAX_READ_COUNT(125) instead ofMAX_WRITE_COUNT(121), so the client encodes requests that are over the MODBUS size limit.MAX_WRITE_COUNThas no other reference in the repo.#2997 introduced the constants and rewrote the two literals:
The second line should have been
MAX_WRITE_COUNT. Measured on released 3.15.0 vs 3.14.0 withreadwrite_registers(..., values=[0]*122):ValueError: 1 <= count 122 <= 121 !FramerRTU.buildFrameputs 257 bytes on the wireThe FC23 request PDU is
10 + 2Nbytes, soN = 121is 252 andN = 122is 254, past the 253 byte PDU maximum (RTU ADU 257 > 256). That derivation lands on the same 121 the spec gives as0x0079, and on the same value this PDU's owndatastore_update()already enforces server-side (1 <= write_count <= 0x079) — which is why a pymodbus client can now send a request a pymodbus server answers with ILLEGAL_VALUE.decode()is deliberately left atMAX_READ_COUNT. Raising there makesDecodePDU.decode()swallow the frame and the server answer nothing, replacing a correct exception response with silence. The second test pins that.Existing coverage could not catch this:
test_register_read_requests_count_errorsgoes throughdatastore_update(), the path that was already right, and its out-of-range cases are 2048 and 0, never 122..125.Mutants run against the new tests: revert → the encode test fails; tighten
decode()as well → the decode test fails;MAX_WRITE_COUNT = 125→ the encode test fails. Full suite 1968 → 1970 passing,check_ci.shsteps clean (codespell, ruff check/format, pylint 10.00, zuban).AI assistance was used in preparing this change.