QuipNetwork / QuipNetwork/quip-validator

pallet-xqvm execute: unbounded sample alloc + step_limit 0 is unlimited (validator OOM / hang)

Open
#10 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.6k
Forks
32
PR merge metrics
No merged PRs in 30d

Description

Title: pallet-xqvm execute: unbounded sample alloc + step_limit 0 is unlimited (validator OOM / hang)

Summary

pallet-xqvm::execute is a signed, permissionless extrinsic. Weight is pre-charged from the caller-supplied step_limit, then the interpreter runs.

Two independent metering holes:

  1. Memory is not metered. BSMX / SSMX / XSMX take a stack i64, convert to usize, and allocate a dense Vec<i64> of that length in one instruction (xquad/xqvm/src/vm.rs). WeightPerStep is 1000 ref_time. A 3-instruction program (PUSH huge; BSMX; HALT) pays for a handful of steps and can request gigabytes.

  2. Zero step limit is treated as infinity. The pallet only checks step_limit <= MaxStepLimit. 0 passes. Vm::set_step_limit maps 0 to u64::MAX. Weight charged is execute_base + WeightPerStep * 0. The node then runs until halt, OOM, or process kill. Substrate will not stop the extrinsic mid-loop because announced weight was tiny.

The crate has xqvm::verifier::verify. The pallet only Program::decodes (magic/CRC). It never calls the verifier.

Impact

Any account can stall or OOM every authoring/importing validator. Liveness of any runtime that includes this pallet.

Toy: PUSH 268435456; BSMX; HALT. Three steps. ~2GB per validator. Or step_limit=0: fee for zero steps, loop forever if the program does not halt.

Suggested fix

  • Reject step_limit == 0 on the consensus path; never map 0 to u64::MAX there.
  • Cap every dense allocation (BSMX/SSMX/XSMX, vec growth, ITER copies); charge proof-size/memory weight.
  • Run verifier::verify before Vm::run.
  • Prefer DispatchResultWithPostInfo and fail closed if an opcode would allocate above remaining weight.

Files

  • xquad/xqvm/src/vm.rs (BSMX/SSMX/XSMX alloc; set_step_limit(0) -> u64::MAX)
  • quip-validator/pallets/xqvm/src/lib.rs (execute, weight = WeightPerStep * step_limit)
  • quip-validator/runtime/src/configs/mod.rs (WeightPerStep)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the cited allocation and step-limit paths in xquad/xqvm/src/vm.rs, then trace execute and its weight calculation in quip-validator/pallets/xqvm/src/lib.rs and WeightPerStep in quip-validator/runtime/src/configs/mod.rs. Reproduce the two toy cases described in the issue and inspect verifier::verify. Done means consensus execution rejects zero limits, bounds memory-consuming operations, and prevents the reported OOM or nonterminating behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
blockchain, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.