Skip to content

Commit 5d251ad

Browse files
authored
v0.8.0 - Solidity >0.8.0 support! 🎉 (#46)
* Create 0.8.0 folder structure + initial edits * Realize the folder structure is moot since we cannot use two different solc in Truffle * Get stricter about versions + remove constructor visibility * change MAX_UINT * Change README to reflect new versioning system * change version * Remove overflow checks in typecast methods * Remove overflow check in slice()
1 parent 358c9db commit 5d251ad

File tree

11 files changed

+6053
-74
lines changed

11 files changed

+6053
-74
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.sol linguist-language=Solidity

README.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,52 @@
22

33
# Solidity Bytes Arrays Utils
44

5-
Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
5+
Bytes tightly packed arrays' utility library for ethereum contracts written in Solidity.
66

77
The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
88

9-
Given this library has an all-internal collection of methods it doesn't make sense having it reside in the mainnet. Instead it will only be available in EPM as an installable package.
9+
Given this library has an all-internal collection of methods it doesn't make sense to have it reside in the mainnet. Instead it will only be available on EPM as an installable package.
1010

11-
_Version Notes_:
11+
## Important Fixes Changelog
12+
13+
_**2021-01-07**_
14+
15+
A bug regarding zero-length slices was disclosed by @MrChico following an audit to the Optimism codebase.
16+
17+
The exact bug happened under the following conditions: if memory slots higher then the current free-memory pointer were tainted before calling the `slice` method with a desired length of `0`, the returned bytes array, instead of being a zero-length slice was an array of arbitrary length based on the values that previously populated that memory region.
18+
19+
Overall, the usage of zero-length slices should be pretty unusual and, as such, hopefully, this bug does not have far-reaching implications. Nonetheless, *please update the library to the new version if you're using it in production*.
20+
21+
**TL;DR: if you're using the `slice` method with a length parameter of '0' in your codebase, please update to version 0.1.2 of the bytes library ASAP!**
22+
23+
_**2020-11-01**_
24+
25+
There was a **critical bug** in the `slice` method, reported on an audit to a DXDao codebase.
26+
27+
Previously, no checks were being made on overflows of the `_start` and `_length` parameters since previous reviews of the codebase deemed this overflow "unexploitable" because of an inordinate expansion of memory (i.e., reading an immensely large memory offset causing huge memory expansion) resulting in an out-of-gas exception.
28+
29+
However, as noted in the review mentioned above, this is not the case. The `slice` method in versions `<=0.9.0` actually allows for arbitrary _kind of_ (i.e., it allows memory writes to very specific values) arbitrary memory writes _in the specific case where these parameters are user-supplied inputs and not hardcoded values (which is uncommon).
30+
31+
This made me realize that in permissioned blockchains where gas is also not a limiting factor this could become problematic in other methods and so I updated all typecasting-related methods to include new bound checks as well.
32+
33+
**TL;DR: if you're using the `slice` method with user-supplied inputs in your codebase please update the bytes library immediately!**
34+
35+
## _Version Notes_:
36+
37+
* Starting from version `v0.8.0` the versioning will change to follow compatible Solidity's compiler versions.
38+
This means that now the library will only compile on Solidity versions `>=0.8.0` so, if you need `<0.8.0` support for your project just use `v0.1.2` of the library with:
39+
40+
```
41+
$ truffle install [email protected]
42+
```
43+
or
44+
```
45+
$ npm install [email protected]
46+
```
47+
48+
* Version `v0.1.2` has a major bug fix.
49+
50+
* Version `v0.1.1` has a critical bug fix.
1251

1352
* Version `v0.9.0` now compiles with Solidity compilers `0.5.x` and `0.6.x`.
1453

contracts/AssertBytes.sol

100755100644
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* This library is compliant with the test event convention that the Truffle suite uses.
88
*/
99

10-
pragma solidity >=0.5.0 <0.7.0;
10+
pragma solidity >=0.8.0 <0.9.0;
1111

1212

1313
library AssertBytes {
@@ -48,7 +48,7 @@ library AssertBytes {
4848
for {
4949
let cc := add(_b, 0x20)
5050
// the next line is the loop condition:
51-
// while(uint(mc < end) + cb == 2)
51+
// while(uint256(mc < end) + cb == 2)
5252
} eq(add(lt(mc, end), cb), 2) {
5353
mc := add(mc, 0x20)
5454
cc := add(cc, 0x20)
@@ -104,7 +104,7 @@ library AssertBytes {
104104

105105
assembly {
106106
// we know _a_offset is 0
107-
let fslot := sload(_a_slot)
107+
let fslot := sload(_a.slot)
108108
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
109109
let mlength := mload(_b)
110110

@@ -133,14 +133,14 @@ library AssertBytes {
133133
let cb := 1
134134

135135
// get the keccak hash to get the contents of the array
136-
mstore(0x0, _a_slot)
136+
mstore(0x0, _a.slot)
137137
let sc := keccak256(0x0, 0x20)
138138

139139
let mc := add(_b, 0x20)
140140
let end := add(mc, mlength)
141141

142142
// the next line is the loop condition:
143-
// while(uint(mc < end) + cb == 2)
143+
// while(uint256(mc < end) + cb == 2)
144144
for {} eq(add(lt(mc, end), cb), 2) {
145145
sc := add(sc, 1)
146146
mc := add(mc, 0x20)

contracts/BytesLib.sol

100755100644
Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
77
* The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
88
*/
9-
pragma solidity >=0.5.0 <0.7.0;
9+
pragma solidity >=0.8.0 <0.9.0;
1010

1111

1212
library BytesLib {
@@ -93,7 +93,7 @@ library BytesLib {
9393
// Read the first 32 bytes of _preBytes storage, which is the length
9494
// of the array. (We don't need to use the offset into the slot
9595
// because arrays use the entire slot.)
96-
let fslot := sload(_preBytes_slot)
96+
let fslot := sload(_preBytes.slot)
9797
// Arrays of 31 bytes or less have an even value in their slot,
9898
// while longer arrays have an odd value. The actual length is
9999
// the slot divided by two for odd values, and the lowest order
@@ -113,7 +113,7 @@ library BytesLib {
113113
// update the contents of the slot.
114114
// uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
115115
sstore(
116-
_preBytes_slot,
116+
_preBytes.slot,
117117
// all the modifications to the slot are inside this
118118
// next block
119119
add(
@@ -143,11 +143,11 @@ library BytesLib {
143143
// The stored value fits in the slot, but the combined value
144144
// will exceed it.
145145
// get the keccak hash to get the contents of the array
146-
mstore(0x0, _preBytes_slot)
146+
mstore(0x0, _preBytes.slot)
147147
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
148148

149149
// save new length
150-
sstore(_preBytes_slot, add(mul(newlength, 2), 1))
150+
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
151151

152152
// The contents of the _postBytes array start 32 bytes into
153153
// the structure. Our first read should obtain the `submod`
@@ -190,12 +190,12 @@ library BytesLib {
190190
}
191191
default {
192192
// get the keccak hash to get the contents of the array
193-
mstore(0x0, _preBytes_slot)
193+
mstore(0x0, _preBytes.slot)
194194
// Start copying to the last used word of the stored array.
195195
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
196196

197197
// save new length
198-
sstore(_preBytes_slot, add(mul(newlength, 2), 1))
198+
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
199199

200200
// Copy over the first `submod` bytes of the new data as in
201201
// case 1 above.
@@ -227,14 +227,15 @@ library BytesLib {
227227

228228
function slice(
229229
bytes memory _bytes,
230-
uint _start,
231-
uint _length
230+
uint256 _start,
231+
uint256 _length
232232
)
233233
internal
234234
pure
235235
returns (bytes memory)
236236
{
237-
require(_bytes.length >= (_start + _length));
237+
require(_length + 31 >= _length, "slice_overflow");
238+
require(_bytes.length >= _start + _length, "slice_outOfBounds");
238239

239240
bytes memory tempBytes;
240241

@@ -282,6 +283,9 @@ library BytesLib {
282283
//if we want a zero-length slice let's just return a zero-length array
283284
default {
284285
tempBytes := mload(0x40)
286+
//zero out the 32 bytes slice we are about to return
287+
//we need to do it because Solidity does not garbage collect
288+
mstore(tempBytes, 0)
285289

286290
mstore(0x40, add(tempBytes, 0x20))
287291
}
@@ -290,8 +294,8 @@ library BytesLib {
290294
return tempBytes;
291295
}
292296

293-
function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) {
294-
require(_bytes.length >= (_start + 20));
297+
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
298+
require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
295299
address tempAddress;
296300

297301
assembly {
@@ -301,8 +305,8 @@ library BytesLib {
301305
return tempAddress;
302306
}
303307

304-
function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) {
305-
require(_bytes.length >= (_start + 1));
308+
function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
309+
require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
306310
uint8 tempUint;
307311

308312
assembly {
@@ -312,8 +316,8 @@ library BytesLib {
312316
return tempUint;
313317
}
314318

315-
function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) {
316-
require(_bytes.length >= (_start + 2));
319+
function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
320+
require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
317321
uint16 tempUint;
318322

319323
assembly {
@@ -323,8 +327,8 @@ library BytesLib {
323327
return tempUint;
324328
}
325329

326-
function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) {
327-
require(_bytes.length >= (_start + 4));
330+
function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
331+
require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
328332
uint32 tempUint;
329333

330334
assembly {
@@ -334,8 +338,8 @@ library BytesLib {
334338
return tempUint;
335339
}
336340

337-
function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) {
338-
require(_bytes.length >= (_start + 8));
341+
function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
342+
require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
339343
uint64 tempUint;
340344

341345
assembly {
@@ -345,8 +349,8 @@ library BytesLib {
345349
return tempUint;
346350
}
347351

348-
function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) {
349-
require(_bytes.length >= (_start + 12));
352+
function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
353+
require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
350354
uint96 tempUint;
351355

352356
assembly {
@@ -356,8 +360,8 @@ library BytesLib {
356360
return tempUint;
357361
}
358362

359-
function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) {
360-
require(_bytes.length >= (_start + 16));
363+
function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
364+
require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
361365
uint128 tempUint;
362366

363367
assembly {
@@ -367,8 +371,8 @@ library BytesLib {
367371
return tempUint;
368372
}
369373

370-
function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) {
371-
require(_bytes.length >= (_start + 32));
374+
function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
375+
require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
372376
uint256 tempUint;
373377

374378
assembly {
@@ -378,8 +382,8 @@ library BytesLib {
378382
return tempUint;
379383
}
380384

381-
function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) {
382-
require(_bytes.length >= (_start + 32));
385+
function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
386+
require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
383387
bytes32 tempBytes32;
384388

385389
assembly {
@@ -410,7 +414,7 @@ library BytesLib {
410414
for {
411415
let cc := add(_postBytes, 0x20)
412416
// the next line is the loop condition:
413-
// while(uint(mc < end) + cb == 2)
417+
// while(uint256(mc < end) + cb == 2)
414418
} eq(add(lt(mc, end), cb), 2) {
415419
mc := add(mc, 0x20)
416420
cc := add(cc, 0x20)
@@ -444,7 +448,7 @@ library BytesLib {
444448

445449
assembly {
446450
// we know _preBytes_offset is 0
447-
let fslot := sload(_preBytes_slot)
451+
let fslot := sload(_preBytes.slot)
448452
// Decode the length of the stored array like in concatStorage().
449453
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
450454
let mlength := mload(_postBytes)
@@ -474,14 +478,14 @@ library BytesLib {
474478
let cb := 1
475479

476480
// get the keccak hash to get the contents of the array
477-
mstore(0x0, _preBytes_slot)
481+
mstore(0x0, _preBytes.slot)
478482
let sc := keccak256(0x0, 0x20)
479483

480484
let mc := add(_postBytes, 0x20)
481485
let end := add(mc, mlength)
482486

483487
// the next line is the loop condition:
484-
// while(uint(mc < end) + cb == 2)
488+
// while(uint256(mc < end) + cb == 2)
485489
for {} eq(add(lt(mc, end), cb), 2) {
486490
sc := add(sc, 1)
487491
mc := add(mc, 0x20)

contracts/Migrations.sol

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
// SPDX-License-Identifier: Unlicense
2-
pragma solidity >=0.5.0 <0.7.0;
2+
pragma solidity >=0.8.0;
3+
34

45

56
contract Migrations {
67
address public owner;
7-
uint public last_completed_migration;
8+
uint256 public last_completed_migration;
89

910
modifier restricted() {
1011
if (msg.sender == owner) _;
1112
}
1213

13-
constructor () public {
14+
constructor () {
1415
owner = msg.sender;
1516
}
1617

17-
function setCompleted(uint completed) public restricted {
18+
function setCompleted(uint256 completed) public restricted {
1819
last_completed_migration = completed;
1920
}
2021

ethpm.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"package_name": "bytes",
3-
"version": "0.0.9",
3+
"version": "0.8.0",
44
"description": "Solidity bytes tightly packed arrays utility library.",
55
"authors": [
66
"Gonçalo Sá <[email protected]>"

0 commit comments

Comments
 (0)