From 1f29c13c2c04340fa797de85d873418716a574b8 Mon Sep 17 00:00:00 2001 From: Dario Russi Date: Mon, 12 Sep 2022 15:21:07 -0700 Subject: [PATCH] More tests around string validation code --- .../src/transaction_arg_validation.rs | 2 +- .../e2e-move-tests/tests/string_args.rs | 531 ++++++++---------- 2 files changed, 221 insertions(+), 312 deletions(-) diff --git a/aptos-move/aptos-vm/src/transaction_arg_validation.rs b/aptos-move/aptos-vm/src/transaction_arg_validation.rs index a65c2aab6ecdb..2b2063b2ae7f0 100644 --- a/aptos-move/aptos-vm/src/transaction_arg_validation.rs +++ b/aptos-move/aptos-vm/src/transaction_arg_validation.rs @@ -163,7 +163,7 @@ fn validate_arg( // performed in `is_valid_txn_arg` let st = session .get_struct_type(*idx) - .expect("unrachable, type must exist"); + .expect("unreachable, type must exist"); let full_name = format!("{}::{}", st.module.short_str_lossless(), st.name); // load the serialized string let len = get_len(cursor)?; diff --git a/aptos-move/e2e-move-tests/tests/string_args.rs b/aptos-move/e2e-move-tests/tests/string_args.rs index b57f7ad4755e1..89cafe59a22de 100644 --- a/aptos-move/e2e-move-tests/tests/string_args.rs +++ b/aptos-move/e2e-move-tests/tests/string_args.rs @@ -14,8 +14,7 @@ struct ModuleData { state: Vec, } -#[test] -fn string_args() { +fn success(tests: Vec<(&str, Vec<(Vec>, &str)>)>) { let mut h = MoveHarness::new(); // Load the code @@ -27,26 +26,23 @@ fn string_args() { // Check in initial state, resource does not exist. assert!(!h.exists_resource(acc.address(), module_data.clone())); - // Now send hi transaction, after that resource should exist and carry value - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi").unwrap(), - vec![], - vec![bcs::to_bytes("hi there!".as_bytes()).unwrap()], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data) - .unwrap() - .state - ) - .unwrap(), - "hi there!" - ); + for (entry, in_out) in tests { + for (args, expected_change) in in_out { + assert_success!(h.run_entry_function(&acc, str::parse(entry).unwrap(), vec![], args)); + assert_eq!( + String::from_utf8( + h.read_resource::(acc.address(), module_data.clone()) + .unwrap() + .state + ) + .unwrap(), + expected_change, + ); + } + } } -#[test] -fn string_args_vec() { +fn fail(tests: Vec<(&str, Vec>, StatusCode)>) { let mut h = MoveHarness::new(); // Load the code @@ -56,78 +52,66 @@ fn string_args_vec() { let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data.clone())); + assert!(!h.exists_resource(acc.address(), module_data)); + + for (entry, args, _err) in tests { + // Now send hi transaction, after that resource should exist and carry value + let status = h.run_entry_function(&acc, str::parse(entry).unwrap(), vec![], args); + assert_vm_status!(status, _err); + } +} + +// Generates a vector of a vector of strings. Used to produce big size arguments +// that require more than 1 byte lenght when compressed in uleb128 +fn big_string_vec(first_dim: u64, second_dim: u64, base: &str) -> Vec { + let mut outer = vec![]; + for i in 0..first_dim { + let mut inner = vec![]; + for j in 0..second_dim { + inner.push(format!("{}{}{}", base, i, j)); + } + outer.push(inner); + } + bcs::to_bytes(&outer).unwrap() +} + +#[test] +fn string_args() { + let mut tests = vec![]; + + // just strings + let args = vec![bcs::to_bytes("hi there!".as_bytes()).unwrap()]; + let expected_change = "hi there!"; + + tests.push(("0xcafe::test::hi", vec![(args, expected_change)])); + + // vector of strings + let mut in_out = vec![]; let s_vec = vec![ "hi there!".as_bytes(), "hello".as_bytes(), "world".as_bytes(), ]; - // Now send hi_vec transaction, after that resource should exist and carry value - let mut i = 0u64; - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi_vec").unwrap(), - vec![], - vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data.clone()) - .unwrap() - .state - ) - .unwrap(), - "hi there!" - ); - // Now send hi_vec transaction, after that resource should exist and carry value - i = 1u64; - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi_vec").unwrap(), - vec![], - vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data.clone()) - .unwrap() - .state - ) - .unwrap(), - "hello" - ); - // Now send hi_vec transaction, after that resource should exist and carry value - i = 2u64; - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi_vec").unwrap(), - vec![], - vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data) - .unwrap() - .state - ) - .unwrap(), - "world" - ); -} + let i = 0u64; + let args = vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()]; + let expected_change = "hi there!"; + in_out.push((args, expected_change)); -#[test] -fn string_args_vec_vec() { - let mut h = MoveHarness::new(); + let i = 1u64; + let args = vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()]; + let expected_change = "hello"; + in_out.push((args, expected_change)); - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); + let i = 2u64; + let args = vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()]; + let expected_change = "world"; + in_out.push((args, expected_change)); - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); + tests.push(("0xcafe::test::hi_vec", in_out)); - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data.clone())); + // vector of vector of strings + let mut in_out = vec![]; let s_vec = vec![ vec![ @@ -146,184 +130,124 @@ fn string_args_vec_vec() { "hello".as_bytes(), ], ]; - // Now send more_hi_vec transaction, after that resource should exist and carry value let i = 0u64; let j = 0u64; - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::more_hi_vec").unwrap(), - vec![], - vec![ - bcs::to_bytes(&s_vec).unwrap(), - bcs::to_bytes(&i).unwrap(), - bcs::to_bytes(&j).unwrap() - ], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data.clone()) - .unwrap() - .state - ) - .unwrap(), - "hi there!" - ); - // Now send more_hi_vec transaction, after that resource should exist and carry value + let args = vec![ + bcs::to_bytes(&s_vec).unwrap(), + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + let expected_change = "hi there!"; + in_out.push((args, expected_change)); + let i = 1u64; - let j = 2u64; - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::more_hi_vec").unwrap(), - vec![], - vec![ - bcs::to_bytes(&s_vec).unwrap(), - bcs::to_bytes(&i).unwrap(), - bcs::to_bytes(&j).unwrap() - ], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data.clone()) - .unwrap() - .state - ) - .unwrap(), - "hi there!" - ); - // Now send more_hi_vec transaction, after that resource should exist and carry value - let i = 2u64; let j = 1u64; - assert_success!(h.run_entry_function( - &acc, - str::parse("0xcafe::test::more_hi_vec").unwrap(), - vec![], - vec![ - bcs::to_bytes(&s_vec).unwrap(), - bcs::to_bytes(&i).unwrap(), - bcs::to_bytes(&j).unwrap() - ], - )); - assert_eq!( - String::from_utf8( - h.read_resource::(acc.address(), module_data) - .unwrap() - .state - ) - .unwrap(), - "hi there!" - ); -} - -#[test] -fn string_args_bad_1() { - let mut h = MoveHarness::new(); - - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); - - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); - - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); - - // Now send hi transaction, after that resource should exist and carry value - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi").unwrap(), - vec![], - vec![bcs::to_bytes(&[0xf0, 0x28, 0x8c, 0xbc]).unwrap()], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) -} + let args = vec![ + bcs::to_bytes(&s_vec).unwrap(), + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + let expected_change = "world"; + in_out.push((args, expected_change)); -#[test] -fn string_args_bad_2() { - let mut h = MoveHarness::new(); + let i = 2u64; + let j = 2u64; + let args = vec![ + bcs::to_bytes(&s_vec).unwrap(), + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + let expected_change = "hello"; + in_out.push((args, expected_change)); + + tests.push(("0xcafe::test::more_hi_vec", in_out)); + + // vectors or strings with size taking more than 1 byte in uleb128 compression + let mut in_out = vec![]; + + let hello = "hello".repeat(60); + let string_arg = big_string_vec(10, 10, hello.as_str()); + let i = 8u64; + let j = 7u64; + let args = vec![ + string_arg, + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + let expected_change = format!("{}{}{}", hello, i, j); + in_out.push((args, expected_change.as_str())); - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); + let hello = "hello".repeat(6); + let string_arg = big_string_vec(300, 2, hello.as_str()); + let i = 8u64; + let j = 0u64; + let args = vec![ + string_arg, + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + let expected_change = format!("{}{}{}", hello, i, j); + in_out.push((args, expected_change.as_str())); - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); + let hello = "hello".repeat(6); + let string_arg = big_string_vec(2, 300, hello.as_str()); + let i = 0u64; + let j = 7u64; + let args = vec![ + string_arg, + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + let expected_change = format!("{}{}{}", hello, i, j); + in_out.push((args, expected_change.as_str())); - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); + tests.push(("0xcafe::test::more_hi_vec", in_out)); - // Now send hi transaction, after that resource should exist and carry value - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi").unwrap(), - vec![], - vec![bcs::to_bytes(&[0xc3, 0x28]).unwrap()], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) + success(tests); } #[test] -fn string_args_bad_vec1() { - let mut h = MoveHarness::new(); - - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); - - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); +fn string_args_bad_utf8() { + let mut tests = vec![]; + + // simple strings + let args = vec![bcs::to_bytes(&[0xf0, 0x28, 0x8c, 0xbc]).unwrap()]; + tests.push(( + "0xcafe::test::hi", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); + let args = vec![bcs::to_bytes(&[0xc3, 0x28]).unwrap()]; + tests.push(( + "0xcafe::test::hi", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); + // vector of strings let bad = vec![0xc3, 0x28]; let s_vec = vec![&bad[..], "hello".as_bytes(), "world".as_bytes()]; - // Now send hi_vec transaction, after that resource should exist and carry value let i = 0u64; - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi_vec").unwrap(), - vec![], - vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) -} - -#[test] -fn string_args_bad_vec2() { - let mut h = MoveHarness::new(); - - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); - - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); + let args = vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()]; + tests.push(( + "0xcafe::test::hi_vec", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); + let bad = vec![0xc3, 0x28]; + let s_vec = vec![&bad[..], "hello".as_bytes(), "world".as_bytes()]; + let args = vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()]; + tests.push(( + "0xcafe::test::hi_vec", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); - let bad = vec![0xff, 0xfe]; - let s_vec = vec!["hello".as_bytes(), "world".as_bytes(), &bad[..]]; - // Now send hi_vec transaction, after that resource should exist and carry value + // vector of vector of strings let i = 0u64; - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::hi_vec").unwrap(), - vec![], - vec![bcs::to_bytes(&s_vec).unwrap(), bcs::to_bytes(&i).unwrap()], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) -} - -#[test] -fn string_args_bad_vec_vec_1() { - let mut h = MoveHarness::new(); - - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); - - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); - - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); + let j = 0u64; let bad = vec![0x40, 0xfe]; let s_vec = vec![ @@ -339,34 +263,16 @@ fn string_args_bad_vec_vec_1() { "hello".as_bytes(), ], ]; - // Now send more_hi_vec transaction, after that resource should exist and carry value - let i = 0u64; - let j = 0u64; - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::more_hi_vec").unwrap(), - vec![], - vec![ - bcs::to_bytes(&s_vec).unwrap(), - bcs::to_bytes(&i).unwrap(), - bcs::to_bytes(&j).unwrap(), - ], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) -} - -#[test] -fn string_args_bad_vec_vec_2() { - let mut h = MoveHarness::new(); - - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); - - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); - - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); + let args = vec![ + bcs::to_bytes(&s_vec).unwrap(), + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + tests.push(( + "0xcafe::test::more_hi_vec", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); let bad = vec![0xf0, 0x28, 0x8c, 0x28]; let s_vec = vec![ @@ -382,36 +288,18 @@ fn string_args_bad_vec_vec_2() { "hello".as_bytes(), ], ]; - // Now send more_hi_vec transaction, after that resource should exist and carry value - let i = 0u64; - let j = 0u64; - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::more_hi_vec").unwrap(), - vec![], - vec![ - bcs::to_bytes(&s_vec).unwrap(), - bcs::to_bytes(&i).unwrap(), - bcs::to_bytes(&j).unwrap(), - ], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) -} - -#[test] -fn string_args_bad_vec_vec_3() { - let mut h = MoveHarness::new(); - - // Load the code - let acc = h.new_account_at(AccountAddress::from_hex_literal("0xcafe").unwrap()); - assert_success!(h.publish_package(&acc, &common::test_dir_path("string_args.data/pack"))); - - let module_data = parse_struct_tag("0xCAFE::test::ModuleData").unwrap(); - - // Check in initial state, resource does not exist. - assert!(!h.exists_resource(acc.address(), module_data)); + let args = vec![ + bcs::to_bytes(&s_vec).unwrap(), + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + tests.push(( + "0xcafe::test::more_hi_vec", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); - let bad = vec![0x40, 0xff]; + let bad = vec![0x60, 0xff]; let s_vec = vec![ vec![ "hi there!".as_bytes(), @@ -425,18 +313,39 @@ fn string_args_bad_vec_vec_3() { ], vec!["world".as_bytes(), "hi there!".as_bytes(), &bad[..]], ]; - // Now send more_hi_vec transaction, after that resource should exist and carry value - let i = 0u64; - let j = 0u64; - let status = h.run_entry_function( - &acc, - str::parse("0xcafe::test::more_hi_vec").unwrap(), - vec![], - vec![ - bcs::to_bytes(&s_vec).unwrap(), - bcs::to_bytes(&i).unwrap(), - bcs::to_bytes(&j).unwrap(), - ], - ); - assert_vm_status!(status, StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT) + let args = vec![ + bcs::to_bytes(&s_vec).unwrap(), + bcs::to_bytes(&i).unwrap(), + bcs::to_bytes(&j).unwrap(), + ]; + tests.push(( + "0xcafe::test::more_hi_vec", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )); + + fail(tests); +} + +#[test] +fn string_args_chopped() { + let idx = 0u64; + let s_vec = vec![ + "hi there!".as_bytes(), + "hello".as_bytes(), + "world".as_bytes(), + ]; + let string_arg = bcs::to_bytes(&s_vec).unwrap(); + let mut i = string_arg.len() - 1; + while i > 1 { + let mut arg = string_arg.clone(); + arg.remove(i); + let args = vec![arg, bcs::to_bytes(&idx).unwrap()]; + fail(vec![( + "0xcafe::test::hi_vec", + args, + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, + )]); + i /= 2; + } }