From 7462815136b0f4e4c89081cde2822d86e03e8348 Mon Sep 17 00:00:00 2001 From: BowTiedWoo Date: Fri, 10 Jan 2025 22:02:50 +0200 Subject: [PATCH] fix: grow memory when not enough space on stack + add additional tests --- clar2wasm/src/standard/standard.wat | 89 ++++++++++++++++ clar2wasm/tests/oom-checker/unit_tests.rs | 120 +++++++++++++++------- 2 files changed, 173 insertions(+), 36 deletions(-) diff --git a/clar2wasm/src/standard/standard.wat b/clar2wasm/src/standard/standard.wat index e299a4fc..580851fc 100644 --- a/clar2wasm/src/standard/standard.wat +++ b/clar2wasm/src/standard/standard.wat @@ -2653,6 +2653,28 @@ (local $i i32) (local $j i32) (local.set $j (local.tee $i (global.get $stack-pointer))) + ;; Check if we need more memory (40 bytes is max needed for uint128) + (if (i32.gt_u + (i32.add (local.get $i) (i32.const 40)) + (i32.mul (memory.size) (i32.const 65536))) + (then + ;; Calculate needed pages (64KB per page) + (memory.grow + (i32.add + (i32.div_u + (i32.sub + (i32.add (local.get $i) (i32.const 40)) + (i32.mul (memory.size) (i32.const 65536)) + ) + (i32.const 65536) + ) + (i32.const 1) ;; Add one extra page for safety + ) + ) + drop ;; Drop the previous memory size returned by memory.grow + ) + ) + ;; slow loop while $hi > 0 (if (i64.ne (local.get $hi) (i64.const 0)) (then @@ -2725,6 +2747,28 @@ (func $stdlib.int-to-string (param $lo i64) (param $hi i64) (result i32 i32) (local $negative i32) (local $len i32) + + ;; Grow memory if needed (41 bytes: 40 for digits + 1 for minus sign) + (if (i32.gt_u + (i32.add (global.get $stack-pointer) (i32.const 41)) + (i32.mul (memory.size) (i32.const 65536))) ;; 0x10000 = memory size of a page + (then + (memory.grow + (i32.add + (i32.shr_u + (i32.sub + (i32.add (global.get $stack-pointer) (i32.const 41)) + (i32.mul (memory.size) (i32.const 65536)) + ) + (i32.const 16) ;; 2^16 = 65536 + ) + (i32.const 1) ;; Extra page for safety + ) + ) + drop + ) + ) + (local.set $negative (i64.lt_s (local.get $hi) (i64.const 0))) ;; add a '-' if n < 0 (if (local.get $negative) @@ -2762,6 +2806,28 @@ (local $i i32) (local $j i32) (local.set $j (local.tee $i (global.get $stack-pointer))) + ;; Check if we need more memory (40 bytes is max needed for uint128) + (if (i32.gt_u + (i32.add (local.get $i) (i32.const 40)) + (i32.mul (memory.size) (i32.const 65536))) ;; 0x10000 = memory size of a page + (then + ;; Calculate needed pages (64KB per page) + (memory.grow + (i32.add + (i32.div_u + (i32.sub + (i32.add (local.get $i) (i32.const 40)) + (i32.mul (memory.size) (i32.const 65536)) + ) + (i32.const 65536) + ) + (i32.const 1) ;; Add one extra page for safety + ) + ) + drop ;; Drop the previous memory size returned by memory.grow + ) + ) + ;; slow loop while $hi > 0 (if (i64.ne (local.get $hi) (i64.const 0)) (then @@ -2837,6 +2903,29 @@ (func $stdlib.int-to-utf8 (param $lo i64) (param $hi i64) (result i32 i32) (local $negative i32) (local $len i32) + + ;; Grow memory if needed (44 bytes: 40 for digits + 4 for UTF-8 minus sign) + (if (i32.gt_u + (i32.add (global.get $stack-pointer) (i32.const 44)) + (i32.mul (memory.size) (i32.const 65536)) + ) + (then + (memory.grow + (i32.add + (i32.shr_u ;; Use shift instead of division + (i32.sub + (i32.add (global.get $stack-pointer) (i32.const 44)) + (i32.mul (memory.size) (i32.const 65536)) + ) + (i32.const 16) ;; 2^16 = 65536 + ) + (i32.const 1) ;; Extra page for safety + ) + ) + drop + ) + ) + (local.set $negative (i32.shl (i64.lt_s (local.get $hi) (i64.const 0)) (i32.const 2))) ;; add a '-' if n < 0 (if (local.get $negative) diff --git a/clar2wasm/tests/oom-checker/unit_tests.rs b/clar2wasm/tests/oom-checker/unit_tests.rs index fe265b97..e9c80974 100644 --- a/clar2wasm/tests/oom-checker/unit_tests.rs +++ b/clar2wasm/tests/oom-checker/unit_tests.rs @@ -70,22 +70,6 @@ fn concat_oom() { ); } -#[cfg(not(feature = "test-clarity-v1"))] -#[test] -fn replace_at_oom() { - crosscheck_oom_with_non_literal_args( - "(replace-at? (list 1 2 3) u0 42)", - &[list_of(TypeSignature::IntType, 3)], - Ok(Some( - Value::some( - Value::cons_list_unsanitized(vec![Value::Int(42), Value::Int(2), Value::Int(3)]) - .unwrap(), - ) - .unwrap(), - )), - ); -} - #[test] fn map_oom() { crosscheck_oom_with_non_literal_args( @@ -243,26 +227,6 @@ fn get_burn_block_info_pox_addrs_oom() { ); } -#[test] -#[ignore = "issue #592"] -fn int_to_ascii_oom() { - crosscheck_oom( - "(int-to-ascii 42)", - Ok(Some( - Value::string_ascii_from_bytes(b"42".to_vec()).unwrap(), - )), - ); -} - -#[test] -#[ignore = "issue #592"] -fn int_to_utf8_oom() { - crosscheck_oom( - "(int-to-utf8 42)", - Ok(Some(Value::string_utf8_from_bytes(b"42".to_vec()).unwrap())), - ); -} - #[test] fn data_var_oom() { crosscheck_oom( @@ -282,3 +246,87 @@ fn secp256k1_recover_oom() { Ok(Some(Value::okay(Value::buff_from(vec![3, 173, 184, 222, 75, 251, 101, 219, 44, 253, 97, 32, 213, 92, 101, 38, 174, 156, 82, 230, 117, 219, 126, 71, 48, 134, 54, 83, 75, 167, 120, 97, 16]).unwrap()).unwrap())), ); } + +#[cfg(not(feature = "test-clarity-v1"))] +#[cfg(test)] +mod clarity_v2_v3 { + use clarity::vm::types::TypeSignature; + use clarity::vm::Value; + + use crate::{crosscheck_oom, crosscheck_oom_with_non_literal_args, list_of}; + + #[test] + fn replace_at_oom() { + crosscheck_oom_with_non_literal_args( + "(replace-at? (list 1 2 3) u0 42)", + &[list_of(TypeSignature::IntType, 3)], + Ok(Some( + Value::some( + Value::cons_list_unsanitized(vec![ + Value::Int(42), + Value::Int(2), + Value::Int(3), + ]) + .unwrap(), + ) + .unwrap(), + )), + ); + } + + #[test] + fn int_to_ascii_oom_negative() { + crosscheck_oom( + "(int-to-ascii -42)", + Ok(Some( + Value::string_ascii_from_bytes(b"-42".to_vec()).unwrap(), + )), + ); + } + + #[test] + fn int_to_utf8_oom_negative() { + crosscheck_oom( + "(int-to-utf8 -42)", + Ok(Some( + Value::string_utf8_from_bytes(b"-42".to_vec()).unwrap(), + )), + ); + } + + #[test] + fn int_to_ascii_oom() { + crosscheck_oom( + "(int-to-ascii 42)", + Ok(Some( + Value::string_ascii_from_bytes(b"42".to_vec()).unwrap(), + )), + ); + } + + #[test] + fn int_to_utf8_oom() { + crosscheck_oom( + "(int-to-utf8 42)", + Ok(Some(Value::string_utf8_from_bytes(b"42".to_vec()).unwrap())), + ); + } + + #[test] + fn unsigned_value_int_to_ascii_oom() { + crosscheck_oom( + "(int-to-ascii u42)", + Ok(Some( + Value::string_ascii_from_bytes(b"42".to_vec()).unwrap(), + )), + ); + } + + #[test] + fn unsigned_value_int_to_utf8_oom() { + crosscheck_oom( + "(int-to-utf8 u42)", + Ok(Some(Value::string_utf8_from_bytes(b"42".to_vec()).unwrap())), + ); + } +}