Nomad
0 net
AI Summary
A low-severity bug in the TypedMemView library's isValid function was caused by incorrect use of the bitwise NOT instruction instead of the ISZERO instruction in Yul assembly, causing the function to always return true regardless of whether memory bounds were valid. The bug was responsibly disclosed to Nomad, patched by replacing 'not' with 'iszero', and publicly documented.
Tags
Entities
TypedMemView
Nomad
Nikita Stupin
Immunefi
Center for Contemplative Research
summa-tx/memview-sol
Not is not iszero | Nikita Stupin’s Blog To start the blog I would like to share how the not and iszero instructions differ in Yul . This have led to discovery of two interesting low-severity bugs one of which is public so I am going to use it as an example to spread the knowledge about the difference. The bug was found in the TypedMemView library. From the library’s README: “TypedMemView is a library for interacting with the EVM’s linear memory in Solidity. It provides safe access to contiguous portions of memory, in a C-like style. Views are stored on the stack, and may be manipulated without manipulating the underlying memory.” Let us take a closer look at the isValid function. /** * @notice Check if the view is of a valid type and points to a valid location * in memory. * @dev We perform this check by examining solidity's unallocated memory * pointer and ensuring that the view's upper bound is less than that. * @param memView The view * @return ret - True if the view is valid */ function isValid ( bytes29 memView ) internal pure returns ( bool ret ) { if ( typeOf ( memView ) == 0xffffffffff ) { return false ;} uint256 _end = end ( memView ); assembly { // solium-disable-previous-line security/no-inline-assembly ret := not ( gt ( _end , mload ( 0x40 ))) } } What do you expect the function to return when _end is less or equal to mload(0x40) ? What about the opposite case? Instinctively, I would say true and false respectively. In reality, the function always returns true! Why? The not instruction in Yul means bitwise not so 1 becomes type(uint256).max - 1 and 0 becomes type(uint256).max and the return value will be true regardless of the input. The impact of the bug was not obvious to me and I did not have enough time to dive deep but the library was used in some notable projects and the bug could potentially break some of its memory safety guarantees. So I decided to responsibly disclose the bug as is to the Nomad team because they use and maintain the library. I reported the bug via Immunefi and then via GitHub Issues because Immunefi closed the report as out of scope and the maintainers gave the permission to open a public issue. Remediation was simple: the not instruction was replaced with iszero . Even though the library was out of scope the Nomad team still rewarded the finding. The reward was donated towards the Center for Contemplative Research . Timeline Date Event 2023-02-15 Bug reported to Nomad via Immunefi 2023-02-15 Report closed by Immunefi as out of scope 2023-02-23 Immunefi communicated that Nomad team allowed public bug report 2023-02-23 Bug reported to Nomad via GitHub Issues 2023-02-23 Fix applied 2023-03-07 Report reopened and reward issued 2023-03-20 Report closed and reward donated 2023-04-08 Permission to publish blog post requested 2023-04-08 Permission to publish blog post granted References https://docs.soliditylang.org/en/v0.8.19/yul.html#evm-dialect https://github.com/summa-tx/memview-sol/issues/11 https://hackerone.com/reports/1622449