Skip to content

Commit ac7d53c

Browse files
committed
feat: add integer overflow/underflow security demos
1 parent cb8215f commit ac7d53c

4 files changed

Lines changed: 132 additions & 0 deletions

File tree

src/OverflowChecked.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
contract OverflowChecked {
5+
function add(uint256 a, uint256 b) external pure returns (uint256) {
6+
return a + b;
7+
}
8+
9+
function sub(uint256 a, uint256 b) external pure returns (uint256) {
10+
return a - b;
11+
}
12+
}

src/OverflowLegacy.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
contract OverflowLegacy {
5+
function add(uint256 a, uint256 b) external pure returns (uint256) {
6+
unchecked {
7+
return a + b;
8+
}
9+
}
10+
11+
function sub(uint256 a, uint256 b) external pure returns (uint256) {
12+
unchecked {
13+
return a - b;
14+
}
15+
}
16+
}

src/OverflowUnchecked.sol

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
contract OverflowUnchecked {
5+
error Overflow();
6+
error Underflow();
7+
8+
function add(uint256 a, uint256 b) external pure returns (uint256) {
9+
if (a > type(uint256).max - b) revert Overflow();
10+
11+
unchecked {
12+
return a + b;
13+
}
14+
}
15+
16+
function sub(uint256 a, uint256 b) external pure returns (uint256) {
17+
if (a < b) revert Underflow();
18+
19+
unchecked {
20+
return a - b;
21+
}
22+
}
23+
24+
function addWrap(uint256 a, uint256 b) external pure returns (uint256) {
25+
unchecked {
26+
return a + b;
27+
}
28+
}
29+
30+
function subWrap(uint256 a, uint256 b) external pure returns (uint256) {
31+
unchecked {
32+
return a - b;
33+
}
34+
}
35+
}

test/Overflow.t.sol

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import {Test} from "forge-std/Test.sol";
5+
import {OverflowLegacy} from "../src/OverflowLegacy.sol";
6+
import {OverflowChecked} from "../src/OverflowChecked.sol";
7+
import {OverflowUnchecked} from "../src/OverflowUnchecked.sol";
8+
9+
contract overflowTest is Test {
10+
OverflowLegacy legacy;
11+
OverflowChecked checked;
12+
OverflowUnchecked uncheckedDemo;
13+
14+
function setUp() public {
15+
legacy = new OverflowLegacy();
16+
checked = new OverflowChecked();
17+
uncheckedDemo = new OverflowUnchecked();
18+
}
19+
20+
function testLegacyAddWrapsToZero() public {
21+
uint256 result = legacy.add(type(uint256).max, 1);
22+
assertEq(result, 0);
23+
}
24+
25+
function testLegacySubWrapsToMax() public {
26+
uint256 result = legacy.sub(0, 1);
27+
assertEq(result, type(uint256).max);
28+
}
29+
30+
function testCheckedAddRevertsOnOverflow() public {
31+
vm.expectRevert();
32+
checked.add(type(uint256).max, 1);
33+
}
34+
35+
function testCheckedSubRevertsOnUnderflow() public {
36+
vm.expectRevert();
37+
checked.sub(0, 1);
38+
}
39+
40+
function testUncheckedAddWithGuardWorks() public {
41+
uint256 result = uncheckedDemo.add(10, 20);
42+
assertEq(result, 30);
43+
}
44+
45+
function testUncheckedSubWithGuardWorks() public {
46+
uint256 result = uncheckedDemo.sub(20, 10);
47+
assertEq(result, 10);
48+
}
49+
50+
function testUncheckedAddRevertsWhenGuardFails() public {
51+
vm.expectRevert(OverflowUnchecked.Overflow.selector);
52+
uncheckedDemo.add(type(uint256).max, 1);
53+
}
54+
55+
function testUncheckedSubRevertsWhenGuardFails() public {
56+
vm.expectRevert(OverflowUnchecked.Underflow.selector);
57+
uncheckedDemo.sub(0, 1);
58+
}
59+
60+
function testUncheckedAddWrapFunctionWrapsToZero() public {
61+
uint256 result = uncheckedDemo.addWrap(type(uint256).max, 1);
62+
assertEq(result, 0);
63+
}
64+
65+
function testUncheckedSubWrapFunctionWrapsToMax() public {
66+
uint256 result = uncheckedDemo.subWrap(0, 1);
67+
assertEq(result, type(uint256).max);
68+
}
69+
}

0 commit comments

Comments
 (0)