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