Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tangy-jokes-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`Memory`: Remove the `asBytes32` and `asPointer` function to reduce the risk of mistakes when manipulating memory pointers.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- `RLP`: The `encode(bytes32)` function now encodes `bytes32` as a fixed size item and not as a scalar in `encode(uint256)`. Users must replace calls to `encode(bytes32)` with `encode(uint256(bytes32))` to preserve the same behavior. ([#6167](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/6167))
- `ERC4337Utils`: The `parseValidationData` now returns a `ValidationRange` as the last return tuple value indicating whether the `validationData` is compared against a timestamp or block number. Developers must update their code to handle this new return value (e.g. `(aggregator, validAfter, validUntil) -> (aggregator, validAfter, validUntil, range)`).
- `SignerWebAuthn`: The `_rawSignatureValidation` function now returns `false` when the signature is not a valid WebAuthn authentication assertion. P256 fallback is removed. Developers can add it back by overriding the function.
- `Memory`: The `setFreeMemoryPointer` function is renamed to `unsafeSetFreeMemoryPointer`. Developers should use `unsafeSetFreeMemoryPointer` instead of `setFreeMemoryPointer` after v5.6.0.

## 5.5.0 (2025-10-31)

Expand Down
2 changes: 1 addition & 1 deletion contracts/token/ERC20/extensions/ERC4626.sol
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
address(asset_),
abi.encodeCall(IERC20Metadata.decimals, ())
);
Memory.setFreeMemoryPointer(ptr);
Memory.unsafeSetFreeMemoryPointer(ptr);

return
(success && LowLevelCall.returnDataSize() >= 32 && uint256(returnedDecimals) <= type(uint8).max)
Expand Down
16 changes: 5 additions & 11 deletions contracts/utils/Memory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,18 @@ library Memory {
/**
* @dev Sets the free `Pointer` to a specific value.
*
* The solidity memory layout requires that the FMP is never set to a value lower than 0x80. Setting the
* FMP to a value lower than 0x80 may cause unexpected behavior. Deallocating all memory can be achieved by
* setting the FMP to 0x80.
*
* WARNING: Everything after the pointer may be overwritten.
**/
function setFreeMemoryPointer(Pointer ptr) internal pure {
function unsafeSetFreeMemoryPointer(Pointer ptr) internal pure {
assembly ("memory-safe") {
mstore(0x40, ptr)
}
}

/// @dev `Pointer` to `bytes32`. Expects a pointer to a properly ABI-encoded `bytes` object.
function asBytes32(Pointer ptr) internal pure returns (bytes32) {
return Pointer.unwrap(ptr);
}

/// @dev `bytes32` to `Pointer`. Expects a pointer to a properly ABI-encoded `bytes` object.
function asPointer(bytes32 value) internal pure returns (Pointer) {
return Pointer.wrap(value);
}

/// @dev Move a pointer forward by a given offset.
function forward(Pointer ptr, uint256 offset) internal pure returns (Pointer) {
return Pointer.wrap(bytes32(uint256(Pointer.unwrap(ptr)) + offset));
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/cryptography/TrieProof.sol
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ library TrieProof {
}

// Reset memory before next iteration. Deallocates `decoded` and `path`.
Memory.setFreeMemoryPointer(fmp);
Memory.unsafeSetFreeMemoryPointer(fmp);
}

// If we've gone through all proof elements without finding a value, the proof is invalid
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/structs/Accumulators.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,6 @@ library Accumulators {
}

function _nullPtr() private pure returns (Memory.Pointer) {
return Memory.asPointer(0x00);
return Memory.Pointer.wrap(0);
}
}
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/utilities.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ function processMultipleItems(uint256[] memory items) internal {
for (uint256 i = 0; i < items.length; i++) {
bytes memory tempData = abi.encode(items[i], block.timestamp);
// Process tempData...
Memory.setFreeMemoryPointer(ptr); // Reset pointer for reuse
Memory.unsafeSetFreeMemoryPointer(ptr); // Reset pointer for reuse
}
}
----
Expand Down
6 changes: 3 additions & 3 deletions test/utils/Memory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ contract MemoryTest is Test {
// - moving the free memory pointer to far causes OOG errors
uint256 constant END_PTR = type(uint24).max;

function testGetsetFreeMemoryPointer(uint256 seed) public pure {
function testGetSetFreeMemoryPointer(uint256 seed) public pure {
bytes32 ptr = bytes32(bound(seed, START_PTR, END_PTR));
ptr.asPointer().setFreeMemoryPointer();
assertEq(Memory.getFreeMemoryPointer().asBytes32(), ptr);
Memory.Pointer.wrap(ptr).unsafeSetFreeMemoryPointer();
assertEq(Memory.Pointer.unwrap(Memory.getFreeMemoryPointer()), ptr);
}

function testAsSliceToBytes(bytes memory input) public pure {
Expand Down
14 changes: 1 addition & 13 deletions test/utils/Memory.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('Memory', function () {
describe('free pointer', function () {
it('sets free memory pointer', async function () {
const ptr = ethers.toBeHex(0xa0, 32);
await expect(this.mock.$setFreeMemoryPointer(ptr)).to.not.be.reverted;
await expect(this.mock.$unsafeSetFreeMemoryPointer(ptr)).to.not.be.reverted;
});

it('gets free memory pointer', async function () {
Expand All @@ -31,18 +31,6 @@ describe('Memory', function () {
);
});
});

describe('pointer conversions', function () {
it('asBytes32', async function () {
const ptr = ethers.toBeHex('0x1234', 32);
await expect(this.mock.$asBytes32(ptr)).to.eventually.equal(ptr);
});

it('asPointer', async function () {
const ptr = ethers.toBeHex('0x1234', 32);
await expect(this.mock.$asPointer(ptr)).to.eventually.equal(ptr);
});
});
});

describe('Slices', function () {
Expand Down