FinalChecks bounds each directive by Svc.Fpy.MAX_DIRECTIVE_SIZE (2048, which sizes the in-memory directive structs), but a statement's arguments travel in an Fw::StatementArgBuffer whose capacity is FW_STATEMENT_ARG_BUFFER_MAX_SIZE = FW_COM_BUFFER_MAX_SIZE - sizeof(FwOpcodeType) - sizeof(FwPacketDescriptorType) = 506, so a directive between 507 and 2045 bytes compiles with no error or warning and is then rejected by the sequencer at load with a raw deserialize error. CONST_CMD is worse: FinalChecks bounds command arguments by FW_CMD_ARG_BUFFER_MAX_SIZE (506), but the statement buffer also holds the 4-byte FwOpcodeType, so the real ceiling is 502 — and every RUN_ARGS/VALIDATE_ARGS in the Ref dictionary has a maximum argument payload of 505-506 bytes, so a seq-run call with a long enough child path lands in the failing window while passing every compiler check. The fix is to check each directive's argument bytes against FW_STATEMENT_ARG_BUFFER_MAX_SIZE from the dictionary, accounting for the opcode that shares the buffer in the CONST_CMD case.
import fpy.test_helpers as th
for n in (506, 507): # PUSH_VAL carrying the message bytes
_, dirs, _ = th.compile_seq('log("%s")\n' % ("A" * n))
th.run_seq(None, dirs)
for path_len in (237, 238): # CONST_CMD: 4-byte opcode + (2 + path_len) + 263
seq = 'Ref.cmdSeq0.VALIDATE_ARGS("%s", Svc.SeqArgs())\n' % ("a" * path_len)
_, dirs, _ = th.compile_seq(seq)
th.run_seq(None, dirs)
| case |
statement argBuf |
result |
log() of 506 bytes |
506 |
loads and runs |
log() of 507 bytes |
507 |
validation fails |
| command with 502 argument bytes |
506 |
loads and runs |
| command with 503 argument bytes |
507 |
validation fails |
FileReadDeserializeError : Deserialize error encountered while reading BODY (1) of file s0.bin: 5 (517 bytes left out of 528)
The compiler only objects at 2046+ bytes, naming the wrong limit: Directive PUSH_VAL in sequence too large (expected at most 2048 bytes, was 2103).
Related: #224 is the same code path scoped to FwSizeStoreType = U8 dictionaries (where the symptom is a struct.error crash rather than a load failure) and names FwSizeStoreType's range as the limit; a fix that checks only what #224 describes would leave the default-config bug in place. #203's body says a long log() message compiles and is truncated at run time — above 506 bytes the sequence does not run at all.
FinalChecksbounds each directive bySvc.Fpy.MAX_DIRECTIVE_SIZE(2048, which sizes the in-memory directive structs), but a statement's arguments travel in anFw::StatementArgBufferwhose capacity isFW_STATEMENT_ARG_BUFFER_MAX_SIZE=FW_COM_BUFFER_MAX_SIZE - sizeof(FwOpcodeType) - sizeof(FwPacketDescriptorType)= 506, so a directive between 507 and 2045 bytes compiles with no error or warning and is then rejected by the sequencer at load with a raw deserialize error.CONST_CMDis worse:FinalChecksbounds command arguments byFW_CMD_ARG_BUFFER_MAX_SIZE(506), but the statement buffer also holds the 4-byteFwOpcodeType, so the real ceiling is 502 — and everyRUN_ARGS/VALIDATE_ARGSin the Ref dictionary has a maximum argument payload of 505-506 bytes, so a seq-run call with a long enough child path lands in the failing window while passing every compiler check. The fix is to check each directive's argument bytes againstFW_STATEMENT_ARG_BUFFER_MAX_SIZEfrom the dictionary, accounting for the opcode that shares the buffer in theCONST_CMDcase.log()of 506 byteslog()of 507 bytesThe compiler only objects at 2046+ bytes, naming the wrong limit:
Directive PUSH_VAL in sequence too large (expected at most 2048 bytes, was 2103).Related: #224 is the same code path scoped to
FwSizeStoreType = U8dictionaries (where the symptom is astruct.errorcrash rather than a load failure) and namesFwSizeStoreType's range as the limit; a fix that checks only what #224 describes would leave the default-config bug in place. #203's body says a longlog()message compiles and is truncated at run time — above 506 bytes the sequence does not run at all.