Security: UInt64 overflow in LP residual weight permanently locks funds above ~$10k LP - #19
Conversation
…nvariant Finding 1 (Medium): _entry_weighted_sum_checked reverts when lp_shares_total * settlement_timestamp > 2^64, permanently blocking claim_lp_residual() for all LPs. Threshold: ~$10,376 USDC (2026). Decreases over time as Unix timestamps grow. Finding 2 (Low): q[i] >= total_user_shares[i] is enforced in the reference model but missing from _assert_invariants on-chain. Includes 5-test PoC suite (tests/test_poc_residual_overflow.py).
|
Hi @y4motion , Thank you for your report! I will review the claims and repro and get back to you. |
The primary claim is valid and earns the bounty.
Low-priority, since no other bug surfaced this missing invariant. Could add it as defense-in-depth. |
|
@andsav Thanks for the review and merge! Glad the report was helpful for the protocol's security. I will claim the bounty via the DevLoot platform. |
|
@andsav Thanks for sorting this out! Apologies for the missing Here's my Algo address:
|
|
Sorry guys, just saw it here. Were you guys able to set this up? @andsav were you able to refund the bounty from the platform? I will think about a way for bounties to work on those kind of situations. |
|
@p2arthur yup, I withdrew and sent it to their wallet directly, nothing lost here! I think allowing the bounty creator to link a PR directly would be the easiest, the PR -> Issue link might not always be there so this can be brittle. |
|
Awesome, its a flows I wasn't able to test in production before, so glad it worked as intended when closing the issue without a PR. Yeah, I can give the option of the bounty creator to link a PR and reward the creator instead of only enabling them to refund. Sounds like an easy way to fix that |
|
Thanks for sorting this out @andsav, funds received! And thanks for checking in @p2arthur 🤝 The manual PR-link override idea sounds like a solid UX improvement for DevLoot — would definitely smooth out these edge cases. Happy to keep contributing to the ecosystem, just ping me whenever new bounties drop! |
Bug Bounty Submission (Issue #13)
Two issues found during manual review of
market_app/contract.py:Finding 1: UInt64 Overflow in LP Residual Weight (Medium)
_entry_weighted_sum_checked(L587–590) computesshares × timestampwith amulwoverflow guard. When called from_total_residual_weight()withlp_shares_total × (settlement_timestamp − 1) > 2⁶⁴, the assert fires and permanently blocksclaim_lp_residual()for all LPs.Threshold (2026):
$10,376 USDC total LP contribution$9,742 by 2030, ~$8,350 by 2040)Impact: LP residual permanently locked — no LP can claim
Trend: Threshold decreases over time (
Call chain:
Secondary: the
total_lp_weighted_entry_sumaddition (L1110–1112) uses native+which wraps silently on UInt64 overflow in AVM, potentially corrupting weight calculations when many small LP entries accumulate.Fix: Use 128-bit intermediates via
mulw/divmodwin_calculate_weight, consistent with howlmsr_mul_div_flooralready handles wide arithmetic.Finding 2: Missing Inventory Coverage Invariant (Low)
The reference model asserts
q[i] >= total_user_shares[i]but the on-chain_assert_invariants()(L631–666) omits this check. Defense-in-depth gap.PoC
5 passing tests in
tests/test_poc_residual_overflow.py:PYTHONPATH=. .venv/bin/python -m pytest tests/test_poc_residual_overflow.py -v # 5 passedFull details in
VULNERABILITY_REPORT.md.closes #13