Assessment reports>Blobstream X>Critical findings>Inputs to ,prove_subchain, are unconstrained
Category: Coding Mistakes

Inputs to prove_subchain are unconstrained

Critical Severity
Critical Impact
High Likelihood

Description

The prove_data_commitment makes multiple calls to prove_subchain and merges the results together. The inputs for these calls are generated via the hints mechanism.

let start_block =
    builder.add(map_ctx.start_block, map_relative_block_nums.as_vec()[0]);
let last_block = builder.add(
    map_ctx.start_block,
    map_relative_block_nums.as_vec()[BATCH_SIZE - 1],
);

// The query_end_block = min(batch_end_block, global_end_block) for fetching the data commitment inputs.
// If batch_end_block > global_end_block, data_comm_proof will be filled with dummy values.
// This is because prove_subchain only checks up to global_end_block, and doing so reduces RPC calls.
let batch_end_block = builder.add(last_block, one);
let past_global_end = builder.gt(batch_end_block, global_end_block);
let query_end_block = builder.select(past_global_end, global_end_block, batch_end_block);

// Fetch and read the data commitment inputs.
let mut input_stream = VariableStream::new();
input_stream.write(&start_block);
input_stream.write(&query_end_block);
let data_comm_fetcher = DataCommitmentOffchainInputs::<BATCH_SIZE> {};
let output_stream = builder
    .async_hint(input_stream, data_comm_fetcher);
let data_comm_proof = output_stream
    .read::<DataCommitmentProofVariable<BATCH_SIZE>>(builder);

There are constraints enforced on start_block and batch_end_block before construction. However, once the arguments are constructed in data_comm_proof, they are left unconstrained and passed directly to prove_subchain.

Impact

Since the inputs to prove_subchain are left completely unconstrained, the prover can violate any assumptions made by the function and cause undefined behavior as demonstrated in Findings ref and ref.

Recommendations

Add proper validation logic for consistency between the initial arguments to the map block and the constructed data_comm_proof object.

builder.assert_is_equal(start_block, data_comm_proof.start_block_height);
builder.assert_is_equal(query_end_block, data_comm_proof.end_block_height);

Remediation

This issue has been acknowledged by Succinct, and a fix was implemented in the following commit: .

Zellic © 2024Back to top ↑