Skip to content

Commit

Permalink
♻️ Zeroize the last slot for SSTORE2 read (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized authored Oct 30, 2022
1 parent 0d3d0fc commit 65a0e7c
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 81 deletions.
52 changes: 26 additions & 26 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Base64Test:testBase64DecodeMemorySafety(bytes) (runs: 256, μ: 400585, ~: 188255)
Base64Test:testBase64DecodeMemorySafety(bytes) (runs: 256, μ: 397127, ~: 188255)
Base64Test:testBase64DecodeSentenceGas() (gas: 3633)
Base64Test:testBase64DecodeShortStringGas() (gas: 933)
Base64Test:testBase64EncodeDecode(bytes) (runs: 256, μ: 16937, ~: 12921)
Base64Test:testBase64EncodeDecodeAltModes(bytes,uint256) (runs: 256, μ: 503997, ~: 390235)
Base64Test:testBase64EncodeDecodeAltModes(bytes,uint256) (runs: 256, μ: 498729, ~: 365941)
Base64Test:testBase64EncodeEmptyString() (gas: 666)
Base64Test:testBase64EncodeFileSafeAndNoPadding(bytes,bool,bool) (runs: 256, μ: 12366, ~: 6522)
Base64Test:testBase64EncodeFileSafeAndNoPadding(bytes,bool,bool) (runs: 256, μ: 12331, ~: 6402)
Base64Test:testBase64EncodeSentence() (gas: 4201)
Base64Test:testBase64EncodeShortStrings() (gas: 6127)
Base64Test:testBase64EncodeToStringWithDoublePadding() (gas: 1240)
Expand All @@ -18,7 +18,7 @@ CREATE3Test:testFailDoubleDeploySameBytecode() (gas: 98422395)
CREATE3Test:testFailFuzzDoubleDeployDifferentBytecode(bytes32,bytes,bytes) (runs: 256, μ: 54145024, ~: 96877297)
CREATE3Test:testFailFuzzDoubleDeploySameBytecode(bytes32,bytes) (runs: 256, μ: 53767347, ~: 96878577)
CREATE3Test:testFuzzDeployERC20(bytes32,string,string,uint8) (runs: 256, μ: 825330, ~: 822580)
DynamicBufferLibTest:testDynamicBuffer(bytes[],uint256) (runs: 256, μ: 529302, ~: 639402)
DynamicBufferLibTest:testDynamicBuffer(bytes[],uint256) (runs: 256, μ: 514993, ~: 373243)
DynamicBufferLibTest:testJoinWithConcat() (gas: 34311)
DynamicBufferLibTest:testJoinWithDynamicBuffer() (gas: 9732)
ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 396)
Expand Down Expand Up @@ -117,11 +117,11 @@ LibBitTest:testPopCount() (gas: 63716)
LibBitmapTest:testBitmapClaimWithGetSet() (gas: 27096)
LibBitmapTest:testBitmapClaimWithToggle() (gas: 17451)
LibBitmapTest:testBitmapFindLastSet() (gas: 1366908)
LibBitmapTest:testBitmapFindLastSet(uint256,uint256) (runs: 256, μ: 53302, ~: 53117)
LibBitmapTest:testBitmapFindLastSet(uint256,uint256) (runs: 256, μ: 53288, ~: 53117)
LibBitmapTest:testBitmapGet() (gas: 2523)
LibBitmapTest:testBitmapGet(uint256) (runs: 256, μ: 2596, ~: 2596)
LibBitmapTest:testBitmapPopCount() (gas: 784890)
LibBitmapTest:testBitmapPopCount(uint256,uint256,uint256) (runs: 256, μ: 160351, ~: 140830)
LibBitmapTest:testBitmapPopCount(uint256,uint256,uint256) (runs: 256, μ: 158702, ~: 140857)
LibBitmapTest:testBitmapPopCountAcrossMultipleBuckets() (gas: 73587)
LibBitmapTest:testBitmapPopCountWithinSingleBucket() (gas: 34030)
LibBitmapTest:testBitmapSet() (gas: 22527)
Expand All @@ -132,9 +132,9 @@ LibBitmapTest:testBitmapSetBatchAcrossMultipleBuckets() (gas: 443521)
LibBitmapTest:testBitmapSetBatchWithinSingleBucket() (gas: 408117)
LibBitmapTest:testBitmapSetTo() (gas: 14292)
LibBitmapTest:testBitmapSetTo(uint256,bool,uint256) (runs: 256, μ: 9883, ~: 2882)
LibBitmapTest:testBitmapSetTo(uint256,uint256) (runs: 256, μ: 26408, ~: 31598)
LibBitmapTest:testBitmapSetTo(uint256,uint256) (runs: 256, μ: 26356, ~: 31586)
LibBitmapTest:testBitmapToggle() (gas: 30832)
LibBitmapTest:testBitmapToggle(uint256,bool) (runs: 256, μ: 20720, ~: 23127)
LibBitmapTest:testBitmapToggle(uint256,bool) (runs: 256, μ: 20685, ~: 23127)
LibBitmapTest:testBitmapUnset() (gas: 22528)
LibBitmapTest:testBitmapUnset(uint256) (runs: 256, μ: 14317, ~: 14340)
LibBitmapTest:testBitmapUnsetBatch() (gas: 2891565)
Expand All @@ -148,12 +148,12 @@ LibBytemapTest:testBytemapSetFromBigArray() (gas: 23221)
LibCloneTest:testClone() (gas: 53011)
LibCloneTest:testClone(uint256) (runs: 256, μ: 71081, ~: 72947)
LibCloneTest:testCloneDeteministicWithImmutableArgs() (gas: 809562)
LibCloneTest:testCloneDeteministicWithImmutableArgs(address,uint256,uint256[],bytes,uint64,uint8,uint256) (runs: 256, μ: 1538972, ~: 1524148)
LibCloneTest:testCloneDeteministicWithImmutableArgs(address,uint256,uint256[],bytes,uint64,uint8,uint256) (runs: 256, μ: 1528455, ~: 1521710)
LibCloneTest:testCloneDeterministic() (gas: 75744)
LibCloneTest:testCloneDeterministic(uint256,bytes32) (runs: 256, μ: 94334, ~: 95655)
LibCloneTest:testCloneDeterministicRevertsIfAddressAlreadyUsed() (gas: 98418504)
LibCloneTest:testCloneWithImmutableArgs() (gas: 281114)
LibCloneTest:testCloneWithImmutableArgs(uint256,address,uint256,uint256[],uint64,uint8) (runs: 256, μ: 1417818, ~: 1383744)
LibCloneTest:testCloneWithImmutableArgs(uint256,address,uint256,uint256[],uint64,uint8) (runs: 256, μ: 1414322, ~: 1412476)
LibPRNGTest:testLCGGas() (gas: 20736)
LibPRNGTest:testPRNGGas() (gas: 25645)
LibPRNGTest:testPRNGNext() (gas: 16139)
Expand All @@ -175,9 +175,9 @@ LibSortTest:testSearchSortedElementInArray(uint256[],uint256) (runs: 256, μ: 69
LibSortTest:testSearchSortedElementInUniquifiedArray(uint256[],uint256) (runs: 256, μ: 80781, ~: 82287)
LibSortTest:testSearchSortedElementNotInArray() (gas: 5756)
LibSortTest:testSearchSortedElementNotInArray(uint256[],uint256) (runs: 256, μ: 70110, ~: 71772)
LibSortTest:testSearchSortedElementNotInArrayNarrow(uint256[],uint256) (runs: 256, μ: 90693, ~: 93500)
LibSortTest:testSearchSortedElementNotInArrayNarrow(uint256[],uint256) (runs: 256, μ: 90663, ~: 93500)
LibSortTest:testSearchSortedElementNotInUniquifiedArray(uint256[],uint256) (runs: 256, μ: 80988, ~: 82388)
LibSortTest:testSearchSortedElementNotInUniquifiedArrayNarrow(uint256[],uint256) (runs: 256, μ: 99661, ~: 102565)
LibSortTest:testSearchSortedElementNotInUniquifiedArrayNarrow(uint256[],uint256) (runs: 256, μ: 99629, ~: 102565)
LibSortTest:testSearchSortedOnRandomArrays(uint256[],uint256) (runs: 256, μ: 10966, ~: 11464)
LibSortTest:testSearchSortedWithEmptyArray() (gas: 633)
LibSortTest:testSort(uint256[]) (runs: 256, μ: 86763, ~: 85741)
Expand Down Expand Up @@ -315,24 +315,24 @@ OwnableRolesTest:testSetOwnerDirect() (gas: 17792)
OwnableRolesTest:testSetOwnerDirect(address) (runs: 256, μ: 17854, ~: 17873)
OwnableRolesTest:testTransferOwnership() (gas: 19445)
OwnableRolesTest:testTransferOwnership(address,bool,bool) (runs: 256, μ: 13790, ~: 13044)
SSTORE2Test:testFuzzReadInvalidPointerCustomBoundsReverts(address,uint256,uint256) (runs: 256, μ: 399992, ~: 189092)
SSTORE2Test:testFuzzReadInvalidPointerCustomStartBoundReverts(address,uint256) (runs: 256, μ: 429332, ~: 631671)
SSTORE2Test:testFuzzReadInvalidPointerRevert(address) (runs: 256, μ: 425906, ~: 631682)
SSTORE2Test:testFuzzWriteRead(bytes) (runs: 256, μ: 466039, ~: 661835)
SSTORE2Test:testFuzzWriteReadCustomBounds(bytes,uint256,uint256) (runs: 256, μ: 456420, ~: 630890)
SSTORE2Test:testFuzzWriteReadCustomBoundsOutOfRangeReverts(bytes,uint256,uint256) (runs: 256, μ: 435781, ~: 238202)
SSTORE2Test:testFuzzWriteReadCustomStartBound(bytes,uint256) (runs: 256, μ: 444770, ~: 256640)
SSTORE2Test:testFuzzWriteReadCustomStartBoundOutOfRangeReverts(bytes,uint256) (runs: 256, μ: 453037, ~: 664282)
SSTORE2Test:testFuzzReadInvalidPointerCustomBoundsReverts(address,uint256,uint256) (runs: 256, μ: 424211, ~: 631747)
SSTORE2Test:testFuzzReadInvalidPointerCustomStartBoundReverts(address,uint256) (runs: 256, μ: 427603, ~: 631671)
SSTORE2Test:testFuzzReadInvalidPointerRevert(address) (runs: 256, μ: 412073, ~: 631682)
SSTORE2Test:testFuzzWriteRead(bytes) (runs: 256, μ: 487104, ~: 669076)
SSTORE2Test:testFuzzWriteReadCustomBounds(bytes,uint256,uint256) (runs: 256, μ: 432253, ~: 252424)
SSTORE2Test:testFuzzWriteReadCustomBoundsOutOfRangeReverts(bytes,uint256,uint256) (runs: 256, μ: 428865, ~: 237602)
SSTORE2Test:testFuzzWriteReadCustomStartBound(bytes,uint256) (runs: 256, μ: 448655, ~: 259936)
SSTORE2Test:testFuzzWriteReadCustomStartBoundOutOfRangeReverts(bytes,uint256) (runs: 256, μ: 454766, ~: 664382)
SSTORE2Test:testReadInvalidPointerCustomBoundsReverts() (gas: 3197)
SSTORE2Test:testReadInvalidPointerCustomStartBoundReverts() (gas: 3218)
SSTORE2Test:testReadInvalidPointerReverts() (gas: 3215)
SSTORE2Test:testWriteRead() (gas: 76085)
SSTORE2Test:testWriteReadCustomBounds() (gas: 34587)
SSTORE2Test:testWriteReadCustomStartBound() (gas: 34779)
SSTORE2Test:testWriteReadEmptyBound() (gas: 33810)
SSTORE2Test:testWriteRead() (gas: 76106)
SSTORE2Test:testWriteReadCustomBounds() (gas: 34608)
SSTORE2Test:testWriteReadCustomStartBound() (gas: 34800)
SSTORE2Test:testWriteReadEmptyBound() (gas: 33834)
SSTORE2Test:testWriteReadEmptyOutOfBoundsReverts() (gas: 36473)
SSTORE2Test:testWriteReadFullBoundedRead() (gas: 76169)
SSTORE2Test:testWriteReadFullStartBound() (gas: 35054)
SSTORE2Test:testWriteReadFullBoundedRead() (gas: 76190)
SSTORE2Test:testWriteReadFullStartBound() (gas: 35075)
SSTORE2Test:testWriteReadOutOfBoundsReverts() (gas: 36495)
SSTORE2Test:testWriteReadOutOfStartBoundReverts() (gas: 36455)
SafeTransferLibTest:testApproveRevertSelector() (gas: 8903)
Expand Down
3 changes: 3 additions & 0 deletions src/utils/SSTORE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ library SSTORE2 {
data := mload(0x40)
mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
mstore(data, size)
mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
extcodecopy(pointer, add(data, 0x20), 1, size)
}
}
Expand Down Expand Up @@ -134,6 +135,7 @@ library SSTORE2 {
data := mload(0x40)
mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
mstore(data, size)
mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
extcodecopy(pointer, add(data, 0x20), add(start, 1), size)
}
}
Expand Down Expand Up @@ -177,6 +179,7 @@ library SSTORE2 {
data := mload(0x40)
mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
mstore(data, size)
mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
extcodecopy(pointer, add(data, 0x20), add(start, 1), size)
}
}
Expand Down
17 changes: 1 addition & 16 deletions test/DynamicBufferLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract DynamicBufferLibTest is TestPlus {
buffer.append(inputs[i]);
assertEq(buffer.data.length, expectedLength);
_brutalizeFreeMemoryStart();
_checkBytesIsZeroRightPadded(buffer.data);
_checkZeroRightPadded(buffer.data);
bool isCorrupted;
/// @solidity memory-safe-assembly
assembly {
Expand Down Expand Up @@ -99,21 +99,6 @@ contract DynamicBufferLibTest is TestPlus {
joinedHash = 0x166b0e99fea53034ed188896344996efc141b922127f90922905e478cb26b312;
}

function _checkBytesIsZeroRightPadded(bytes memory s) internal pure {
bool failed;
/// @solidity memory-safe-assembly
assembly {
let lastAlignedWord := mload(add(add(s, 0x20), and(mload(s), not(31))))
let remainder := and(mload(s), 31)
if remainder {
if shl(mul(8, remainder), lastAlignedWord) {
failed := 1
}
}
}
if (failed) revert("Bytes is not zero right padded!");
}

function _boundInputs(bytes[] memory inputs) internal pure {
// Limit the total number of inputs.
/// @solidity memory-safe-assembly
Expand Down
57 changes: 21 additions & 36 deletions test/LibString.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ contract LibStringTest is TestPlus {
}

function testToStringZeroRightPadded(uint256 x) public pure {
_checkStringIsZeroRightPadded(LibString.toString(x));
_checkZeroRightPadded(LibString.toString(x));
}

function testToHexStringZero() public {
Expand Down Expand Up @@ -108,7 +108,7 @@ contract LibStringTest is TestPlus {
}

function testToHexStringZeroRightPadded(uint256 x) public pure {
_checkStringIsZeroRightPadded(LibString.toHexString(x));
_checkZeroRightPadded(LibString.toHexString(x));
}

function testToHexStringFixedLengthInsufficientLength() public {
Expand All @@ -126,7 +126,7 @@ contract LibStringTest is TestPlus {
function testToHexStringFixedLengthZeroRightPadded(uint256 x, uint256 randomness) public pure {
uint256 minLength = (bytes(LibString.toHexString(x)).length - 2) * 2;
uint256 length = (randomness % 32) + minLength;
_checkStringIsZeroRightPadded(LibString.toHexString(x, length));
_checkZeroRightPadded(LibString.toHexString(x, length));
}

function testFromAddressToHexString() public {
Expand All @@ -137,7 +137,7 @@ contract LibStringTest is TestPlus {
}

function testAddressToHexStringZeroRightPadded(address x) public pure {
_checkStringIsZeroRightPadded(LibString.toHexString(x));
_checkZeroRightPadded(LibString.toHexString(x));
}

function testFromAddressToHexStringWithLeadingZeros() public {
Expand Down Expand Up @@ -212,18 +212,18 @@ contract LibStringTest is TestPlus {
}
string memory checksumed = LibString.toHexStringChecksumed(r);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(checksumed);
_checkZeroRightPadded(checksumed);
assertEq(keccak256(bytes(checksumed)), keccak256(bytes(expectedResult)));
}

function testHexStringNoPrefixVariants(uint256 x, uint256 randomness) public brutalizeMemory {
string memory noPrefix = LibString.toHexStringNoPrefix(x);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(noPrefix);
_checkZeroRightPadded(noPrefix);
string memory expectedResult = LibString.concat("0x", noPrefix);
string memory withPrefix = LibString.toHexString(x);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(withPrefix);
_checkZeroRightPadded(withPrefix);
assertEq(keccak256(bytes(withPrefix)), keccak256(bytes(expectedResult)));

uint256 length;
Expand All @@ -233,11 +233,11 @@ contract LibStringTest is TestPlus {
}
noPrefix = LibString.toHexStringNoPrefix(x, length);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(noPrefix);
_checkZeroRightPadded(noPrefix);
expectedResult = LibString.concat("0x", noPrefix);
withPrefix = LibString.toHexString(x, length);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(withPrefix);
_checkZeroRightPadded(withPrefix);
assertEq(keccak256(bytes(withPrefix)), keccak256(bytes(expectedResult)));

address xAddress;
Expand All @@ -247,11 +247,11 @@ contract LibStringTest is TestPlus {
}
noPrefix = LibString.toHexStringNoPrefix(xAddress);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(noPrefix);
_checkZeroRightPadded(noPrefix);
expectedResult = LibString.concat("0x", noPrefix);
withPrefix = LibString.toHexString(xAddress);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(withPrefix);
_checkZeroRightPadded(withPrefix);
assertEq(keccak256(bytes(withPrefix)), keccak256(bytes(expectedResult)));
}

Expand Down Expand Up @@ -305,7 +305,7 @@ contract LibStringTest is TestPlus {
_brutalizeFreeMemoryStart();
string memory replaced = LibString.replace(subject, search, replacement);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(replaced);
_checkZeroRightPadded(replaced);
assertEq(replaced, expectedResult);
} else {
string memory expectedResult = string(
Expand Down Expand Up @@ -504,7 +504,7 @@ contract LibStringTest is TestPlus {
_brutalizeFreeMemoryStart();
string memory expectedResult = _repeatOriginal(subject, times);
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(repeated);
_checkZeroRightPadded(repeated);
assertEq(repeated, expectedResult);
}

Expand Down Expand Up @@ -543,7 +543,7 @@ contract LibStringTest is TestPlus {
uint256 end = start + bytes(expectedResult).length;

string memory slice = LibString.slice(subject, start, end);
_checkStringIsZeroRightPadded(slice);
_checkZeroRightPadded(slice);
assertEq(slice, expectedResult);
}

Expand Down Expand Up @@ -676,7 +676,7 @@ contract LibStringTest is TestPlus {
_brutalizeFreeMemoryStart();
assertTrue(_stringArraysAreSame(splitted, elements));
for (uint256 i; i < splitted.length; ++i) {
_checkStringIsZeroRightPadded(splitted[i]);
_checkZeroRightPadded(splitted[i]);
}
}
}
Expand Down Expand Up @@ -729,7 +729,7 @@ contract LibStringTest is TestPlus {
string memory expectedResult = string(bytes.concat(bytes(a), bytes(b)));
_roundUpFreeMemoryPointer();
_brutalizeFreeMemoryStart();
_checkStringIsZeroRightPadded(concatenated);
_checkZeroRightPadded(concatenated);
assertEq(concatenated, expectedResult);
}

Expand Down Expand Up @@ -801,7 +801,7 @@ contract LibStringTest is TestPlus {

_roundUpFreeMemoryPointer();
string memory escaped = LibString.escapeHTML(input);
_checkStringIsZeroRightPadded(escaped);
_checkZeroRightPadded(escaped);
_brutalizeFreeMemoryStart();

assertEq(expectedResult, escaped);
Expand All @@ -827,7 +827,7 @@ contract LibStringTest is TestPlus {
string memory hexCode = LibString.replace(LibString.toHexString(i), "0x", "00");
string memory expectedOutput = string(bytes.concat(bytes("abc\\u"), bytes(hexCode), bytes("_123")));
string memory escaped = LibString.escapeJSON(input);
_checkStringIsZeroRightPadded(escaped);
_checkZeroRightPadded(escaped);
assertEq(escaped, expectedOutput);
}
}
Expand Down Expand Up @@ -856,7 +856,7 @@ contract LibStringTest is TestPlus {
_roundUpFreeMemoryPointer();
bytes32 packed = LibString.packOne(a);
string memory unpacked = LibString.unpackOne(packed);
_checkStringIsZeroRightPadded(unpacked);
_checkZeroRightPadded(unpacked);
_brutalizeFreeMemoryStart();

if (bytes(a).length < 32) {
Expand Down Expand Up @@ -904,8 +904,8 @@ contract LibStringTest is TestPlus {
bytes32 packed = LibString.packTwo(a, b);
_roundUpFreeMemoryPointer();
(string memory unpackedA, string memory unpackedB) = LibString.unpackTwo(packed);
_checkStringIsZeroRightPadded(unpackedA);
_checkStringIsZeroRightPadded(unpackedB);
_checkZeroRightPadded(unpackedA);
_checkZeroRightPadded(unpackedB);
_brutalizeFreeMemoryStart();

unchecked {
Expand Down Expand Up @@ -1005,21 +1005,6 @@ contract LibStringTest is TestPlus {
}
}

function _checkStringIsZeroRightPadded(string memory s) internal pure {
bool failed;
/// @solidity memory-safe-assembly
assembly {
let lastAlignedWord := mload(add(add(s, 0x20), and(mload(s), not(31))))
let remainder := and(mload(s), 31)
if remainder {
if shl(mul(8, remainder), lastAlignedWord) {
failed := 1
}
}
}
if (failed) revert("String is not zero right padded!");
}

function _stringArraysAreSame(string[] memory a, string[] memory b) internal pure returns (bool) {
unchecked {
if (a.length != b.length) {
Expand Down
Loading

0 comments on commit 65a0e7c

Please sign in to comment.