From 0b3111771711104d4c52f6dfdf2cdc1702ea2202 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Thu, 17 Oct 2024 18:29:30 +0700 Subject: [PATCH 01/12] fix: handle u32 & u16 subtract underflow manually --- sway-lib-core/src/ops.sw | 29 ++++++++-- .../math_inline_tests/src/main.sw | 56 +++++++++++++++++++ 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 3e820b71025..2d1fbc2e6d2 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -173,17 +173,38 @@ impl Subtract for u64 { } } -// unlike addition, underflowing subtraction does not need special treatment -// because VM handles underflow impl Subtract for u32 { fn subtract(self, other: Self) -> Self { - __sub(self, other) + // any non-64-bit value is compiled to a u64 value under-the-hood + // constants (like Self::max() below) are also automatically promoted to u64 + let res = __sub(self, other); + // integer underflow + if __gt(res, Self::max()) { + if panic_on_overflow_is_enabled() { + __revert(0) + } else { + // overflow enabled + __mod(res, __add(Self::max(), 1)) + } + } else { + // no overflow + res + } } } impl Subtract for u16 { fn subtract(self, other: Self) -> Self { - __sub(self, other) + let res = __sub(self, other); + if __gt(res, Self::max()) { + if panic_on_overflow_is_enabled() { + __revert(0) + } else { + __mod(res, __add(Self::max(), 1)) + } + } else { + res + } } } diff --git a/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw b/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw index c2641d71272..b5c407aa8dd 100644 --- a/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw +++ b/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw @@ -900,6 +900,22 @@ fn math_u256_overflow_mul_revert() { log(b); } +#[test(should_revert)] +fn math_u16_underflow_sub_revert() { + let a = 0u16; + let b = 1u16; + let c = a - b; + log(c); +} + +#[test(should_revert)] +fn math_u32_underflow_sub_revert() { + let a = 0u32; + let b = 1u32; + let c = a - b; + log(c); +} + #[test] fn math_u8_overflow_add() { let _ = disable_panic_on_overflow(); @@ -944,6 +960,26 @@ fn math_u16_overflow_add() { require(e == u16::max() - 2, e); } +#[test] +fn math_u16_underflow_sub() { + assert((u16::max() - u16::max()) == 0u16); + assert((u16::min() - u16::min()) == 0u16); + assert((10u16 - 5u16) == 5u16); + + let _ = disable_panic_on_overflow(); + + let a = 0u16; + let b = 1u16; + + let c = a - b; + assert(c == u16::max()); + + let d = u16::max(); + + let e = a - d; + assert(e == b); +} + #[test] fn math_u32_overflow_add() { let _ = disable_panic_on_overflow(); @@ -966,6 +1002,26 @@ fn math_u32_overflow_add() { require(e == u32::max() - 2, e); } +#[test] +fn math_u32_underflow_sub() { + assert((u32::max() - u32::max()) == 0u32); + assert((u32::min() - u32::min()) == 0u32); + assert((10u32 - 5u32) == 5u32); + + let _ = disable_panic_on_overflow(); + + let a = 0u32; + let b = 1u32; + + let c = a - b; + assert(c == u32::max()); + + let d = u32::max(); + + let e = a - d; + assert(e == b); +} + #[test] fn math_u64_overflow_add() { let _ = disable_panic_on_overflow(); From 260547ec99753dc74a12ee8230f02e28ae133d18 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:18:27 +1100 Subject: [PATCH 02/12] fix: u32 add impl avoid UB --- sway-lib-core/src/ops.sw | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 2d1fbc2e6d2..9868f740ca4 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -77,17 +77,31 @@ impl Add for u32 { impl Add for u16 { fn add(self, other: Self) -> Self { - let res = __add(self, other); - if __gt(res, Self::max()) { + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __add(self_u64, other_u64); + let max_u8_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u8_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { // overflow enabled // res % (Self::max() + 1) - __mod(res, __add(Self::max(), 1)) + let res_u64 = __mod(res_u64, __add(max_u8_u64, 1)); + asm(input: res_u64) { + input: u16 + } } } else { - res + asm(input: res_u64) { + input: u16 + } } } } From e83b2bece886520bf99339386c5d9c3cc42fb615 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:20:00 +1100 Subject: [PATCH 03/12] fix: u16 add impl avoid UB --- sway-lib-core/src/ops.sw | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 9868f740ca4..c24747ae49c 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -56,21 +56,31 @@ impl Add for u64 { // Emulate overflowing arithmetic for non-64-bit integer types impl Add for u32 { fn add(self, other: Self) -> Self { - // any non-64-bit value is compiled to a u64 value under-the-hood - // constants (like Self::max() below) are also automatically promoted to u64 - let res = __add(self, other); - // integer overflow - if __gt(res, Self::max()) { + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __add(self_u64, other_u64); + let max_u8_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u8_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { // overflow enabled // res % (Self::max() + 1) - __mod(res, __add(Self::max(), 1)) + let res_u64 = __mod(res_u64, __add(max_u8_u64, 1)); + asm(input: res_u64) { + input: u32 + } } } else { - // no overflow - res + asm(input: res_u64) { + input: u32 + } } } } From b9959b1e23e1821c6ba05402bcc0e42e76529e66 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:31:53 +1100 Subject: [PATCH 04/12] fix: u32 add param names --- sway-lib-core/src/ops.sw | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index c24747ae49c..562d0305a51 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -63,16 +63,16 @@ impl Add for u32 { input: u64 }; let res_u64 = __add(self_u64, other_u64); - let max_u8_u64 = asm(input: Self::max()) { + let max_u32_u64 = asm(input: Self::max()) { input: u64 }; - if __gt(res_u64, max_u8_u64) { + if __gt(res_u64, max_u32_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { // overflow enabled // res % (Self::max() + 1) - let res_u64 = __mod(res_u64, __add(max_u8_u64, 1)); + let res_u64 = __mod(res_u64, __add(max_u32_u64, 1)); asm(input: res_u64) { input: u32 } From 75eba8197969f979475bc1edda1efb5ec509be44 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:32:16 +1100 Subject: [PATCH 05/12] fix: u16 add param names --- sway-lib-core/src/ops.sw | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 562d0305a51..af98b7571a0 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -94,16 +94,16 @@ impl Add for u16 { input: u64 }; let res_u64 = __add(self_u64, other_u64); - let max_u8_u64 = asm(input: Self::max()) { + let max_u16_u64 = asm(input: Self::max()) { input: u64 }; - if __gt(res_u64, max_u8_u64) { + if __gt(res_u64, max_u16_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { // overflow enabled // res % (Self::max() + 1) - let res_u64 = __mod(res_u64, __add(max_u8_u64, 1)); + let res_u64 = __mod(res_u64, __add(max_u16_u64, 1)); asm(input: res_u64) { input: u16 } From a735c123ff009c589eba538a25d5d0426dbcd85a Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:32:54 +1100 Subject: [PATCH 06/12] fix: u32 sub impl avoid UB --- sway-lib-core/src/ops.sw | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index af98b7571a0..642573911db 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -199,20 +199,31 @@ impl Subtract for u64 { impl Subtract for u32 { fn subtract(self, other: Self) -> Self { - // any non-64-bit value is compiled to a u64 value under-the-hood - // constants (like Self::max() below) are also automatically promoted to u64 - let res = __sub(self, other); - // integer underflow - if __gt(res, Self::max()) { + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __sub(self_u64, other_u64); + let max_u32_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u32_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { // overflow enabled - __mod(res, __add(Self::max(), 1)) + // res % (Self::max() + 1) + let res_u64 = __mod(res_u64, __add(max_u32_u64, 1)); + asm(input: res_u64) { + input: u32 + } } } else { - // no overflow - res + asm(input: res_u64) { + input: u32 + } } } } From 8f71ac45d3c3e8a689b0994df5616e27b5d890b6 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:33:47 +1100 Subject: [PATCH 07/12] fix: u16 sub impl avoid UB --- sway-lib-core/src/ops.sw | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 642573911db..8185be0e6ae 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -230,15 +230,31 @@ impl Subtract for u32 { impl Subtract for u16 { fn subtract(self, other: Self) -> Self { - let res = __sub(self, other); - if __gt(res, Self::max()) { + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __sub(self_u64, other_u64); + let max_u16_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u16_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { - __mod(res, __add(Self::max(), 1)) + // overflow enabled + // res % (Self::max() + 1) + let res_u64 = __mod(res_u64, __add(max_u16_u64, 1)); + asm(input: res_u64) { + input: u16 + } } } else { - res + asm(input: res_u64) { + input: u16 + } } } } From b1472c20e82f4614e4bb7679039fbd4c4b966190 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:34:03 +1100 Subject: [PATCH 08/12] fix: u8 sub impl avoid UB --- sway-lib-core/src/ops.sw | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 8185be0e6ae..58f046b21b5 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -261,7 +261,32 @@ impl Subtract for u16 { impl Subtract for u8 { fn subtract(self, other: Self) -> Self { - __sub(self, other) + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __sub(self_u64, other_u64); + let max_u8_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u8_u64) { + if panic_on_overflow_is_enabled() { + __revert(0) + } else { + // overflow enabled + // res % (Self::max() + 1) + let res_u64 = __mod(res_u64, __add(max_u8_u64, 1)); + asm(input: res_u64) { + input: u8 + } + } + } else { + asm(input: res_u64) { + input: u8 + } + } } } From 204bf2793a3c82a85865aa10e796e987004d3179 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:37:39 +1100 Subject: [PATCH 09/12] fix: u32 mul impl avoid UB --- sway-lib-core/src/ops.sw | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 58f046b21b5..0828a4e551a 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -343,21 +343,31 @@ impl Multiply for u64 { // Emulate overflowing arithmetic for non-64-bit integer types impl Multiply for u32 { fn multiply(self, other: Self) -> Self { - // any non-64-bit value is compiled to a u64 value under-the-hood - // constants (like Self::max() below) are also automatically promoted to u64 - let res = __mul(self, other); - if __gt(res, Self::max()) { + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __mul(self_u64, other_u64); + let max_u32_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u32_u64) { if panic_on_overflow_is_enabled() { - // integer overflow __revert(0) } else { // overflow enabled // res % (Self::max() + 1) - __mod(res, __add(Self::max(), 1)) + let res_u64 = __mod(res_u64, __add(max_u32_u64, 1)); + asm(input: res_u64) { + input: u32 + } } } else { - // no overflow - res + asm(input: res_u64) { + input: u32 + } } } } From 18a71d3e595dbdf4c72e53563ca3e17484c769d2 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:38:03 +1100 Subject: [PATCH 10/12] fix: u16 mul impl avoid UB --- sway-lib-core/src/ops.sw | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 0828a4e551a..b09b8b359ca 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -374,15 +374,31 @@ impl Multiply for u32 { impl Multiply for u16 { fn multiply(self, other: Self) -> Self { - let res = __mul(self, other); - if __gt(res, Self::max()) { + let self_u64 = asm(input: self) { + input: u64 + }; + let other_u64 = asm(input: other) { + input: u64 + }; + let res_u64 = __mul(self_u64, other_u64); + let max_u16_u64 = asm(input: Self::max()) { + input: u64 + }; + if __gt(res_u64, max_u16_u64) { if panic_on_overflow_is_enabled() { __revert(0) } else { - __mod(res, __add(Self::max(), 1)) + // overflow enabled + // res % (Self::max() + 1) + let res_u64 = __mod(res_u64, __add(max_u16_u64, 1)); + asm(input: res_u64) { + input: u16 + } } } else { - res + asm(input: res_u64) { + input: u16 + } } } } From ab7ac1eba48c27b2ddfb41cf9e25bdea518a7b15 Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:44:49 +1100 Subject: [PATCH 11/12] chore: math_u8_underflow_sub test --- .../math_inline_tests/src/main.sw | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw b/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw index b5c407aa8dd..8df9aabe54a 100644 --- a/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw +++ b/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw @@ -938,6 +938,26 @@ fn math_u8_overflow_add() { require(e == u8::max() - 2, e); } +#[test] +fn math_u8_underflow_sub() { + assert((u8::max() - u8::max()) == 0u8); + assert((u8::min() - u8::min()) == 0u8); + assert((10u8 - 5u8) == 5u8); + + let _ = disable_panic_on_overflow(); + + let a = 0u8; + let b = 1u8; + + let c = a - b; + assert(c == u8::max()); + + let d = u8::max(); + + let e = a - d; + assert(e == b); +} + #[test] fn math_u16_overflow_add() { let _ = disable_panic_on_overflow(); From 2ca957184e0ea0a3465f0e75118ead667ebe9c5e Mon Sep 17 00:00:00 2001 From: K1-R1 Date: Wed, 23 Oct 2024 18:46:25 +1100 Subject: [PATCH 12/12] chore: forc fmt --- .../test_programs/math_inline_tests/src/main.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw b/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw index 8df9aabe54a..de0c4841544 100644 --- a/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw +++ b/test/src/in_language_tests/test_programs/math_inline_tests/src/main.sw @@ -943,7 +943,7 @@ fn math_u8_underflow_sub() { assert((u8::max() - u8::max()) == 0u8); assert((u8::min() - u8::min()) == 0u8); assert((10u8 - 5u8) == 5u8); - + let _ = disable_panic_on_overflow(); let a = 0u8; @@ -985,7 +985,7 @@ fn math_u16_underflow_sub() { assert((u16::max() - u16::max()) == 0u16); assert((u16::min() - u16::min()) == 0u16); assert((10u16 - 5u16) == 5u16); - + let _ = disable_panic_on_overflow(); let a = 0u16;