{"record":{"id":"7eb9a3aa67dde2d2","repo":"linera-io/linera-protocol","slug":"logs-payload-extends-past-receipt-boundary","errorCode":null,"errorMessage":"logs payload extends past receipt boundary","messagePattern":"logs payload extends past receipt boundary","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/proof/mod.rs","lineNumber":404,"sourceCode":"    ensure!(list_header.list, \"receipt must be an RLP list\");\n\n    // Limit reads to the receipt's declared payload.\n    ensure!(\n        data.len() >= list_header.payload_length,\n        \"receipt payload extends past available data\"\n    );\n    let mut data = &data[..list_header.payload_length];\n\n    // Skip: status (0), cumulative_gas_used (1), logs_bloom (2)\n    for i in 0..3 {\n        skip_rlp_item(&mut data).map_err(|e| anyhow!(\"failed to skip receipt field {i}: {e}\"))?;\n    }\n\n    // Decode the logs list\n    let logs_header =\n        alloy_rlp::Header::decode(&mut data).map_err(|e| anyhow!(\"invalid logs list RLP: {e}\"))?;\n    ensure!(logs_header.list, \"logs must be an RLP list\");\n    ensure!(\n        data.len() >= logs_header.payload_length,\n        \"logs payload extends past receipt boundary\"\n    );\n\n    let mut logs_data = &data[..logs_header.payload_length];\n    let mut logs = Vec::new();\n    while !logs_data.is_empty() {\n        logs.push(decode_log(&mut logs_data)?);\n    }\n\n    Ok(logs)\n}\n\n/// Parses a `DepositInitiated` event from a receipt log.\n///\n/// Verifies that `topic[0]` matches the event signature, that the log was emitted by\n/// the `expected_emitter` (bridge contract address), and ABI-decodes the data fields.\n/// The `depositor` field is indexed (stored in `topics[1]`); all other parameters are","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/mod.rs#L386-L422","documentation":"The companion bounds check after 'logs must be an RLP list': the logs list's declared payload_length must fit within the bytes remaining inside the receipt payload. It guards the subsequent `&data[..logs_header.payload_length]` slice against out-of-bounds access when the inner length claim exceeds the outer receipt's size.","triggerScenarios":"Malformed or adversarial RLP where the inner logs length contradicts the outer receipt length; receipts truncated mid-logs; encoding bugs producing nested length mismatches.","commonSituations":"Fuzzing corpus hitting nested-length inconsistencies; a fork or non-standard chain emitting receipts with inconsistent inner lengths; corrupted storage or transport flipping bytes inside the length prefix.","solutions":["Reject the message and re-fetch the receipt from a trusted RPC to compare","If building the bytes yourself, always encode with an RLP library (never concatenate manually) so outer/inner lengths stay consistent","Treat recurring mismatches from one relayer as hostile input"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match decode_receipt_logs(bytes) {\n    Ok(logs) => logs,\n    Err(e) if e.to_string().contains(\"extends past receipt boundary\") => {\n        // nested length inconsistency: treat as hostile/corrupt input\n        anyhow::bail!(\"malformed receipt: inner logs length exceeds outer payload\")\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always produce RLP with a library so nested length prefixes are computed correctly","Refetch and compare when a single message fails this check; persistent failures mean a bad producer","Fuzz the decoder with mutated length fields to confirm it errors instead of panicking"],"tags":["ethereum","rlp","receipt","logs","truncated-data","bridge","rust"],"backgroundTag":"rlp-decoding-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}