Raise InitializationFailed for every non-conforming initialize answer - #89
Conversation
cloneAndInitialize called ICloneableV2.initialize through the high-level ABI, so an implementation that does not support ICloneableV2 — no initialize(bytes) and no fallback, a fallback returning nothing, a short return — reverted with no data before the sentinel comparison ran, and the caller received bare 0x instead of InitializationFailed. That is precisely the case the ICloneableV2 sentinel exists to catch. Now a low-level call with an explicit shape check: a no-data revert, a return of other than exactly 32 bytes, or a 32-byte word that is not ICLONEABLE_V2_SUCCESS all revert InitializationFailed. A revert carrying data is the implementation's own diagnosis of a call it handled and is bubbled verbatim, since it is more specific than the factory's error. Exactly 32 bytes rather than at least 32: the ABI encoding of a bytes32 return is one word and the interface says the implementation MUST return the hash, so a longer blob whose first word is the hash is not that. Three fixtures pin the shapes: TestNotCloneable (no initialize, no fallback), TestFallbackRawReturn (fallback returns the given bytes raw), TestCloneableRawRevert (initialize reverts with the given bytes). Every case runs through both clone entry points. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. WalkthroughThe factory now performs initialization with a low-level call. It propagates non-empty revert data and raises ChangesInitialization response handling
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The initialize call is now a low-level call with an explicit return-shape check, so every clone path's gas moved; the snapshot follows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
@coderabbitai review |
|
The snapshot is not compared by CI; the regenerated file belonged to no issue. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… tests and comments - testV1ShapedImplementationIsRejected: an ICloneableV1 answers initialize(bytes) with no return data and is InitializationFailed through both entry points. - The wrong-word vm.assume imports ICLONEABLE_V2_SUCCESS. - testInitializeReturnsSentinelSucceeds drops the runtime-code asserts that duplicate the clone-shape tests. - The low-level-call comment states current behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nitialization-failed-typed-error
|
@coderabbitai review |
|
…tests One fixture, TestCloneableRawAnswer, replaces four: it reverts with or returns raw bytes chosen by the caller. The tests keep one case per distinct mutant: empty return, sentinel with trailing bytes, no-data revert, and a revert carrying the sentinel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nitialization-failed-typed-error
…d-error Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cloneAndInitializeraisesInitializationFailedwhen the clone'sinitializeanswers anything but the 32-byteICLONEABLE_V2_SUCCESS. A revert with data still bubbles verbatim.Before, a high-level call ran the ABI decoder before the sentinel check. So a no-data revert (no
initializeand no fallback, or a barerevert()), an empty return (anICloneableV1, a silent fallback) or a short return came out as bare0x, and an over-long return starting with the sentinel was accepted. Now it is a low-level call. A revert with data bubbles. Every other answer that is not exactly the 32-byte sentinel revertsInitializationFailed. A failed call that reaches the final check has no return data, so the length check rejects it without a separate!success.Migration
ICloneableV2implementation (ICloneableV1included) now revertsInitializationFailed(0x19b991a8) instead of empty data. So does an out-of-gas insideinitialize: it is also a no-data revert.initializethat returns more than 32 bytes starting with the sentinel no longer clones.Closes #61
QA
4ca99feis the trim plus main79130af, merged cleanly. The full suite passes locally (54 passed, 0 failed). CI is green on4ca99fe(test, legal, static).TestCloneableRawAnswer, reverts with or returns whatever raw bytes the test chooses. Each test runs through bothcloneDeterministicandcloneDeterministicOpenSalt.4ca99fe, full suite per mutant (baseline 54 passed): 14 mutants: 13 killed, 1 survived, 0 no-run. M00 is main's high-level call. M01:!= 32→< 32. M02: length check dropped. M03: empty return exempt from the length check. M04: never bubble. M05: bubble the no-data revert too. M06: bubbled data truncated. M07: sentinel comparison inverted. M08: sentinel constant changed. M09:datanot forwarded. M10: EIP-1167 suffix changed. M11:!success ||put back. M12: bubble guard> 0→> 32, so a revert of up to 32 bytes reaches the final check. M13: a successful empty return accepted. M11 survives because it is equivalent: a failed call that reaches the final check has empty return data, so the length check rejects it anyway. Main's clone-flow tests kill M07–M10.testInitializeReturnsNothingRevertsInitializationFailedkills M00, M02, M03 and M13. It is the only test that kills M13.testInitializeReturnsSentinelWithTrailingBytesRevertsInitializationFailedkills M00, M01 and M02. It is the only test that kills M01.testInitializeRevertsWithoutDataRevertsInitializationFailedkills M00, M02, M03 and M05. It is the only test that kills M05.testInitializeRevertsWithSentinelBubblesVerbatimkills M04, M06, M09 and M12. It is the only test that kills M12.ICloneableV2.initializeNatSpec (MUST returnkeccak256("ICloneableV2.initialize"), which exists so that non-support is caught),ICloneableFactoryV3.cloneDeterministic(the clone is created ONLY if that hash is returned), abytes32return being exactly 32 bytes, andICloneableV1.initialize(same selector, no return value).🤖 Generated with Claude Code
Summary by CodeRabbit