2

I have two little tests in which I'm trying to return bytes that are populated via inline assembly. It works when I'm using a pre-defined return variable, but I'm getting a buffer overrun when I try to do it all in inline assembly. What am I missing here?

pragma solidity ^0.8.4;

contract AssemblyTest {

    function working() external view returns (bytes memory res) {
        assembly {
            res := mload(0x40)           // get free memory pointer
            mstore(res, 0x40)            // length: 64 bytes
            mstore(add(res, 0x20), 1)    // first 32 bytes
            mstore(add(res, 0x40), 2)    // second 32 bytes
            mstore(0x40, add(res, 0x60)) // update free memory pointer
        }
    }

    function notWorking() external view returns (bytes memory) {
        assembly {
            let res := mload(0x40)       // get free memory pointer
            mstore(res, 0x40)            // length: 64 bytes
            mstore(add(res, 0x20), 1)    // first 32 bytes
            mstore(add(res, 0x40), 2)    // second 32 bytes
            mstore(0x40, add(res, 0x60)) // update free memory pointer
            return (res, 0x60)           // return 96 bytes (32 bytes length + 64 bytes data)
        }
    }
}
3
  • I'll also answer your other question (ethereum.stackexchange.com/q/129982/84305) when I have more time. Unless you already solved it ?
    – hroussille
    Jun 12, 2022 at 18:49
  • I found a "solution" by replicating what abi.encode() did, but I was still confused about one thing (a 0x40 that I couldn't explain; probably an offset just like here).
    – Daniel
    Jun 12, 2022 at 19:10
  • What I was going to propose anyway was a generic replication of abi.encode (generic for the array size), feel free to edit your question so that I can give a look to that 0x40.
    – hroussille
    Jun 12, 2022 at 19:44

1 Answer 1

2

Your data should be returned abi.encoded. In your case, since you are dealing with a dynamic type, you must include the offset data start (see this answer)

Modify your function for this, and it should work :

    function nowItWorks() external view returns (bytes memory) {
        assembly {
            let res := mload(0x40)       // get free memory pointer
            mstore(res, 0x20)            // return data offset : abi encoding
            mstore(add(res, 0x20), 0x40) // length: 64 bytes
            mstore(add(res, 0x40), 1)    // first 32 bytes
            mstore(add(res, 0x60), 2)    // second 32 bytes
            mstore(0x40, add(res, 0x80)) // update free memory pointer
            return (res, 0x80)           // return 128 bytes (32 bytes data offset + 32 bytes length + 64 bytes data)
        }
    }

Since your function is external, and you are returning (and exiting the contract) from the function, you can also choose not to respect solidity memory layout and remove all code related to the free memory pointer :

      function nowItWorks() external view returns (bytes memory) {
        assembly {
            let res := 0x00              // Take any memory location
            mstore(res, 0x20)            // return data offset : abi encoding
            mstore(add(res, 0x20), 0x40) // length: 64 bytes
            mstore(add(res, 0x40), 1)    // first 32 bytes
            mstore(add(res, 0x60), 2)    // second 32 bytes
            return (res, 0x80)           // return 128 bytes (32 bytes data offset + 32 bytes length + 64 bytes data)
        }
    }
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.