Browse Source

Multiplication overflow detection for integer width > 64 bits (#988)

* mul overflow > 64 bits

Signed-off-by: salaheldinsoliman <salaheldin_sameh@aucegypt.edu>
salaheldinsoliman 3 years ago
parent
commit
a78b61adc2

+ 4 - 2
Cargo.toml

@@ -16,7 +16,8 @@ cc = "1.0"
 
 
 [dependencies]
 [dependencies]
 regex = "1"
 regex = "1"
-num-bigint = "0.4"
+rand = "0.8"
+num-bigint = { version = "0.4", features = ["rand"]}
 num-traits = "0.2"
 num-traits = "0.2"
 num-integer = "0.1.44"
 num-integer = "0.1.44"
 parity-wasm = "0.45"
 parity-wasm = "0.45"
@@ -56,7 +57,8 @@ num-derive = "0.3"
 parity-scale-codec = "3.1"
 parity-scale-codec = "3.1"
 ethabi = "17.0"
 ethabi = "17.0"
 wasmi = "0.11"
 wasmi = "0.11"
-rand = "0.7"
+# rand version 0.7 is needed for ed25519_dalek::keypair::generate, used in solana_tests/signature_verify.rs
+rand_07 = { package = "rand", version = "0.7" }
 sha2 = "0.10"
 sha2 = "0.10"
 # solana_rbpf makes api changes in patch versions
 # solana_rbpf makes api changes in patch versions
 solana_rbpf = "=0.2.32"
 solana_rbpf = "=0.2.32"

+ 447 - 54
src/emit/mod.rs

@@ -1827,14 +1827,52 @@ pub trait TargetRuntime<'a> {
                 let right = self.expression(bin, r, vartab, function, ns);
                 let right = self.expression(bin, r, vartab, function, ns);
 
 
                 let bits = left.into_int_value().get_type().get_bit_width();
                 let bits = left.into_int_value().get_type().get_bit_width();
+                let o = bin.build_alloca(function, left.get_type(), "");
+                let f = self.power(bin, *unchecked, bits, res_ty.is_signed_int(), o);
 
 
-                let f = self.power(bin, *unchecked, bits, res_ty.is_signed_int());
-
-                bin.builder
-                    .build_call(f, &[left.into(), right.into()], "power")
+                // If the function returns zero, then the operation was successful.
+                let error_return = bin
+                    .builder
+                    .build_call(f, &[left.into(), right.into(), o.into()], "power")
                     .try_as_basic_value()
                     .try_as_basic_value()
                     .left()
                     .left()
-                    .unwrap()
+                    .unwrap();
+
+                // Load the result pointer
+                let res = bin.builder.build_load(o, "");
+
+                if !bin.math_overflow_check || *unchecked || ns.target != Target::Solana {
+                    // In Substrate, overflow case will hit an unreachable expression, so no additional checks are needed.
+                    res
+                } else {
+                    // In Solana, a return other than zero will abort execution. We need to check if power() returned a zero or not.
+                    let error_block = bin.context.append_basic_block(function, "error");
+                    let return_block = bin.context.append_basic_block(function, "return_block");
+
+                    let error_ret = bin.builder.build_int_compare(
+                        IntPredicate::NE,
+                        error_return.into_int_value(),
+                        error_return.get_type().const_zero().into_int_value(),
+                        "",
+                    );
+
+                    bin.builder
+                        .build_conditional_branch(error_ret, error_block, return_block);
+                    bin.builder.position_at_end(error_block);
+
+                    self.assert_failure(
+                        bin,
+                        bin.context
+                            .i8_type()
+                            .ptr_type(AddressSpace::Generic)
+                            .const_null(),
+                        bin.context.i32_type().const_zero(),
+                    );
+
+                    bin.builder.position_at_end(return_block);
+
+                    res
+                }
             }
             }
             Expression::Equal(_, l, r) => {
             Expression::Equal(_, l, r) => {
                 if l.ty().is_address() {
                 if l.ty().is_address() {
@@ -5549,7 +5587,277 @@ pub trait TargetRuntime<'a> {
         vector.into()
         vector.into()
     }
     }
 
 
-    // emit a multiply for any width with or without overflow checking
+    // Signed overflow detection is handled by the following steps:
+    // 1- Do an unsigned multiplication first, This step will check if the generated value will fit in N bits. (unsigned overflow)
+    // 2- Get the result, and negate it if needed.
+    // 3- Check for signed overflow, by checking for an unexpected change in the sign of the result.
+    fn signed_ovf_detect(
+        &self,
+        bin: &Binary<'a>,
+        mul_ty: IntType<'a>,
+        mul_bits: u32,
+        left: IntValue<'a>,
+        right: IntValue<'a>,
+        bits: u32,
+        function: FunctionValue<'a>,
+    ) -> IntValue<'a> {
+        // We check for signed overflow based on the facts:
+        //  - * - = +
+        //  + * + = +
+        //  - * + = - (if op1 and op2 != 0)
+        // if one of the operands is zero, discard the last rule.
+        let left_negative = bin.builder.build_int_compare(
+            IntPredicate::SLT,
+            left,
+            left.get_type().const_zero(),
+            "left_negative",
+        );
+
+        let left_abs = bin
+            .builder
+            .build_select(
+                left_negative,
+                bin.builder.build_int_neg(left, "signed_left"),
+                left,
+                "left_abs",
+            )
+            .into_int_value();
+
+        let right_negative = bin.builder.build_int_compare(
+            IntPredicate::SLT,
+            right,
+            right.get_type().const_zero(),
+            "right_negative",
+        );
+
+        let right_abs = bin
+            .builder
+            .build_select(
+                right_negative,
+                bin.builder.build_int_neg(right, "signed_right"),
+                right,
+                "right_abs",
+            )
+            .into_int_value();
+
+        let l = bin.build_alloca(function, mul_ty, "");
+        let r = bin.build_alloca(function, mul_ty, "");
+        let o = bin.build_alloca(function, mul_ty, "");
+
+        bin.builder
+            .build_store(l, bin.builder.build_int_z_extend(left_abs, mul_ty, ""));
+        bin.builder
+            .build_store(r, bin.builder.build_int_z_extend(right_abs, mul_ty, ""));
+
+        let return_val = bin.builder.build_call(
+            bin.module.get_function("__mul32_with_builtin_ovf").unwrap(),
+            &[
+                bin.builder
+                    .build_pointer_cast(
+                        l,
+                        bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                        "left",
+                    )
+                    .into(),
+                bin.builder
+                    .build_pointer_cast(
+                        r,
+                        bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                        "right",
+                    )
+                    .into(),
+                bin.builder
+                    .build_pointer_cast(
+                        o,
+                        bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                        "output",
+                    )
+                    .into(),
+                bin.context
+                    .i32_type()
+                    .const_int(mul_bits as u64 / 32, false)
+                    .into(),
+            ],
+            "",
+        );
+
+        let res = bin.builder.build_load(o, "mul");
+        let ovf_any_type = if mul_bits != bits {
+            // If there are any set bits, then there is an overflow.
+            let check_ovf = bin.builder.build_right_shift(
+                res.into_int_value(),
+                mul_ty.const_int((bits).into(), false),
+                false,
+                "",
+            );
+            bin.builder.build_int_compare(
+                IntPredicate::NE,
+                check_ovf,
+                check_ovf.get_type().const_zero(),
+                "",
+            )
+        } else {
+            // If no size extension took place, there is no overflow in most significant N bits
+            bin.context.bool_type().const_zero()
+        };
+
+        let negate_result = bin
+            .builder
+            .build_xor(left_negative, right_negative, "negate_result");
+
+        let res = bin.builder.build_select(
+            negate_result,
+            bin.builder
+                .build_int_neg(res.into_int_value(), "unsigned_res"),
+            res.into_int_value(),
+            "res",
+        );
+
+        let error_block = bin.context.append_basic_block(function, "error");
+        let return_block = bin.context.append_basic_block(function, "return_block");
+
+        // Extract sign bit of the operands and the result
+        let left_sign_bit = self.extract_sign_bit(bin, left, left.get_type());
+        let right_sign_bit = self.extract_sign_bit(bin, right, right.get_type());
+        let res_sign_bit = if mul_bits == bits {
+            // If no extension took place, get the leftmost bit(sign bit).
+            self.extract_sign_bit(bin, res.into_int_value(), res.into_int_value().get_type())
+        } else {
+            // If extension took place, truncate the result to the type of the operands then extract the leftmost bit(sign bit).
+            self.extract_sign_bit(
+                bin,
+                bin.builder
+                    .build_int_truncate(res.into_int_value(), left.get_type(), ""),
+                left.get_type(),
+            )
+        };
+
+        let value_fits_n_bits = bin.builder.build_not(
+            bin.builder.build_or(
+                return_val
+                    .try_as_basic_value()
+                    .left()
+                    .unwrap()
+                    .into_int_value(),
+                ovf_any_type,
+                "",
+            ),
+            "",
+        );
+
+        let left_is_zero =
+            bin.builder
+                .build_int_compare(IntPredicate::EQ, left, left.get_type().const_zero(), "");
+        let right_is_zero = bin.builder.build_int_compare(
+            IntPredicate::EQ,
+            right,
+            right.get_type().const_zero(),
+            "",
+        );
+
+        // If one of the operands is zero
+        let mul_by_zero = bin.builder.build_or(left_is_zero, right_is_zero, "");
+
+        // Will resolve to one if signs are differnet
+        let different_signs = bin.builder.build_xor(left_sign_bit, right_sign_bit, "");
+
+        let not_ok_operation = bin
+            .builder
+            .build_not(bin.builder.build_xor(different_signs, res_sign_bit, ""), "");
+
+        // Here, we disregard the last rule mentioned above if there is a multiplication by zero.
+        bin.builder.build_conditional_branch(
+            bin.builder.build_and(
+                bin.builder.build_or(not_ok_operation, mul_by_zero, ""),
+                value_fits_n_bits,
+                "",
+            ),
+            return_block,
+            error_block,
+        );
+
+        bin.builder.position_at_end(error_block);
+
+        self.assert_failure(
+            bin,
+            bin.context
+                .i8_type()
+                .ptr_type(AddressSpace::Generic)
+                .const_null(),
+            bin.context.i32_type().const_zero(),
+        );
+
+        bin.builder.position_at_end(return_block);
+
+        bin.builder
+            .build_int_truncate(res.into_int_value(), left.get_type(), "")
+    }
+
+    // Call void __mul32 and return the result.
+    fn call_mul32_without_ovf(
+        &self,
+        bin: &Binary<'a>,
+        l: PointerValue<'a>,
+        r: PointerValue<'a>,
+        o: PointerValue<'a>,
+        mul_bits: u32,
+        mul_type: IntType<'a>,
+    ) -> IntValue<'a> {
+        bin.builder.build_call(
+            bin.module.get_function("__mul32").unwrap(),
+            &[
+                bin.builder
+                    .build_pointer_cast(
+                        l,
+                        bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                        "left",
+                    )
+                    .into(),
+                bin.builder
+                    .build_pointer_cast(
+                        r,
+                        bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                        "right",
+                    )
+                    .into(),
+                bin.builder
+                    .build_pointer_cast(
+                        o,
+                        bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                        "output",
+                    )
+                    .into(),
+                bin.context
+                    .i32_type()
+                    .const_int(mul_bits as u64 / 32, false)
+                    .into(),
+            ],
+            "",
+        );
+
+        let res = bin.builder.build_load(o, "mul");
+
+        bin.builder
+            .build_int_truncate(res.into_int_value(), mul_type, "")
+    }
+
+    // Utility function to extract the sign bit of an IntValue
+    fn extract_sign_bit(
+        &self,
+        bin: &Binary<'a>,
+        operand: IntValue<'a>,
+        int_type: IntType<'a>,
+    ) -> IntValue<'a> {
+        let n_bits_to_shift = int_type.get_bit_width() - 1;
+        let val_to_shift = int_type.const_int(n_bits_to_shift as u64, false);
+        let shifted = bin
+            .builder
+            .build_right_shift(operand, val_to_shift, false, "");
+        bin.builder
+            .build_int_truncate(shifted, bin.context.bool_type(), "")
+    }
+
+    // Emit a multiply for any width with or without overflow checking
     fn mul(
     fn mul(
         &self,
         &self,
         bin: &Binary<'a>,
         bin: &Binary<'a>,
@@ -5561,12 +5869,13 @@ pub trait TargetRuntime<'a> {
     ) -> IntValue<'a> {
     ) -> IntValue<'a> {
         let bits = left.get_type().get_bit_width();
         let bits = left.get_type().get_bit_width();
 
 
-        if bits > 64 {
-            // round up the number of bits to the next 32
+        // Mul with overflow is not supported beyond this bit range, so we implement our own function
+        if bits > 32 {
+            // Round up the number of bits to the next 32
             let mul_bits = (bits + 31) & !31;
             let mul_bits = (bits + 31) & !31;
             let mul_ty = bin.context.custom_width_int_type(mul_bits);
             let mul_ty = bin.context.custom_width_int_type(mul_bits);
 
 
-            // round up bits
+            // Round up bits
             let l = bin.build_alloca(function, mul_ty, "");
             let l = bin.build_alloca(function, mul_ty, "");
             let r = bin.build_alloca(function, mul_ty, "");
             let r = bin.build_alloca(function, mul_ty, "");
             let o = bin.build_alloca(function, mul_ty, "");
             let o = bin.build_alloca(function, mul_ty, "");
@@ -5574,57 +5883,127 @@ pub trait TargetRuntime<'a> {
             if mul_bits == bits {
             if mul_bits == bits {
                 bin.builder.build_store(l, left);
                 bin.builder.build_store(l, left);
                 bin.builder.build_store(r, right);
                 bin.builder.build_store(r, right);
-            } else if signed {
-                bin.builder
-                    .build_store(l, bin.builder.build_int_s_extend(left, mul_ty, ""));
-                bin.builder
-                    .build_store(r, bin.builder.build_int_s_extend(right, mul_ty, ""));
-            } else {
+            }
+            // LLVM-IR can handle multiplication of sizes up to 64 bits. If the size is larger, we need to implement our own mutliplication function.
+            // We divide the operands into sizes of 32 bits (check __mul32 in stdlib/bigint.c documentation).
+            // If the size is not divisble by 32, we extend it to the next 32 bits. For example, int72 will be extended to int96.
+            // Here, we zext the operands to the nearest 32 bits. zext is called instead of sext because we need to do unsigned multiplication by default.
+            // It will not matter in terms of mul without overflow, because we always truncate the result to the bit size of the operands.
+            // In mul with overflow however, it is needed so that overflow can be detected if the most significant bits of the result are not zeros.
+            else {
                 bin.builder
                 bin.builder
                     .build_store(l, bin.builder.build_int_z_extend(left, mul_ty, ""));
                     .build_store(l, bin.builder.build_int_z_extend(left, mul_ty, ""));
                 bin.builder
                 bin.builder
                     .build_store(r, bin.builder.build_int_z_extend(right, mul_ty, ""));
                     .build_store(r, bin.builder.build_int_z_extend(right, mul_ty, ""));
             }
             }
 
 
-            bin.builder.build_call(
-                bin.module.get_function("__mul32").unwrap(),
-                &[
-                    bin.builder
-                        .build_pointer_cast(
-                            l,
-                            bin.context.i32_type().ptr_type(AddressSpace::Generic),
-                            "left",
-                        )
-                        .into(),
-                    bin.builder
-                        .build_pointer_cast(
-                            r,
-                            bin.context.i32_type().ptr_type(AddressSpace::Generic),
-                            "right",
-                        )
-                        .into(),
-                    bin.builder
-                        .build_pointer_cast(
-                            o,
-                            bin.context.i32_type().ptr_type(AddressSpace::Generic),
-                            "output",
-                        )
-                        .into(),
+            if bin.math_overflow_check && !unchecked {
+                if signed {
+                    return self
+                        .signed_ovf_detect(bin, mul_ty, mul_bits, left, right, bits, function);
+                }
+
+                // Unsigned overflow detection Approach:
+                // If the size is a multiple of 32, we call __mul32_with_builtin_ovf and it returns an overflow flag (check __mul32_with_builtin_ovf in stdlib/bigint.c documentation)
+                // If that is not the case, some extra work has to be done. We have to check the extended bits for any set bits. If there is any, an overflow occured.
+                // For example, if we have uint72, it will be extended to uint96. __mul32 with ovf will raise an ovf flag if the result overflows 96 bits, not 72.
+                // We account for that by checking the extended leftmost bits. In the example mentioned, they will be 96-72=24 bits.
+                let return_val = bin.builder.build_call(
+                    bin.module.get_function("__mul32_with_builtin_ovf").unwrap(),
+                    &[
+                        bin.builder
+                            .build_pointer_cast(
+                                l,
+                                bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                                "left",
+                            )
+                            .into(),
+                        bin.builder
+                            .build_pointer_cast(
+                                r,
+                                bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                                "right",
+                            )
+                            .into(),
+                        bin.builder
+                            .build_pointer_cast(
+                                o,
+                                bin.context.i32_type().ptr_type(AddressSpace::Generic),
+                                "output",
+                            )
+                            .into(),
+                        bin.context
+                            .i32_type()
+                            .const_int(mul_bits as u64 / 32, false)
+                            .into(),
+                    ],
+                    "ovf",
+                );
+
+                let res = bin.builder.build_load(o, "mul");
+
+                let error_block = bin.context.append_basic_block(function, "error");
+                let return_block = bin.context.append_basic_block(function, "return_block");
+
+                // If the operands were extended to nearest 32 bit size, check the most significant N bits, where N equals bit width after extension minus original bit width.
+                let ovf_any_type = if mul_bits != bits {
+                    // If there are any set bits, then there is an overflow.
+                    let check_ovf = bin.builder.build_right_shift(
+                        res.into_int_value(),
+                        mul_ty.const_int((bits).into(), false),
+                        false,
+                        "",
+                    );
+                    bin.builder.build_int_compare(
+                        IntPredicate::NE,
+                        check_ovf,
+                        check_ovf.get_type().const_zero(),
+                        "",
+                    )
+                } else {
+                    // If no size extension took place, there is no overflow in most significant N bits
+                    bin.context.bool_type().const_zero()
+                };
+
+                // Until this point, we only checked the extended bits for ovf. But mul ovf can take place any where from bit size to double bit size.
+                // For example: If we have uint72, it will be extended to uint96. We only checked the most significant 24 bits for overflow, which can happen up to 72*2=144 bits.
+                // bool __mul32_with_builtin_ovf takes care of overflowing bits beyond 96.
+                // What is left now is to or these two ovf flags, and check if any one of them is set. If so, an overflow occured.
+                let lowbit = bin.builder.build_int_truncate(
+                    bin.builder.build_or(
+                        ovf_any_type,
+                        return_val
+                            .try_as_basic_value()
+                            .left()
+                            .unwrap()
+                            .into_int_value(),
+                        "",
+                    ),
+                    bin.context.bool_type(),
+                    "bit",
+                );
+
+                // If ovf, raise an error, else return the result.
+                bin.builder
+                    .build_conditional_branch(lowbit, error_block, return_block);
+
+                bin.builder.position_at_end(error_block);
+
+                self.assert_failure(
+                    bin,
                     bin.context
                     bin.context
-                        .i32_type()
-                        .const_int(mul_bits as u64 / 32, false)
-                        .into(),
-                ],
-                "",
-            );
+                        .i8_type()
+                        .ptr_type(AddressSpace::Generic)
+                        .const_null(),
+                    bin.context.i32_type().const_zero(),
+                );
 
 
-            let res = bin.builder.build_load(o, "mul");
+                bin.builder.position_at_end(return_block);
 
 
-            if mul_bits == bits {
-                res.into_int_value()
-            } else {
                 bin.builder
                 bin.builder
                     .build_int_truncate(res.into_int_value(), left.get_type(), "")
                     .build_int_truncate(res.into_int_value(), left.get_type(), "")
+            } else {
+                return self.call_mul32_without_ovf(bin, l, r, o, mul_bits, left.get_type());
             }
             }
         } else if bin.math_overflow_check && !unchecked {
         } else if bin.math_overflow_check && !unchecked {
             self.build_binary_op_with_overflow_check(
             self.build_binary_op_with_overflow_check(
@@ -5646,6 +6025,7 @@ pub trait TargetRuntime<'a> {
         unchecked: bool,
         unchecked: bool,
         bits: u32,
         bits: u32,
         signed: bool,
         signed: bool,
+        o: PointerValue<'a>,
     ) -> FunctionValue<'a> {
     ) -> FunctionValue<'a> {
         /*
         /*
             int ipow(int base, int exp)
             int ipow(int base, int exp)
@@ -5660,7 +6040,6 @@ pub trait TargetRuntime<'a> {
                         break;
                         break;
                     base *= base;
                     base *= base;
                 }
                 }
-
                 return result;
                 return result;
             }
             }
         */
         */
@@ -5679,9 +6058,13 @@ pub trait TargetRuntime<'a> {
         let pos = bin.builder.get_insert_block().unwrap();
         let pos = bin.builder.get_insert_block().unwrap();
 
 
         // __upower(base, exp)
         // __upower(base, exp)
-        let function =
-            bin.module
-                .add_function(&name, ty.fn_type(&[ty.into(), ty.into()], false), None);
+        let function = bin.module.add_function(
+            &name,
+            bin.context
+                .i64_type()
+                .fn_type(&[ty.into(), ty.into(), o.get_type().into()], false),
+            None,
+        );
 
 
         let entry = bin.context.append_basic_block(function, "entry");
         let entry = bin.context.append_basic_block(function, "entry");
         let loop_block = bin.context.append_basic_block(function, "loop");
         let loop_block = bin.context.append_basic_block(function, "loop");
@@ -5724,6 +6107,8 @@ pub trait TargetRuntime<'a> {
             signed,
             signed,
         );
         );
 
 
+        let multiply = bin.builder.get_insert_block().unwrap();
+
         bin.builder.build_unconditional_branch(nomultiply);
         bin.builder.build_unconditional_branch(nomultiply);
         bin.builder.position_at_end(nomultiply);
         bin.builder.position_at_end(nomultiply);
 
 
@@ -5743,7 +6128,13 @@ pub trait TargetRuntime<'a> {
         bin.builder.build_conditional_branch(zero, done, notdone);
         bin.builder.build_conditional_branch(zero, done, notdone);
         bin.builder.position_at_end(done);
         bin.builder.position_at_end(done);
 
 
-        bin.builder.build_return(Some(&result3.as_basic_value()));
+        // If successful operation, load the result in the output pointer then return zero.
+        bin.builder.build_store(
+            function.get_nth_param(2).unwrap().into_pointer_value(),
+            result3.as_basic_value(),
+        );
+        bin.builder
+            .build_return(Some(&bin.context.i64_type().const_zero()));
 
 
         bin.builder.position_at_end(notdone);
         bin.builder.position_at_end(notdone);
 
 
@@ -5756,6 +6147,8 @@ pub trait TargetRuntime<'a> {
             signed,
             signed,
         );
         );
 
 
+        let notdone = bin.builder.get_insert_block().unwrap();
+
         base.add_incoming(&[(&base2, notdone)]);
         base.add_incoming(&[(&base2, notdone)]);
         result.add_incoming(&[(&result3.as_basic_value(), notdone)]);
         result.add_incoming(&[(&result3.as_basic_value(), notdone)]);
         exp.add_incoming(&[(&exp2, notdone)]);
         exp.add_incoming(&[(&exp2, notdone)]);

+ 64 - 10
stdlib/bigint.c

@@ -3,20 +3,20 @@
 #include <stdbool.h>
 #include <stdbool.h>
 
 
 /*
 /*
-	In wasm/bpf, the instruction for multiplying two 64 bit values results in a 64 bit value. In
+    In wasm/bpf, the instruction for multiplying two 64 bit values results in a 64 bit value. In
     other words, the result is truncated. The largest values we can multiply without truncation
     other words, the result is truncated. The largest values we can multiply without truncation
-	is 32 bit (by casting to 64 bit and doing a 64 bit multiplication). So, we divvy the work
-	up into a 32 bit multiplications.
+    is 32 bit (by casting to 64 bit and doing a 64 bit multiplication). So, we divvy the work
+    up into a 32 bit multiplications.
 
 
-	No overflow checking is done.
+    No overflow checking is done.
 
 
-	0		0		0		r5		r4		r3		r2		r1
-	0		0		0		0		l4		l3		l2		l1 *
+    0		0		0		r5		r4		r3		r2		r1
+    0		0		0		0		l4		l3		l2		l1 *
     ------------------------------------------------------------
     ------------------------------------------------------------
-	0		0		0		r5*l1	r4*l1	r3*l1	r2*l1	r1*l1
-	0		0		r5*l2	r4*l2	r3*l2	r2*l2 	r1*l2	0
-	0		r5*l3	r4*l3	r3*l3	r2*l3 	r1*l3	0		0
-	r5*l4	r4*l4	r3*l4	r2*l4 	r1*l4	0		0 		0  +
+    0		0		0		r5*l1	r4*l1	r3*l1	r2*l1	r1*l1
+    0		0		r5*l2	r4*l2	r3*l2	r2*l2 	r1*l2	0
+    0		r5*l3	r4*l3	r3*l3	r2*l3 	r1*l3	0		0
+    r5*l4	r4*l4	r3*l4	r2*l4 	r1*l4	0		0 		0  +
     ------------------------------------------------------------
     ------------------------------------------------------------
 */
 */
 void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
 void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
@@ -62,6 +62,60 @@ void __mul32(uint32_t left[], uint32_t right[], uint32_t out[], int len)
     }
     }
 }
 }
 
 
+// A version of __mul32 that detects overflow. 
+bool __mul32_with_builtin_ovf(uint32_t left[], uint32_t right[], uint32_t out[], int len)
+{
+    bool overflow = false;
+    uint64_t val1 = 0, carry = 0;
+    int left_len = len, right_len = len;
+    while (left_len > 0 && !left[left_len - 1])
+        left_len--;
+    while (right_len > 0 && !right[right_len - 1])
+        right_len--;
+    int right_start = 0, right_end = 0;
+    int left_start = 0;
+    // We extend len to check for possible overflow. len = bit_width / 32. Checking for overflow for intN (where N = number of bits) requires checking for any set bits beyond N up to N*2.
+    len = len * 2;
+    for (int l = 0; l < len; l++)
+    {
+        int i = 0;
+        if (l >= left_len)
+            right_start++;
+        if (l >= right_len)
+            left_start++;
+        if (right_end < right_len)
+            right_end++;
+
+        for (int r = right_end - 1; r >= right_start; r--)
+        {
+            uint64_t m = (uint64_t)left[left_start + i] * (uint64_t)right[r];
+            i++;
+            if (__builtin_add_overflow(val1, m, &val1))
+                carry += 0x100000000;
+        }
+
+        // If the loop is within the operand bit size, just do the assignment
+        if (l < len / 2)
+        {
+            out[l] = val1;
+        }
+
+        // If the loop extends to more than the bit size, we check for overflow.
+        else if (l >= len / 2)
+        {
+            if (val1 > 0)
+            {
+                overflow = true;
+                break;
+            }
+        }
+
+        val1 = (val1 >> 32) | carry;
+        carry = 0;
+    }
+    return overflow;
+}
+
 // Some compiler runtime builtins we need.
 // Some compiler runtime builtins we need.
 
 
 // 128 bit shift left.
 // 128 bit shift left.

BIN
stdlib/bpf/bigint.bc


BIN
stdlib/wasm/bigint.bc


+ 5 - 1
tests/solana.rs

@@ -116,6 +116,10 @@ struct Assign {
 }
 }
 
 
 fn build_solidity(src: &str) -> VirtualMachine {
 fn build_solidity(src: &str) -> VirtualMachine {
+    build_solidity_with_overflow_check(src, false)
+}
+
+fn build_solidity_with_overflow_check(src: &str, math_overflow_flag: bool) -> VirtualMachine {
     let mut cache = FileResolver::new();
     let mut cache = FileResolver::new();
 
 
     cache.set_file_contents("test.sol", src.to_string());
     cache.set_file_contents("test.sol", src.to_string());
@@ -135,7 +139,7 @@ fn build_solidity(src: &str) -> VirtualMachine {
         namespaces,
         namespaces,
         "bundle.sol",
         "bundle.sol",
         inkwell::OptimizationLevel::Default,
         inkwell::OptimizationLevel::Default,
-        false,
+        math_overflow_flag,
         false,
         false,
     );
     );
 
 

+ 364 - 1
tests/solana_tests/primitives.rs

@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::build_solidity;
 use crate::build_solidity;
+use crate::build_solidity_with_overflow_check;
 use ethabi::ethereum_types::U256;
 use ethabi::ethereum_types::U256;
-use num_bigint::{BigInt, BigUint};
+use num_bigint::{BigInt, BigUint, RandBigInt, Sign};
+use rand::seq::SliceRandom;
 use rand::Rng;
 use rand::Rng;
 use std::ops::Add;
 use std::ops::Add;
 use std::ops::BitAnd;
 use std::ops::BitAnd;
@@ -349,7 +351,9 @@ fn uint() {
             }
             }
 
 
             function mul(uintN a, uintN b) public returns (uintN) {
             function mul(uintN a, uintN b) public returns (uintN) {
+                unchecked {
                 return a * b;
                 return a * b;
+                }
             }
             }
 
 
             function div(uintN a, uintN b) public returns (uintN) {
             function div(uintN a, uintN b) public returns (uintN) {
@@ -361,7 +365,9 @@ fn uint() {
             }
             }
 
 
             function pow(uintN a, uintN b) public returns (uintN) {
             function pow(uintN a, uintN b) public returns (uintN) {
+                unchecked {
                 return a ** b;
                 return a ** b;
+                }
             }
             }
 
 
             function or(uintN a, uintN b) public returns (uintN) {
             function or(uintN a, uintN b) public returns (uintN) {
@@ -599,6 +605,361 @@ fn truncate_uint(n: &mut U256, width: usize) {
     }
     }
 }
 }
 
 
+#[test]
+fn test_power_overflow_boundaries() {
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function pow(uintN a, uintN b) public returns (uintN) { 
+                return a ** b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+        contract.constructor("test", &[]);
+
+        let return_value = contract.function(
+            "pow",
+            &[
+                ethabi::Token::Uint(U256::from(2)),
+                ethabi::Token::Uint(U256::from(width - 1)),
+            ],
+            &[],
+            None,
+        );
+
+        let res = BigUint::from(2_u32).pow((width - 1) as u32);
+
+        assert_eq!(
+            return_value,
+            vec![ethabi::Token::Uint(U256::from_big_endian(
+                &res.to_bytes_be()
+            ))]
+        );
+
+        let sesa = contract.function_must_fail(
+            "pow",
+            &[
+                ethabi::Token::Uint(U256::from(2)),
+                ethabi::Token::Uint(U256::from(width + 1)),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(sesa, Ok(0));
+    }
+}
+
+#[test]
+fn test_overflow_boundaries() {
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1]. We generate these boundaries:
+        let upper_boundary = BigInt::from(2_u32).pow(width - 1).sub(1);
+        let lower_boundary = BigInt::from(2_u32).pow(width - 1).mul(-1);
+        let second_op = BigInt::from(1_u32);
+
+        // Multiply the boundaries by 1.
+        contract.constructor("test", &[]);
+        let return_value = contract.function(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&upper_boundary, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&second_op, width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+        assert_eq!(
+            return_value,
+            vec![ethabi::Token::Int(bigint_to_eth(
+                &upper_boundary,
+                width.try_into().unwrap(),
+            ))]
+        );
+
+        let return_value = contract.function(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&lower_boundary, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&second_op, width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+        assert_eq!(
+            return_value,
+            vec![ethabi::Token::Int(bigint_to_eth(
+                &lower_boundary,
+                width.try_into().unwrap(),
+            ))]
+        );
+
+        let upper_boundary_plus_one = BigInt::from(2_u32).pow(width - 1);
+
+        // We subtract 2 instead of one to make the number even, so that no rounding occurs when we divide by 2 later on.
+        let lower_boundary_minus_two = BigInt::from(2_u32).pow(width - 1).mul(-1_i32).sub(2_i32);
+
+        let upper_second_op = upper_boundary_plus_one.div(2);
+
+        let lower_second_op = lower_boundary_minus_two.div(2);
+
+        let res = contract.function_must_fail(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&upper_second_op, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&BigInt::from(2), width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(res, Ok(0));
+
+        let res = contract.function_must_fail(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&lower_second_op, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&BigInt::from(2), width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(res, Ok(0));
+
+        let res = contract.function_must_fail(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&upper_boundary, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&upper_boundary, width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(res, Ok(0));
+
+        let res = contract.function_must_fail(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&lower_boundary, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&lower_boundary, width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(res, Ok(0));
+
+        let res = contract.function_must_fail(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(&upper_boundary, width.try_into().unwrap())),
+                ethabi::Token::Int(bigint_to_eth(&lower_boundary, width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(res, Ok(0));
+    }
+}
+
+#[test]
+fn test_mul_within_range_signed() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let mut contract = build_solidity(&src);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1]. Here we generate a random number within this range and multiply it by -1, 1 or 0.
+        let first_operand_rand = rng.gen_bigint(width - 1).sub(1_u32);
+        println!("First op : {:?}", first_operand_rand);
+
+        let side = vec![-1, 0, 1];
+        // -1, 1 or 0
+        let second_op = BigInt::from(*side.choose(&mut rng).unwrap() as i32);
+        println!("second op : {:?}", second_op);
+
+        contract.constructor("test", &[]);
+        let return_value = contract.function(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(
+                    &first_operand_rand,
+                    width.try_into().unwrap(),
+                )),
+                ethabi::Token::Int(bigint_to_eth(&second_op, width.try_into().unwrap())),
+            ],
+            &[],
+            None,
+        );
+
+        let res = first_operand_rand.mul(second_op);
+        assert_eq!(
+            return_value,
+            vec![ethabi::Token::Int(bigint_to_eth(
+                &res,
+                width.try_into().unwrap(),
+            ))]
+        );
+    }
+}
+
+#[test]
+fn test_mul_within_range() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(uintN a, uintN b) public returns (uintN) { 
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+        contract.constructor("test", &[]);
+        for _ in 0..10 {
+            // Max number to fit unsigned N bits is (2^N)-1
+            let limit = BigUint::from(2_u32).pow(width).sub(1_u32);
+
+            // Generate a random number within the the range [0, 2^N -1]
+            let first_operand_rand = rng.gen_biguint(width.into()).add(1_u32);
+
+            // Calculate a number that when multiplied by first_operand_rand, the result will not overflow N bits (the result of this division will cast the float result to int result, therefore lowering it. The result of multiplication will never overflow).
+            let second_operand_rand = limit.div(&first_operand_rand);
+
+            let return_value = contract.function(
+                "mul",
+                &[
+                    ethabi::Token::Uint(U256::from_big_endian(&first_operand_rand.to_bytes_be())),
+                    ethabi::Token::Uint(U256::from_big_endian(&second_operand_rand.to_bytes_be())),
+                ],
+                &[],
+                None,
+            );
+            let res = first_operand_rand * second_operand_rand;
+
+            assert_eq!(
+                return_value,
+                vec![ethabi::Token::Uint(U256::from_big_endian(
+                    &res.to_bytes_be()
+                ))]
+            );
+        }
+    }
+}
+
+#[test]
+fn test_overflow_detect_signed() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        contract.constructor("test", &[]);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1] .Generate a value that will overflow this range:
+        let limit = BigInt::from(2_u32).pow(width - 1).add(1_u32);
+
+        // Divide Limit by a random number
+        let first_operand_rand = rng.gen_bigint((width - 1).into()).sub(1_u32);
+
+        // Calculate a number that when multiplied by first_operand_rand, the result will overflow N bits
+        let mut second_operand_rand = limit / &first_operand_rand;
+
+        if let Sign::Minus = second_operand_rand.sign() {
+            // Decrease by 1 if negative, this is to make sure the result will overflow
+            second_operand_rand = second_operand_rand.sub(1);
+        } else {
+            // Increase by 1 if psotive
+            second_operand_rand = second_operand_rand.add(1);
+        }
+
+        let res = contract.function_must_fail(
+            "mul",
+            &[
+                ethabi::Token::Int(bigint_to_eth(
+                    &first_operand_rand,
+                    width.try_into().unwrap(),
+                )),
+                ethabi::Token::Int(bigint_to_eth(
+                    &second_operand_rand,
+                    width.try_into().unwrap(),
+                )),
+            ],
+            &[],
+            None,
+        );
+
+        assert_ne!(res, Ok(0));
+    }
+}
+
+#[test]
+fn test_overflow_detect_unsigned() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(uintN a, uintN b) public returns (uintN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        contract.constructor("test", &[]);
+
+        for _ in 0..10 {
+            // N bits can hold the range [0, (2^N)-1]. Generate a value that overflows N bits
+            let limit = BigUint::from(2_u32).pow(width);
+
+            // Generate a random number within the the range [0, 2^N -1]
+            let first_operand_rand = rng.gen_biguint(width.into()).add(1_u32);
+
+            // Calculate a number that when multiplied by first_operand_rand, the result will overflow N bits
+            let second_operand_rand = limit.div(&first_operand_rand).add(1_usize);
+
+            let res = contract.function_must_fail(
+                "mul",
+                &[
+                    ethabi::Token::Uint(U256::from_big_endian(&first_operand_rand.to_bytes_be())),
+                    ethabi::Token::Uint(U256::from_big_endian(&second_operand_rand.to_bytes_be())),
+                ],
+                &[],
+                None,
+            );
+            assert_ne!(res, Ok(0));
+        }
+    }
+}
+
 #[test]
 #[test]
 fn int() {
 fn int() {
     let mut rng = rand::thread_rng();
     let mut rng = rand::thread_rng();
@@ -615,7 +976,9 @@ fn int() {
             }
             }
 
 
             function mul(intN a, intN b) public returns (intN) {
             function mul(intN a, intN b) public returns (intN) {
+                unchecked {
                 return a * b;
                 return a * b;
+                }
             }
             }
 
 
             function div(intN a, intN b) public returns (intN) {
             function div(intN a, intN b) public returns (intN) {

+ 1 - 1
tests/solana_tests/signature_verify.rs

@@ -46,7 +46,7 @@ fn verify() {
 
 
     vm.constructor("foo", &[]);
     vm.constructor("foo", &[]);
 
 
-    let mut csprng = rand::thread_rng();
+    let mut csprng = rand_07::thread_rng();
     let keypair: Keypair = Keypair::generate(&mut csprng);
     let keypair: Keypair = Keypair::generate(&mut csprng);
 
 
     let message: &[u8] =
     let message: &[u8] =

+ 2 - 0
tests/solana_tests/simple.rs

@@ -259,8 +259,10 @@ fn two_arrays() {
 
 
             constructor() {
             constructor() {
                 for(uint i = 0; i < 10; i++) {
                 for(uint i = 0; i < 10; i++) {
+                    unchecked {
                     array1.push((i*uint(sha256("i"))));
                     array1.push((i*uint(sha256("i"))));
                     array2.push(((i+1)*uint(sha256("i"))));
                     array2.push(((i+1)*uint(sha256("i"))));
+                    }
                }
                }
             }
             }
         }"#,
         }"#,

+ 4 - 47
tests/substrate.rs

@@ -1195,53 +1195,10 @@ impl MockSubstrate {
     }
     }
 }
 }
 
 
-pub fn build_solidity(src: &'static str) -> MockSubstrate {
-    let mut cache = FileResolver::new();
-
-    cache.set_file_contents("test.sol", src.to_string());
-
-    let (res, ns) = compile(
-        OsStr::new("test.sol"),
-        &mut cache,
-        inkwell::OptimizationLevel::Default,
-        Target::default_substrate(),
-        false,
-    );
-
-    ns.print_diagnostics_in_plain(&cache, false);
-
-    assert!(!ns.diagnostics.any_errors());
-
-    assert!(!res.is_empty());
-
-    let programs: Vec<Program> = res
-        .iter()
-        .map(|res| Program {
-            code: res.0.clone(),
-            abi: abi::substrate::load(&res.1).unwrap(),
-        })
-        .collect();
-
-    let mut accounts = HashMap::new();
-
-    let account = account_new();
-
-    accounts.insert(account, (programs[0].code.clone(), 0));
-
-    let vm = VirtualMachine::new(account, account_new(), 0);
-
-    MockSubstrate {
-        accounts,
-        printbuf: String::new(),
-        store: HashMap::new(),
-        programs,
-        vm,
-        current_program: 0,
-        events: Vec::new(),
-    }
+pub fn build_solidity(src: &str) -> MockSubstrate {
+    build_solidity_with_overflow_check(src, false)
 }
 }
-
-pub fn build_solidity_with_overflow_check(src: &'static str) -> MockSubstrate {
+pub fn build_solidity_with_overflow_check(src: &str, math_overflow_flag: bool) -> MockSubstrate {
     let mut cache = FileResolver::new();
     let mut cache = FileResolver::new();
 
 
     cache.set_file_contents("test.sol", src.to_string());
     cache.set_file_contents("test.sol", src.to_string());
@@ -1251,7 +1208,7 @@ pub fn build_solidity_with_overflow_check(src: &'static str) -> MockSubstrate {
         &mut cache,
         &mut cache,
         inkwell::OptimizationLevel::Default,
         inkwell::OptimizationLevel::Default,
         Target::default_substrate(),
         Target::default_substrate(),
-        true,
+        math_overflow_flag,
     );
     );
 
 
     ns.print_diagnostics_in_plain(&cache, false);
     ns.print_diagnostics_in_plain(&cache, false);

+ 377 - 2
tests/substrate_tests/expressions.rs

@@ -1,10 +1,14 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
 use crate::{build_solidity, build_solidity_with_overflow_check};
 use crate::{build_solidity, build_solidity_with_overflow_check};
-use num_bigint::BigInt;
-use num_bigint::Sign;
+use num_bigint::{BigInt, BigUint, RandBigInt, Sign};
 use parity_scale_codec::{Decode, Encode};
 use parity_scale_codec::{Decode, Encode};
+use rand::seq::SliceRandom;
 use rand::Rng;
 use rand::Rng;
+use std::ops::Add;
+use std::ops::Div;
+use std::ops::Mul;
+use std::ops::Sub;
 
 
 #[test]
 #[test]
 fn celcius_and_fahrenheit() {
 fn celcius_and_fahrenheit() {
@@ -941,6 +945,57 @@ fn large_power() {
     );
     );
 }
 }
 
 
+#[test]
+fn test_power_overflow_boundaries() {
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function pow(uintN a, uintN b) public returns (uintN) { 
+                return a ** b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        let base = BigUint::from(2_u32);
+        let mut base_data = base.to_bytes_le();
+
+        let exp = BigUint::from(width - 1);
+        let mut exp_data = exp.to_bytes_le();
+
+        let width_rounded = (width as usize / 8).next_power_of_two();
+
+        base_data.resize((width_rounded) as usize, 0);
+        exp_data.resize((width_rounded) as usize, 0);
+
+        contract.function(
+            "pow",
+            base_data
+                .clone()
+                .into_iter()
+                .chain(exp_data.into_iter())
+                .collect(),
+        );
+
+        let res = BigUint::from(2_usize).pow((width - 1).try_into().unwrap());
+        let mut res_data = res.to_bytes_le();
+        res_data.resize(width / 8, 0);
+        contract.vm.output.truncate((width / 8) as usize);
+
+        assert_eq!(contract.vm.output, res_data);
+
+        let exp = exp.add(1_usize);
+        let mut exp_data = exp.to_bytes_le();
+        exp_data.resize((width_rounded) as usize, 0);
+
+        contract.function_expect_failure(
+            "pow",
+            base_data.into_iter().chain(exp_data.into_iter()).collect(),
+        );
+    }
+}
+
 #[test]
 #[test]
 fn multiply() {
 fn multiply() {
     let mut rng = rand::thread_rng();
     let mut rng = rand::thread_rng();
@@ -950,7 +1005,9 @@ fn multiply() {
         "
         "
         contract c {
         contract c {
             function multiply(uint a, uint b) public returns (uint) {
             function multiply(uint a, uint b) public returns (uint) {
+                unchecked {
                 return a * b;
                 return a * b;
+                }
             }
             }
 
 
             function multiply_with_cast() public returns (uint64) {
             function multiply_with_cast() public returns (uint64) {
@@ -1001,6 +1058,310 @@ fn multiply() {
     }
     }
 }
 }
 
 
+#[test]
+fn test_mul_within_range_signed() {
+    // We generate a random value that fits N bits. Then, we multiply that value by 1, -1 or 0.
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let width_rounded = (width / 8_usize).next_power_of_two();
+
+        let mut runtime = build_solidity(&src);
+
+        let a = rng.gen_bigint((width - 1).try_into().unwrap()).sub(1_u32);
+        let a_sign = a.sign();
+        let mut a_data = a.to_signed_bytes_le();
+
+        let side = vec![-1, 0, 1];
+        let b = BigInt::from(*side.choose(&mut rng).unwrap() as i32);
+        let b_sign = b.sign();
+        let mut b_data = b.to_signed_bytes_le();
+
+        a_data.resize((width_rounded) as usize, sign_extend(a_sign));
+        b_data.resize((width_rounded) as usize, sign_extend(b_sign));
+
+        runtime.function(
+            "mul",
+            a_data.into_iter().chain(b_data.into_iter()).collect(),
+        );
+
+        runtime.vm.output.truncate((width / 8) as usize);
+
+        let value = a * b;
+        let value_sign = value.sign();
+
+        let mut value_data = value.to_signed_bytes_le();
+        value_data.resize((width / 8) as usize, sign_extend(value_sign));
+
+        assert_eq!(value_data, runtime.vm.output);
+    }
+}
+
+#[test]
+fn test_mul_within_range() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(uintN a, uintN b) public returns (uintN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let width_rounded = (width as usize / 8).next_power_of_two();
+        let mut runtime = build_solidity(&src);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1]. Here we generate a random number within this range and multiply it by 1
+        let a = rng.gen_biguint((width).try_into().unwrap()).sub(1_u32);
+
+        let mut a_data = a.to_bytes_le();
+
+        let b = BigUint::from(1_u32);
+
+        let mut b_data = b.to_bytes_le();
+        a_data.resize((width_rounded) as usize, 0);
+        b_data.resize((width_rounded) as usize, 0);
+
+        runtime.function(
+            "mul",
+            a_data.into_iter().chain(b_data.into_iter()).collect(),
+        );
+
+        runtime.vm.output.truncate((width / 8) as usize);
+
+        let value = a * b;
+
+        let mut value_data = value.to_bytes_le();
+        value_data.resize((width / 8) as usize, 0);
+
+        assert_eq!(value_data, runtime.vm.output);
+    }
+}
+
+#[test]
+fn test_overflow_boundaries() {
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1]. We generate these boundaries:
+        let upper_boundary = BigInt::from(2_u32).pow(width - 1).sub(1_u32);
+        let mut up_data = upper_boundary.to_signed_bytes_le();
+
+        let lower_boundary = BigInt::from(2_u32).pow(width - 1).mul(-1_i32);
+        let mut low_data = lower_boundary.to_signed_bytes_le();
+
+        let second_op = BigInt::from(1_u32);
+        let mut sec_data = second_op.to_signed_bytes_le();
+
+        let width_rounded = (width as usize / 8).next_power_of_two();
+
+        up_data.resize((width_rounded) as usize, 0);
+        low_data.resize((width_rounded) as usize, 255);
+        sec_data.resize((width_rounded) as usize, 0);
+
+        // Multiply the boundaries by 1.
+        contract.function(
+            "mul",
+            up_data
+                .clone()
+                .into_iter()
+                .chain(sec_data.clone().into_iter())
+                .collect(),
+        );
+
+        let res = upper_boundary.clone().mul(1_u32);
+        let mut res_data = res.to_signed_bytes_le();
+        res_data.resize((width / 8) as usize, 0);
+        contract.vm.output.truncate((width / 8) as usize);
+
+        assert_eq!(res_data, contract.vm.output);
+
+        contract.function(
+            "mul",
+            low_data
+                .clone()
+                .into_iter()
+                .chain(sec_data.clone().into_iter())
+                .collect(),
+        );
+
+        let res = lower_boundary.clone().mul(1_u32);
+        let mut res_data = res.to_signed_bytes_le();
+        res_data.resize((width / 8) as usize, 0);
+        contract.vm.output.truncate((width / 8) as usize);
+
+        assert_eq!(res_data, contract.vm.output);
+
+        let upper_boundary_plus_one = BigInt::from(2_u32).pow(width - 1);
+
+        // We subtract 2 instead of one to make the number even, so that no rounding occurs when we divide by 2 later on.
+        let lower_boundary_minus_two = BigInt::from(2_u32).pow(width - 1).mul(-1_i32).sub(2_i32);
+
+        let upper_second_op = upper_boundary_plus_one.div(2_u32);
+        let mut upper_second_op_data = upper_second_op.to_signed_bytes_le();
+
+        let lower_second_op = lower_boundary_minus_two.div(2_u32);
+        let mut lower_second_op_data = lower_second_op.to_signed_bytes_le();
+
+        let mut two_data = BigInt::from(2_u32).to_signed_bytes_le();
+
+        upper_second_op_data.resize((width_rounded) as usize, 0);
+        two_data.resize((width_rounded) as usize, 0);
+        lower_second_op_data.resize((width_rounded) as usize, 255);
+
+        // This will generate a value more than the upper boundary.
+        contract.function_expect_failure(
+            "mul",
+            upper_second_op_data
+                .clone()
+                .into_iter()
+                .chain(two_data.clone().into_iter())
+                .collect(),
+        );
+
+        // Generate a value less than the lower boundary
+        contract.function_expect_failure(
+            "mul",
+            lower_second_op_data
+                .clone()
+                .into_iter()
+                .chain(two_data.clone().into_iter())
+                .collect(),
+        );
+
+        // Upper boundary * Upper boundary
+        contract.function_expect_failure(
+            "mul",
+            up_data
+                .clone()
+                .into_iter()
+                .chain(up_data.clone().into_iter())
+                .collect(),
+        );
+
+        // Lower boundary * Lower boundary
+        contract.function_expect_failure(
+            "mul",
+            low_data
+                .clone()
+                .into_iter()
+                .chain(low_data.clone().into_iter())
+                .collect(),
+        );
+
+        // Lower boundary * Upper boundary
+        contract.function_expect_failure(
+            "mul",
+            low_data
+                .clone()
+                .into_iter()
+                .chain(up_data.clone().into_iter())
+                .collect(),
+        );
+    }
+}
+
+#[test]
+fn test_overflow_detect_signed() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1] .Generate a value that will overflow this range:
+        let limit = BigInt::from(2_u32).pow(width - 1).add(1_u32);
+
+        let first_operand_rand = rng.gen_bigint((width - 1).into()).sub(1_u32);
+        let first_op_sign = first_operand_rand.sign();
+        let mut first_op_data = first_operand_rand.to_signed_bytes_le();
+
+        let width_rounded = (width as usize / 8).next_power_of_two();
+        first_op_data.resize((width_rounded) as usize, sign_extend(first_op_sign));
+
+        // Calculate a number that when multiplied by first_operand_rand, the result will overflow N bits
+        let mut second_operand_rand = limit / &first_operand_rand;
+
+        if let Sign::Minus = second_operand_rand.sign() {
+            // Decrease by 1 if negative, this is to make sure the result will overflow
+            second_operand_rand = second_operand_rand.sub(1);
+        } else {
+            // Increase by 1 if psotive
+            second_operand_rand = second_operand_rand.add(1);
+        }
+
+        let second_op_sign = second_operand_rand.sign();
+        let mut second_op_data = second_operand_rand.to_signed_bytes_le();
+        second_op_data.resize((width_rounded) as usize, sign_extend(second_op_sign));
+
+        contract.function_expect_failure(
+            "mul",
+            first_op_data
+                .into_iter()
+                .chain(second_op_data.into_iter())
+                .collect(),
+        );
+    }
+}
+
+#[test]
+fn test_overflow_detect_unsigned() {
+    let mut rng = rand::thread_rng();
+    for width in (8..=256).step_by(8) {
+        let src = r#"
+        contract test {
+            function mul(uintN a, uintN b) public returns (uintN) {
+                return a * b;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+        let mut contract = build_solidity_with_overflow_check(&src, true);
+
+        // The range of values that can be held in signed N bits is [-2^(N-1), 2^(N-1)-1] .Generate a value that will overflow this range:
+        let limit = BigUint::from(2_u32).pow(width).add(1_u32);
+
+        let first_operand_rand = rng.gen_biguint((width).into()).sub(1_u32);
+        let mut first_op_data = first_operand_rand.to_bytes_le();
+
+        let width_rounded = (width as usize / 8).next_power_of_two();
+        first_op_data.resize((width_rounded) as usize, 0);
+
+        // Calculate a number that when multiplied by first_operand_rand, the result will overflow N bits
+        let second_operand_rand = (limit / &first_operand_rand).add(1_u32);
+
+        let mut second_op_data = second_operand_rand.to_bytes_le();
+        second_op_data.resize((width_rounded) as usize, 0);
+
+        contract.function_expect_failure(
+            "mul",
+            first_op_data
+                .into_iter()
+                .chain(second_op_data.into_iter())
+                .collect(),
+        );
+    }
+}
+
 #[test]
 #[test]
 fn bytes_bitwise() {
 fn bytes_bitwise() {
     #[derive(Debug, PartialEq, Eq, Encode, Decode)]
     #[derive(Debug, PartialEq, Eq, Encode, Decode)]
@@ -1329,6 +1690,7 @@ fn addition_overflow() {
             }
             }
         }
         }
         "#,
         "#,
+        true,
     );
     );
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
@@ -1351,6 +1713,7 @@ fn unchecked_addition_overflow() {
             }
             }
         }
         }
         "#,
         "#,
+        true,
     );
     );
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
@@ -1372,6 +1735,7 @@ fn subtraction_underflow() {
             }
             }
         }
         }
         "#,
         "#,
+        true,
     );
     );
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
@@ -1394,6 +1758,7 @@ fn unchecked_subtraction_underflow() {
             }
             }
         }
         }
         "#,
         "#,
+        true,
     );
     );
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
@@ -1415,6 +1780,7 @@ fn multiplication_overflow() {
             }
             }
         }
         }
         "#,
         "#,
+        true,
     );
     );
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
@@ -1437,6 +1803,7 @@ fn unchecked_multiplication_overflow() {
             }
             }
         }
         }
         "#,
         "#,
+        true,
     );
     );
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
@@ -1509,3 +1876,11 @@ fn address_pass_by_value() {
 
 
     runtime.function("bar", Vec::new());
     runtime.function("bar", Vec::new());
 }
 }
+
+fn sign_extend(sign: Sign) -> u8 {
+    if sign == Sign::Minus {
+        255
+    } else {
+        0
+    }
+}