Skip to content

Commit 4830d00

Browse files
SwayStar123bitzoicsdankelK1-R1
authored
Correctly check for overflow in add, mul and pow (#6452)
## Description Adds flag checks for overflow in core lib, properly cap values if overflow is enabled ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: Cameron Carstens <bitzoic.eth@gmail.com> Co-authored-by: Sophie Dankel <47993817+sdankel@users.noreply.github.com> Co-authored-by: K1-R1 <77465250+K1-R1@users.noreply.github.com>
1 parent 8232d42 commit 4830d00

File tree

3 files changed

+427
-25
lines changed
  • sway-lib-core/src
  • sway-lib-std/src
  • test/src/in_language_tests/test_programs/math_inline_tests/src

3 files changed

+427
-25
lines changed

sway-lib-core/src/ops.sw

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,15 @@ impl Add for u32 {
5959
// any non-64-bit value is compiled to a u64 value under-the-hood
6060
// constants (like Self::max() below) are also automatically promoted to u64
6161
let res = __add(self, other);
62+
// integer overflow
6263
if __gt(res, Self::max()) {
63-
// integer overflow
64-
__revert(0)
64+
if panic_on_overflow_is_enabled() {
65+
__revert(0)
66+
} else {
67+
// overflow enabled
68+
// res % (Self::max() + 1)
69+
__mod(res, __add(Self::max(), 1))
70+
}
6571
} else {
6672
// no overflow
6773
res
@@ -73,7 +79,13 @@ impl Add for u16 {
7379
fn add(self, other: Self) -> Self {
7480
let res = __add(self, other);
7581
if __gt(res, Self::max()) {
76-
__revert(0)
82+
if panic_on_overflow_is_enabled() {
83+
__revert(0)
84+
} else {
85+
// overflow enabled
86+
// res % (Self::max() + 1)
87+
__mod(res, __add(Self::max(), 1))
88+
}
7789
} else {
7890
res
7991
}
@@ -93,7 +105,16 @@ impl Add for u8 {
93105
input: u64
94106
};
95107
if __gt(res_u64, max_u8_u64) {
96-
__revert(0)
108+
if panic_on_overflow_is_enabled() {
109+
__revert(0)
110+
} else {
111+
// overflow enabled
112+
// res % (Self::max() + 1)
113+
let res_u64 = __mod(res_u64, __add(max_u8_u64, 1));
114+
asm(input: res_u64) {
115+
input: u8
116+
}
117+
}
97118
} else {
98119
asm(input: res_u64) {
99120
input: u8
@@ -229,8 +250,14 @@ impl Multiply for u32 {
229250
// constants (like Self::max() below) are also automatically promoted to u64
230251
let res = __mul(self, other);
231252
if __gt(res, Self::max()) {
232-
// integer overflow
233-
__revert(0)
253+
if panic_on_overflow_is_enabled() {
254+
// integer overflow
255+
__revert(0)
256+
} else {
257+
// overflow enabled
258+
// res % (Self::max() + 1)
259+
__mod(res, __add(Self::max(), 1))
260+
}
234261
} else {
235262
// no overflow
236263
res
@@ -242,7 +269,11 @@ impl Multiply for u16 {
242269
fn multiply(self, other: Self) -> Self {
243270
let res = __mul(self, other);
244271
if __gt(res, Self::max()) {
245-
__revert(0)
272+
if panic_on_overflow_is_enabled() {
273+
__revert(0)
274+
} else {
275+
__mod(res, __add(Self::max(), 1))
276+
}
246277
} else {
247278
res
248279
}
@@ -262,7 +293,16 @@ impl Multiply for u8 {
262293
input: u64
263294
};
264295
if __gt(res_u64, max_u8_u64) {
265-
__revert(0)
296+
if panic_on_overflow_is_enabled() {
297+
__revert(0)
298+
} else {
299+
// overflow enabled
300+
// res % (Self::max() + 1)
301+
let res_u64 = __mod(res_u64, __add(max_u8_u64, 1));
302+
asm(input: res_u64) {
303+
input: u8
304+
}
305+
}
266306
} else {
267307
asm(input: res_u64) {
268308
input: u8
@@ -1258,3 +1298,19 @@ pub fn ok_str_eq() {
12581298
assert("" != "a");
12591299
assert("a" != "b");
12601300
}
1301+
1302+
fn flags() -> u64 {
1303+
asm() {
1304+
flag
1305+
}
1306+
}
1307+
1308+
fn panic_on_overflow_is_enabled() -> bool {
1309+
__eq(
1310+
__and(
1311+
flags(),
1312+
0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000010,
1313+
),
1314+
0,
1315+
)
1316+
}

sway-lib-std/src/math.sw

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
library;
33

44
use ::assert::*;
5+
use ::revert::revert;
6+
use ::option::Option::{self, None, Some};
57
use ::flags::{
68
disable_panic_on_overflow,
79
panic_on_overflow_enabled,
@@ -78,12 +80,38 @@ pub trait Power {
7880
fn pow(self, exponent: u32) -> Self;
7981
}
8082

83+
fn u256_checked_mul(a: u256, b: u256) -> Option<u256> {
84+
let res = u256::zero();
85+
86+
// The six-bit immediate value is used to select operating mode, as follows:
87+
88+
// Bits Short name Description
89+
// ..XXXX reserved Reserved and must be zero
90+
// .X.... indirect0 Is lhs operand ($rB) indirect or not
91+
// X..... indirect1 Is rhs operand ($rC) indirect or not
92+
// As both operands are indirect, 110000 is used, which is 48 in decimal.
93+
let of = asm(res: res, a: a, b: b) {
94+
wqml res a b i48;
95+
of: u64
96+
};
97+
98+
if of != 0 {
99+
return None;
100+
}
101+
102+
Some(res)
103+
}
104+
81105
impl Power for u256 {
82106
/// Raises self to the power of `exponent`, using exponentiation by squaring.
83107
///
84-
/// # Panics
108+
/// # Additional Information
109+
///
110+
/// * If panic on overflow is disabled, and the result overflows, the return value will be 0.
85111
///
86-
/// Panics if the result overflows the type.
112+
/// # Reverts
113+
///
114+
/// * Reverts if the result overflows the type, if panic on overflow is enabled.
87115
fn pow(self, exponent: u32) -> Self {
88116
let one = 0x0000000000000000000000000000000000000000000000000000000000000001u256;
89117

@@ -97,13 +125,28 @@ impl Power for u256 {
97125

98126
while exp > 1 {
99127
if (exp & 1) == 1 {
100-
acc = acc * base;
128+
// acc = acc * base;
129+
let res = u256_checked_mul(acc, base);
130+
acc = match res {
131+
Some(val) => val,
132+
None => return u256::zero(),
133+
}
101134
}
102135
exp = exp >> 1;
103-
base = base * base;
136+
// base = base * base;
137+
let res = u256_checked_mul(base, base);
138+
base = match res {
139+
Some(val) => val,
140+
None => return u256::zero(),
141+
}
104142
}
105143

106-
acc * base
144+
// acc * base
145+
let res = u256_checked_mul(acc, base);
146+
match res {
147+
Some(val) => val,
148+
None => u256::zero(),
149+
}
107150
}
108151
}
109152

@@ -118,14 +161,21 @@ impl Power for u64 {
118161

119162
impl Power for u32 {
120163
fn pow(self, exponent: u32) -> Self {
121-
let res = asm(r1: self, r2: exponent, r3) {
164+
let mut res = asm(r1: self, r2: exponent, r3) {
122165
exp r3 r1 r2;
123166
r3: u64
124167
};
125-
// If panic on wrapping math is enabled, only then revert
126-
if panic_on_overflow_enabled() {
127-
assert(res <= Self::max().as_u64());
168+
169+
if res > Self::max().as_u64() {
170+
// If panic on wrapping math is enabled, only then revert
171+
if panic_on_overflow_enabled() {
172+
revert(0);
173+
} else {
174+
// Follow spec of returning 0 for overflow
175+
res = 0;
176+
}
128177
}
178+
129179
asm(r1: res) {
130180
r1: Self
131181
}
@@ -134,14 +184,21 @@ impl Power for u32 {
134184

135185
impl Power for u16 {
136186
fn pow(self, exponent: u32) -> Self {
137-
let res = asm(r1: self, r2: exponent, r3) {
187+
let mut res = asm(r1: self, r2: exponent, r3) {
138188
exp r3 r1 r2;
139189
r3: u64
140190
};
141-
// If panic on wrapping math is enabled, only then revert
142-
if panic_on_overflow_enabled() {
143-
assert(res <= Self::max().as_u64());
191+
192+
if res > Self::max().as_u64() {
193+
// If panic on wrapping math is enabled, only then revert
194+
if panic_on_overflow_enabled() {
195+
revert(0);
196+
} else {
197+
// Follow spec of returning 0 for overflow
198+
res = 0;
199+
}
144200
}
201+
145202
asm(r1: res) {
146203
r1: Self
147204
}
@@ -150,14 +207,21 @@ impl Power for u16 {
150207

151208
impl Power for u8 {
152209
fn pow(self, exponent: u32) -> Self {
153-
let res = asm(r1: self, r2: exponent, r3) {
210+
let mut res = asm(r1: self, r2: exponent, r3) {
154211
exp r3 r1 r2;
155212
r3: u64
156213
};
157-
// If panic on wrapping math is enabled, only then revert
158-
if panic_on_overflow_enabled() {
159-
assert(res <= Self::max().as_u64());
214+
215+
if res > Self::max().as_u64() {
216+
// If panic on wrapping math is enabled, only then revert
217+
if panic_on_overflow_enabled() {
218+
revert(0);
219+
} else {
220+
// Follow spec of returning 0 for overflow
221+
res = 0;
222+
}
160223
}
224+
161225
asm(r1: res) {
162226
r1: Self
163227
}

0 commit comments

Comments
 (0)