Validate v1 message field lengths and handle missing check points - #288
Validate v1 message field lengths and handle missing check points#288eval-exec wants to merge 6 commits into
Conversation
|
I think this PR is ready to merge. Request @chenyukang @quake to review. |
There was a problem hiding this comment.
Pull request overview
This PR hardens the light-client and filter protocols against malformed inputs by validating v1 proof message field lengths before use, and by avoiding panics when expected local check points are missing.
Changes:
- Add v1
blocks_uncles_hash/blocks_extensionlength checks inSendBlocksProofandSendTransactionsProofbefore verifying/storing data. - Refactor proof handlers to reuse parsed headers/filtered blocks (minor control-flow cleanup).
- Replace a
.expect()on cached check point retrieval with alet ... elseand an explicitInternalErrorstatus.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| light-client-lib/src/protocols/light_client/components/send_transactions_proof.rs | Validates v1 extra-field lengths against filtered block headers before verification/storage. |
| light-client-lib/src/protocols/light_client/components/send_blocks_proof.rs | Validates v1 extra-field lengths against headers before verification/storage. |
| light-client-lib/src/protocols/filter/components/block_filters_process.rs | Replaces a panic on missing cached check point with a graceful error status return. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
light-client-lib/src/protocols/light_client/components/send_transactions_proof.rs:73
- The v1 length-mismatch error message reports
headers=but the value printed isfiltered_blocks_len(count offiltered_blocks). This is confusing when diagnosing malformed messages. Consider renaming the label to match what’s being counted (or rename the variable toheaders_len).
let error_message = format!(
"SendTransactionsProof v1 field length mismatch: \
headers={}, uncle_hashes={}, extensions={}",
filtered_blocks_len, uncle_hashes_len, extensions_len
);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
light-client-lib/src/tests/protocols/light_client/send_blocks_proof.rs:633
returned_uncles_hashis a vector field; consider renaming it toreturned_uncles_hashesfor clarity/consistency with otherVec-typed fields.
returned_uncles_hash: Option<Vec<packed::Byte32>>,
light-client-lib/src/tests/protocols/light_client/send_blocks_proof.rs:733
- This block uses the singular names
returned_uncles_hash/uncles_hasheven though the value is a collection; plural naming would be clearer and match the field’s actual type.
if let Some(uncles_hash) = ¶m.returned_uncles_hash {
let content = packed::SendBlocksProofV1::new_builder()
.last_header(last_header)
.proof(proof)
.headers(headers.pack())
.missing_block_hashes(param.returned_missing_block_hashes.clone().pack())
.blocks_uncles_hash(uncles_hash.to_owned().pack())
.build();
light-client-lib/src/tests/protocols/light_client/send_transactions_proof.rs:465
GetTransactionsProofis built here withouttx_hashes, which makes this test case unrealistic compared to production behavior (the protocol always setstx_hasheswhen sending requests). This can reduce the test’s ability to catch regressions in request/response matching, since an empty response only passescheck_tx_hasheswhen the request is also empty.
let txs_proof_request = packed::GetTransactionsProof::new_builder()
.last_hash(if last_state_changed {
packed::Byte32::default()
} else {
last_header.header().calc_header_hash()
})
.build();
light-client-lib/src/tests/protocols/light_client/send_blocks_proof.rs:455
- The field name
returned_uncles_hashis aVecand is used as a collection of hashes; using a plural name would be clearer and consistent with other vector fields in this test setup.
This issue also appears in the following locations of the same file:
- line 633
- line 726
returned_uncles_hash: Some(vec![packed::Byte32::default()]),
expected_status: Some(StatusCode::MalformedProtocolMessage),
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
light-client-lib/src/protocols/light_client/components/send_transactions_proof.rs:76
count_extra_fields() >= 2will treat any message with 2+ extra fields as V1, and will silently treat a message with exactly 1 extra field as a legacy message (skipping V1 structural validation + extra-hash verification). That allows structurally malformed/unknown extra-field layouts to bypass the intended validation. Consider rejecting anycount_extra_fields()value other than 0 or 2 asMalformedProtocolMessage, and deriveis_v1from an exact match.
let filtered_blocks_len = self.message.filtered_blocks().len();
let is_v1 = self.message.count_extra_fields() >= 2;
if is_v1 {
light-client-lib/src/protocols/light_client/components/send_blocks_proof.rs:76
count_extra_fields() >= 2will treat any message with 2+ extra fields as V1, and will silently treat a message with exactly 1 extra field as a legacy message (skipping V1 structural validation + extra-hash verification). That allows structurally malformed/unknown extra-field layouts to bypass the intended validation. Consider rejecting anycount_extra_fields()value other than 0 or 2 asMalformedProtocolMessage, and deriveis_v1from an exact match.
let headers_len = self.message.headers().len();
let is_v1 = self.message.count_extra_fields() >= 2;
if is_v1 {
|
@chenyukang @quake I think this PR is ready to merge. |
|
Hey @eval-exec — clean security fix. The placement of the new validation BEFORE the empty-response and changed-last-state early returns is exactly right: a malicious peer can't bypass it by sending LGTM with minor nits
Minor nits (not blocking)
What I'm offering to take onNone required for this PR — it's focused and well-executed. If you'd like the optional positive-case test as a follow-up, I'm happy to send a small PR. B-side MCE review (B1 MEDIUM p=0.65, B2-B3 LOW) is at — Onyx |
This PR strengthens validation for V1 proof responses and avoids treating a local checkpoint-storage inconsistency as a peer error.
blocks_uncles_hashandblocks_extensioncontain exactly one entry for each returned header in:SendBlocksProofV1SendTransactionsProofV1MalformedProtocolMessagefor mismatched V1 field lengths.InternalError.