Normal behavior:
Value-transfer and state-mutating flows (funding, share issuance, withdrawals) should be safe from reentrancy: an external call that transfers ETH must not allow the recipient to re-enter other state-changing entrypoints in a way that breaks invariants. one or more sentences
Specific issue:
The contract contains multiple external/value-sending entrypoints (fund_cyfrin → internal fund_investor/fund_owner, withdraw_shares() which performs a raw_call to msg.sender, and pay_holding_debt()), but it lacks a consistent applied non-reentrancy guard across those flows — the top-of-file # pragma nonreentrancy on is only a comment and not an applied decorator. Because of this, a malicious investor contract can receive ETH from withdraw_shares() and, during that external transfer, re-enter the contract (for example by calling fund_cyfrin(1)), causing share issuance, timestamp updates, or company accounting to occur in an inconsistent state (e.g., after company_balance was already reduced by the withdrawing call). This can lead to economic manipulation or accounting corruption.
Likelihood:
Medium — requires an attacker to invest via a smart-contract wallet (easy to deploy) and then trigger a withdrawal to their contract so their fallback/receive re-enters the contract. Real-world investors are often EOAs, but any single malicious contract investor suffices.
Impact:
High — Possible outcomes include:
Purchasing shares while company_balance has been reduced for a payout, leading to cheaper shares (share-price manipulation) and unfair allocation.
Corrupting issued_shares / share_received_time semantics by reentering funding logic mid-withdrawal.
Creating accounting inconsistencies that can be exploited later for gains or to cause other users’ calls to revert.
Financial and trust damage — attackers may be able to profit or irreparably break share accounting.
Below is a single Solidity PoC that demonstrates the reentrancy pattern:
Explanation (brief)
Apply a consistent non-reentrancy strategy across all external/value-sending functions. Use explicit @nonreentrant decorators (or a reentrancy mutex) on all functions that mutate funds or issue shares (withdraw_shares, fund_cyfrin, pay_holding_debt, and any refund/partial-issue branches). Use checks→effects→interactions and prefer a pull payment model where feasible (record owed amounts and let recipients claim with a protected claim() function). If external calls are required, ensure state is fully consistent before making them.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.