Skip to content

security: close Critical and High findings in fix-gas#129

Closed
jamesatomc wants to merge 0 commit into
fix-gasfrom
audit/fix-critical-high
Closed

security: close Critical and High findings in fix-gas#129
jamesatomc wants to merge 0 commit into
fix-gasfrom
audit/fix-critical-high

Conversation

@jamesatomc

Copy link
Copy Markdown
Member

Scope

Remediates all Critical/High findings from the fix-gas audit:

  • fail-close uncertified multi-validator checkpoint production/sync
  • bind single-validator checkpoint sync to an already signature-verified DAG vertex
  • pass signed gas limits into Move VM and report actual weighted VM gas
  • enforce checkpoint gas ceiling
  • validate sequence numbers during deterministic replay and zero-effect batching
  • atomically commit logical state, checkpoint metadata, transaction indexes, and receipts
  • roll back partial in-memory ChangeSet application
  • meter/validate before runtime persistence and propagate object-store failures
  • disable arbitrary-address mutable object borrowing until an explicit shared/system object capability model exists

Deliberate fail-closed behavior

Distributed multi-validator finality remains disabled in this patch. It must not be re-enabled until Mysticeti committed output is connected to real validator vote aggregation / quorum certificates. This patch does not reconstruct committee private keys or fabricate quorum signatures.

Validation

The branch includes a deterministic patch workflow that formats, checks, and tests kanari-core, kanari-move-runtime-v1, and kanari-node. Keep this PR in draft until all checks pass and the generated source commit is reviewed.

@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
kanari-sdk Ready Ready Preview, Comment Jun 23, 2026 7:07pm

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements critical security hardening patches, including transactional state changes, atomic checkpoint finalization, strict mutable object ownership verification, and checkpoint gas limits. The review feedback identifies several key issues: the use of the unstable let_chains feature in both the Move runtime and the Python helper script which breaks stable Rust compilation; a severe performance bottleneck and potential race condition caused by cloning the entire Blockchain history and dropping/re-acquiring locks during checkpoint finalization; a second redundant blockchain clone in the atomic updates builder; and head-of-line blocking in the DAG vertex production when a transaction exceeds the remaining checkpoint gas budget.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1072 to +1074
if let Some(s_addr) = sender
&& stored_obj.owner != s_addr
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This uses the unstable let_chains feature (if let ... && ...), which will fail to compile on stable Rust. Since sender.is_some_and is already used elsewhere in the codebase, we should use it here as well to maintain compatibility with stable Rust. Note that scripts/apply_critical_high_fixes.py must also be updated to avoid regenerating this unstable syntax.

                        if sender.is_some_and(|s_addr| stored_obj.owner != s_addr) {

Comment thread scripts/apply_critical_high_fixes.py Outdated
Comment on lines +178 to +184
''' if let Some(s_addr) = sender
&& stored_obj.owner != s_addr
{
return Err(anyhow::anyhow!(
"Ownership verification failed: mutable object arguments must be owned by the authenticated sender"
));
}''',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This replacement introduces the unstable let_chains feature into mod.rs, which will break compilation on stable Rust. Update the replacement string to use sender.is_some_and(...) instead.

    '''                        if sender.is_some_and(|s_addr| stored_obj.owner != s_addr) {
                            return Err(anyhow::anyhow!(
                                "Ownership verification failed: mutable object arguments must be owned by the authenticated sender"
                            ));
                        }''',

Comment on lines +123 to +127
let mut next_chain = self
.blockchain
.read()
.unwrap_or_else(|e| e.into_inner())
.clone();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Cloning the entire Blockchain struct (including dag_checkpoints) on every checkpoint finalization introduces a significant performance bottleneck that scales quadratically $O(N^2)$ with the chain height. Furthermore, dropping the read lock and later acquiring a write lock at line 147 to overwrite self.blockchain introduces a race condition where concurrent updates to the blockchain could be silently overwritten (lost updates). Consider refactoring this to acquire the write lock upfront or using an incremental update pattern that avoids cloning the entire history.

Comment on lines +451 to +460
for tx in transactions {
let next = declared_gas
.checked_add(tx.transaction.gas_limit())
.ok_or_else(|| anyhow::anyhow!("Checkpoint gas budget overflow"))?;
if next > max_checkpoint_gas {
break;
}
declared_gas = next;
selected.push(tx);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using break when a transaction's gas limit exceeds the remaining checkpoint budget causes head-of-line blocking. It prevents any subsequent transactions from being packed, even if they have smaller gas limits and belong to different senders (which wouldn't violate sequence number ordering). Consider using continue instead of break for transactions from different senders, while keeping track of skipped senders to ensure we don't execute subsequent transactions from a skipped sender out of order.

Comment thread crates/kanari-core/src/engine.rs Outdated
Comment on lines +360 to +365
let mut slim_chain = chain.clone();
for existing in &mut slim_chain.dag_checkpoints {
if !existing.transactions.is_empty() {
*existing = Self::checkpoint_without_transactions(existing);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Cloning the chain here results in a second full clone of the blockchain history during checkpoint finalization (since it was already cloned in apply_checkpoint.rs). This is highly inefficient. Since slim_chain is only used to serialize a version without transactions, consider passing chain by value or optimizing the serialization to avoid cloning the entire history.

@jamesatomc jamesatomc closed this Jun 23, 2026
@jamesatomc jamesatomc force-pushed the audit/fix-critical-high branch from c543cb2 to b6d1531 Compare June 23, 2026 19:06
@jamesatomc jamesatomc deleted the audit/fix-critical-high branch June 24, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant