Skip to content

Commit 9d77aef

Browse files
authored
🐞 Fix LibBytes.uint8At stale byte (#1550)
* 🐞 Fix LibBytes.uint8At stale byte * Added Test Case
1 parent a07082e commit 9d77aef

3 files changed

Lines changed: 81 additions & 18 deletions

File tree

β€Žsrc/utils/LibBytes.solβ€Ž

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,19 @@ library LibBytes {
129129
assembly {
130130
for { let packed := sload($.slot) } 1 {} {
131131
if iszero(eq(or(packed, 0xff), packed)) {
132+
if iszero(lt(i, and(packed, 0xff))) { break }
132133
if iszero(gt(i, 0x1e)) {
133134
result := byte(i, packed)
134135
break
135136
}
136-
if iszero(gt(i, and(0xff, packed))) {
137-
mstore(0x00, $.slot)
138-
let j := sub(i, 0x1f)
139-
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
140-
}
141-
break
142-
}
143-
if iszero(gt(i, shr(8, packed))) {
144137
mstore(0x00, $.slot)
145-
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
138+
let j := sub(i, 0x1f)
139+
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
140+
break
146141
}
142+
if iszero(gt(shr(8, packed), i)) { break }
143+
mstore(0x00, $.slot)
144+
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
147145
break
148146
}
149147
}

β€Žsrc/utils/g/LibBytes.solβ€Ž

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,21 +133,19 @@ library LibBytes {
133133
assembly {
134134
for { let packed := sload($.slot) } 1 {} {
135135
if iszero(eq(or(packed, 0xff), packed)) {
136+
if iszero(lt(i, and(packed, 0xff))) { break }
136137
if iszero(gt(i, 0x1e)) {
137138
result := byte(i, packed)
138139
break
139140
}
140-
if iszero(gt(i, and(0xff, packed))) {
141-
mstore(0x00, $.slot)
142-
let j := sub(i, 0x1f)
143-
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
144-
}
145-
break
146-
}
147-
if iszero(gt(i, shr(8, packed))) {
148141
mstore(0x00, $.slot)
149-
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
142+
let j := sub(i, 0x1f)
143+
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
144+
break
150145
}
146+
if iszero(gt(shr(8, packed), i)) { break }
147+
mstore(0x00, $.slot)
148+
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
151149
break
152150
}
153151
}

β€Žtest/LibBytes.t.solβ€Ž

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ pragma solidity ^0.8.4;
44
import "./utils/SoladyTest.sol";
55
import {LibBytes} from "../src/utils/LibBytes.sol";
66

7+
contract BytesStore {
8+
LibBytes.BytesStorage internal value;
9+
10+
function set(bytes calldata newValue) external {
11+
LibBytes.setCalldata(value, newValue);
12+
}
13+
14+
function length() external view returns (uint256) {
15+
return LibBytes.length(value);
16+
}
17+
18+
function at_(uint256 index) external view returns (uint8) {
19+
return LibBytes.uint8At(value, index);
20+
}
21+
}
22+
723
contract LibBytesTest is SoladyTest {
824
function testLoad(bytes memory a) public {
925
if (a.length < 32) a = abi.encodePacked(a, new bytes(32));
@@ -414,4 +430,55 @@ contract LibBytesTest is SoladyTest {
414430
require(keccak256(expectedChildren[i]) == keccak256(children[i]));
415431
}
416432
}
433+
434+
function testUint8AtStaleByteAfterShrink() public {
435+
BytesStore store = new BytesStore();
436+
bytes memory previous = new bytes(32);
437+
previous[31] = 0xbb;
438+
439+
store.set(previous);
440+
store.set(new bytes(31));
441+
442+
assertEq(store.length(), 31);
443+
assertEq(store.at_(31), 0);
444+
}
445+
446+
function testUint8AtPaddingAfterShortValue() public {
447+
BytesStore store = _storeWithDirtyPadding(5);
448+
assertEq(store.length(), 5);
449+
assertEq(store.at_(4), 0x11);
450+
assertEq(store.at_(5), 0);
451+
assertEq(store.at_(30), 0);
452+
}
453+
454+
function testUint8AtPaddingAfterSpilledValue() public {
455+
BytesStore store = _storeWithDirtyPadding(40);
456+
assertEq(store.length(), 40);
457+
assertEq(store.at_(39), 0x11);
458+
assertEq(store.at_(40), 0);
459+
}
460+
461+
function testUint8AtPaddingAfterTaggedLongValue() public {
462+
BytesStore store = _storeWithDirtyPadding(255);
463+
assertEq(store.length(), 255);
464+
assertEq(store.at_(254), 0x11);
465+
assertEq(store.at_(255), 0);
466+
assertEq(store.at_(256), 0);
467+
}
468+
469+
function _storeWithDirtyPadding(uint256 n) internal returns (BytesStore store) {
470+
store = new BytesStore();
471+
472+
bytes memory value = new bytes(n);
473+
for (uint256 i; i < n; ++i) {
474+
value[i] = 0x11;
475+
}
476+
477+
bytes memory data = abi.encodeWithSelector(BytesStore.set.selector, value);
478+
for (uint256 i = 4 + 32 + 32 + n; i < data.length; ++i) {
479+
data[i] = 0xaa;
480+
}
481+
(bool success,) = address(store).call(data);
482+
require(success);
483+
}
417484
}

0 commit comments

Comments
Β (0)