Company Simulator

First Flight #51
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Impact: high
Likelihood: low
Invalid

Unchecked multiplication shares_owned * share_price may overflow (incorrect payout)

Root + Impact

Description

  • Normal behavior:

    When an investor redeems, withdraw shares() computes their payout as shares_owned share_price and sends that ETH to the investor.

  • Specific issue:

    The multiplication shares owned share price is performed without overflow checks. If shares_owned and/or share_price are large enough, the product can overflow the 256-bit unsigned integer and wrap around producing a much smaller payout than intended (or potentially zero). This allows an attacker (or accidental state) to cause incorrect, truncated payouts either stealing funds (if the wrap results permit other logic to misuse the value), or depriving investors of funds.

// Root cause in the codebase with @> marks to highlight the relevant section
@external
def withdraw_shares():
shares_owned: uint256 = self.shares[msg.sender]
assert shares_owned > 0, "Not an investor!!!"
@> share_price: uint256 = self.get_share_price()
@> payout: uint256 = shares_owned * share_price # <-- unchecked multiplication (overflow risk)
...

Risk

Likelihood:

  • requires either very large shares owned (close to TOTAL_SHARES) and/or an unexpectedly large share price (due to miscalculation, external manipulation, or precision scaling).

  • Attackers controlling a contract that manipulates share accounting or an owner who increases public shares cap / injects extreme values may increase likelihood.

Impact:

  • overflow leads to incorrect payout calculations:

  • Investors may receive much less ETH than they are owed (financial loss).

  • The contract accounting (company_balance) may be reduced by a wrong value and not match issued shares, causing insolvency bugs and downstream DoS.

  • In edge cases an overflow could produce a huge payout (if later logic interprets wrapped value as large) — causing fund drain.

  • Trust and legal/auditing consequences for mispaid redemptions.

Proof of Concept

Multiplying shares_owned * share_price without checks can overflow 256-bit arithmetic; for example 2**200 * 2**60 = 2**260 wraps modulo 2**256 and produces a tiny/wrong payout (e.g., 16), demonstrating payout truncation.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
PoC: Demonstrates unchecked multiplication overflow for payout calculation.
This mimics the vulnerable logic: payout = shares_owned * share_price
*/
contract OverflowPoC {
// deliberately small max to make overflow easier in test (simulate wrap)
// but using real uint256 values we craft numbers to overflow 256-bit.
function mulOverflow(uint256 a, uint256 b) public pure returns (uint256 product) {
// vulnerable multiplication (in original contract, no check exists)
product = a * b;
}
// helper to compute a pair that overflows 256-bit:
// choose a = 2**200, b = 2**60 -> product = 2**260 -> wraps mod 2**256
function exampleOverflow() external pure returns (uint256 a, uint256 b, uint256 wrapped) {
a = uint256(1) << 200; // 2**200
b = uint256(1) << 60; // 2**60
wrapped = mulOverflow(a, b); // expected wrap: 2**260 mod 2**256 == 2**4 == 16
}
}

Recommended Mitigation

Explanation (brief)

Perform safe multiplication checks before using the product as a payout. Ensure that payout / share_price == shares_owned (when share_price > 0) or use explicit overflow-safe arithmetic utilities. Revert if overflow would occur. Additionally add sanity limits on share_price and shares_owned (e.g., ensure shares_owned <= TOTAL_SHARES and share_price cannot exceed sensible bounds).

- remove this code
+ add this code
- payout: uint256 = shares_owned * share_price
+ # Prevent multiplication overflow: ensure shares_owned * share_price won't wrap
+ # If share_price == 0, payout is 0 (handled earlier by validations)
+ if share_price != 0:
+ # computed product and verify non-overflow by reverse division
+ payout: uint256 = shares_owned * share_price
+ # reverse check: if shares_owned > 0 then payout // shares_owned must equal share_price
+ assert shares_owned == 0 or payout // shares_owned == share_price, "Multiplication overflow detected"
+ else:
+ payout = 0
+
+ # Additional sanity checks
+ assert shares_owned <= TOTAL_SHARES, "Invalid share count"
+ # Optionally clamp/share_price upper bound (example): assert share_price <= MAX_PAYOUT_PER_SHARE
Updates

Lead Judging Commences

0xshaedyw Lead Judge
7 days ago
0xshaedyw Lead Judge 6 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.