audit(foundry): security review of foundry/src/ at d136ff3#22
audit(foundry): security review of foundry/src/ at d136ff3#22Abidoyesimze wants to merge 1 commit into
Conversation
Severity-tagged findings report (0 Critical / 4 High / 4 Medium / 8 Low-Informational) covering all 7 contracts, plus proxy-specific checks for the 4 upgradeable platform contracts (initializer protection verified at runtime, storage layout confirmed via forge inspect). Confirms all 3 seeded DINTaskAuditor leads as real. Findings-only: no contract in foundry/src/ was modified. PoC tests added in foundry/test/SecurityFindings.t.sol (9 new, 13/13 passing alongside the existing UpgradeValidation.t.sol suite).
|
@umeradl Kindly check this findings and let me know if anything is needed |
Overall Review — task_060726_4, Part 1Thanks @Abidoyesimze — reviewed in full, and this is a strong submission. I pulled the branch, spot-checked the findings against source at the pinned commit, and ran the PoC suite locally. The 9 new tests in What stood out
Follow-ups before merge
Context — this closes out Part 1 of the taskFor the record, linking the related threads so the full picture is in one place:
Net: strong pass on Part 1. Let's settle the two severity ratings and the gas-estimate labeling, and this is good to merge. Nice work. |
Manually merges the Part 1 deliverable of task_060726_4 from Similoluwa Abidoye (@Abidoyesimze, PR #22, branch security/foundry-src-review). The 7 contracts in foundry/src/ are byte-identical between the audit's pinned commit (d136ff3) and current develop, so the findings and line references apply as-is to the live contracts. - Documentation/technical/audits/foundry-src-security-review.md (relocated from the PR's Developer/audits/ path) - foundry/test/SecurityFindings.t.sol — 9 PoC/verification tests, all passing (forge test --match-contract SecurityFindingsTest). Findings: 0 Critical, 4 High, 4 Medium, 8 Low/Informational. Open follow-ups tracked on PR #22: severity of H-1/H-2 (candidate for Critical) and a measured spec-scale gas number for H-1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merged manually to
|
|
Thanks for the thorough review — replying to both follow-ups. Sent as a small PR into 1. Severity — agree, H-1 and H-2 → Critical. I'd land on the same call independently: both permanently brick a GI on non-upgradeable contracts with zero recovery path, and both are cheap (H-1) or free (H-2) for a single unprivileged participant to trigger. Renumbered to C-1/C-2, remaining High findings shifted to H-1/H-2, every cross-reference in the report updated. No finding content changed, only labels. 2. Gas number. Went with the real measurement rather than just labeling the estimate. One thing worth surfacing explicitly, since it shapes how the number had to be built: 3. FFI/npx note — reproduced independently in a second environment, same failure class, confirmed unrelated to this report's PoCs. New PoC test ( |
Summary
Security review of all 7 contracts in
foundry/src/, pinned to commitd136ff3. Findings-only — no API/storage/behavior changes to any contract. Full report:Developer/audits/2026-07_foundry-src-security-review.md. PoC tests:foundry/test/SecurityFindings.t.sol(9 new tests, all passing; existingUpgradeValidation.t.solsuite untouched and still green).Findings: 0 Critical · 4 High · 4 Medium · 8 Low/Informational
The four upgradeable platform contracts (
DinToken,DinCoordinator,DinValidatorStake,DINModelRegistry) came back clean on every proxy-specific check: initializer protection is verified at runtime (not just present in source — see PoC), storage layout is confirmed correct viaforge inspect storage-layout, and there's no in-scope upgrade-path access-control issue since Transparent Proxy keeps that authority entirely outside the implementations. The real risk is concentrated in the two non-upgradeable task contracts,DINTaskCoordinatorandDINTaskAuditor, which run the actual federated-learning round.The three most important findings:
H-1 (Sybil-cheap gas-DoS via unbounded registration) confirms the seeded lead:
slashAuditors,slashAggregators,finalizeEvaluation,finalizeT1Aggregation, andfinalizeT2Aggregationall iterate the entire GI's registrants in one unpaginated transaction, and none of them cap registration the way client submissions are capped atMAX_LM_SUBMISSIONS. Using the contract's own documented "spec" parameters (10 auditors/batch, 100 models/batch), a few hundred registered auditors is enough to blow past any realistic L2 block gas limit — and sinceMIN_STAKEis 10 DIN at ~0.00001 ETH under the default exchange rate, an attacker can register hundreds of Sybil identities for a few cents and deliberately brick any model's GI on demand, with no recovery path since the task contracts aren't upgradeable.H-2 (zero-CID sentinel collision, PoC-confirmed) is a genuinely new find:
finalizeT1Aggregation/finalizeT2Aggregationusebytes32(0)to mean "nobody submitted," but never reject_aggregationCID == 0at submission time. Any single assigned aggregator who submits the zero CID — and is the only one of their batch to respond in time, a routine scenario, not an edge case — makes that ambiguity real: the finalize call reverts withTC_NoSubmissionson a batch that had a legitimate submission, and since finalization processes every batch in one transaction, one poisoned batch blocks the whole GI. Permanently, with no override. Seetest_finalizeT1Aggregation_zeroCID_bricksEntireGI.H-3 (no quorum on T1/T2 finalization) rounds out the "GI integrity" cluster: unlike
DINTaskAuditor, which explicitly enforcesminEligibilityQuorum/minScoreQuorum, nothing stopsfinalizeT1Aggregation/finalizeT2Aggregationfrom accepting a single aggregator's submission as the batch's "consensus" result if the other two assigned aggregators simply haven't responded yet. That output flows into Tier-2 and ultimately the new global model with zero cross-validation, defeating the point of assigning multiple aggregators per batch.H-4 confirms the third seeded lead (blockhash/timestamp shuffle is predictable and grindable by the very party — the model owner — who benefits from the outcome). M-1 through M-4 and the Low/Informational table round out the rest; see the report for full detail on each.
What I'd do differently with more time
DinValidatorStake.slash()'s active/pending-withdrawal split given more time since it's the one place subtracting across two balances in the same function.Test plan
forge build— clean at pinned commit and after adding PoC testsforge test— 13/13 passing (4 pre-existingUpgradeValidation.t.sol+ 9 newSecurityFindings.t.sol)