{"record":{"id":"049bbccd4a5ec931","repo":"YunaiV/ruoyi-vue-pro","slug":"createdecodeparser-length-d-framebodyle","errorCode":null,"errorMessage":"[createDecodeParser][帧长度异常，length: %d, frameBodyLength: %d]","messagePattern":"\\[createDecodeParser\\]\\[帧长度异常，length: (.+?), frameBodyLength: (.+?)\\]","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/length/IotTcpLengthFieldFrameCodec.java","lineNumber":81,"sourceCode":"\n    @Override\n    public RecordParser createDecodeParser(Handler<Buffer> handler) {\n        // 创建状态机：先读取头部，再读取消息体\n        RecordParser parser = RecordParser.newFixed(headerLength);\n        parser.maxRecordSize(MAX_FRAME_LENGTH); // 设置最大记录大小，防止 DoS 攻击\n        final AtomicReference<Integer> bodyLength = new AtomicReference<>(null); // 消息体长度，null 表示读取头部阶段\n        final AtomicReference<Buffer> headerBuffer = new AtomicReference<>(null); // 头部消息\n\n        // 处理读取到的数据\n        parser.handler(buffer -> {\n            if (bodyLength.get() == null) {\n                // 阶段 1: 读取头部，解析长度字段\n                headerBuffer.set(buffer.copy());\n                int length = readLength(buffer, lengthFieldOffset, lengthFieldLength);\n                int frameBodyLength = length + lengthAdjustment;\n                // 检查帧长度是否合法\n                if (frameBodyLength < 0) {\n                    throw new IllegalStateException(String.format(\n                            \"[createDecodeParser][帧长度异常，length: %d, frameBodyLength: %d]\",\n                            length, frameBodyLength));\n                }\n                // 消息体为空，抛出异常\n                if (frameBodyLength == 0) {\n                    throw new IllegalStateException(\"[createDecodeParser][消息体不能为空]\");\n                }\n\n                // 【重要】切换到读取消息体模式\n                bodyLength.set(frameBodyLength);\n                parser.fixedSizeMode(frameBodyLength);\n            } else {\n                // 阶段 2: 读取消息体，组装完整帧\n                Buffer frame = processFrame(headerBuffer.get(), buffer);\n                // 重置状态，准备读取下一帧\n                bodyLength.set(null);\n                headerBuffer.set(null);\n                parser.fixedSizeMode(headerLength);","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/YunaiV/ruoyi-vue-pro/blob/0418084e222612af2fc1141f566af454f9236ab1/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/codec/length/IotTcpLengthFieldFrameCodec.java#L63-L99","documentation":"During length-field decoding the codec computes frameBodyLength = readLength(...) + lengthAdjustment and rejects a NEGATIVE result as an illegal frame. A negative value means the decoded length field plus the configured adjustment underflows int — a malformed/malicious frame, a wrong lengthFieldOffset/Length (bytes misread), wrong byte order, or a badly-set negative lengthAdjustment.","triggerScenarios":"Device sends a length field whose decoded value + lengthAdjustment < 0; lengthFieldOffset/Length wrong so unrelated header bytes are interpreted as the length; lengthAdjustment set to a large negative number; signed vs unsigned misinterpretation; malicious oversized length field that wraps.","commonSituations":"Codec parameters (lengthFieldOffset, lengthFieldLength, lengthAdjustment) do not match the device protocol spec; device uses a length field that includes its own header bytes but adjustment wasn't set; endian mismatch.","solutions":["Verify lengthFieldOffset, lengthFieldLength, and lengthAdjustment against the device's wire-format datasheet (capture a known-good frame with Wireshark and decode by hand).","Check byte order / signedness: readLength uses getUnsignedByte/getUnsignedShort/getInt — getInt is signed, so values > 2^31 appear negative; use a smaller field or guard the upper bit.","If lengthAdjustment is intentional, recompute it: adjustment = (bytes-after-length-field not counted in length) - (length-field-value offset).","Add an upper-bound check (frameBodyLength > MAX_FRAME_LENGTH) in addition to the negative check for robustness."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// validate codec params against the protocol spec at construction time\nif (lengthFieldLength != 1 && lengthFieldLength != 2 && lengthFieldLength != 4) {\n    throw new IllegalArgumentException(\"lengthFieldLength 必须是 1、2 或 4\");\n}\nif (lengthFieldOffset < 0) {\n    throw new IllegalArgumentException(\"lengthFieldOffset 不能为负\");\n}\n// and bound the decoded length at runtime (see exampleFix in enriched)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Capture a known-good frame and decode the length field by hand to lock down offset/length/adjustment.","Remember getInt (4-byte) is SIGNED — guard the high bit or use a 2-byte field for large frames.","Add both a lower bound (>=0, already present) and an upper bound (<= MAX_FRAME_LENGTH) check.","Treat a persistent negative-length error as a protocol-spec mismatch, not a device bug, until verified."],"tags":["tcp","codec","length-field","validation"],"backgroundTag":null,"analyzedSha":"0418084e222612af2fc1141f566af454f9236ab1","analyzedAt":"2026-08-14T00:56:18.412Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}