diff --git a/cosmwasm/packages/enclave-ffi-types/src/types.rs b/cosmwasm/packages/enclave-ffi-types/src/types.rs index a9b675522..109dbfe69 100644 --- a/cosmwasm/packages/enclave-ffi-types/src/types.rs +++ b/cosmwasm/packages/enclave-ffi-types/src/types.rs @@ -154,6 +154,8 @@ pub enum EnclaveError { Panic, #[display(fmt = "enclave ran out of heap memory")] OutOfMemory, + #[display(fmt = "depth of nested contract calls exceeded")] + ExceededRecursionLimit, /// Unexpected Error happened, no more details available #[display(fmt = "unknown error")] Unknown, diff --git a/cosmwasm/packages/std/src/errors/system_error.rs b/cosmwasm/packages/std/src/errors/system_error.rs index 6e317bb58..77a700181 100644 --- a/cosmwasm/packages/std/src/errors/system_error.rs +++ b/cosmwasm/packages/std/src/errors/system_error.rs @@ -21,6 +21,7 @@ pub enum SystemError { NoSuchContract { addr: HumanAddr }, Unknown {}, UnsupportedRequest { kind: String }, + ExceededRecursionLimit {}, } impl std::error::Error for SystemError {} @@ -45,6 +46,7 @@ impl std::fmt::Display for SystemError { SystemError::UnsupportedRequest { kind } => { write!(f, "Unsupported query type: {}", kind) } + SystemError::ExceededRecursionLimit {} => write!(f, "Query recursion limit exceeded"), } } } diff --git a/cosmwasm/packages/wasmi-runtime/Enclave.config.prod.xml b/cosmwasm/packages/wasmi-runtime/Enclave.config.prod.xml index f2b1d4248..feb1e1dbd 100644 --- a/cosmwasm/packages/wasmi-runtime/Enclave.config.prod.xml +++ b/cosmwasm/packages/wasmi-runtime/Enclave.config.prod.xml @@ -3,7 +3,7 @@ 0 0 0x800000 - 0x8000000 + 0x10000000 1 1 1 diff --git a/cosmwasm/packages/wasmi-runtime/Enclave.config.xml b/cosmwasm/packages/wasmi-runtime/Enclave.config.xml index 8df1c3bb5..04e8b068b 100644 --- a/cosmwasm/packages/wasmi-runtime/Enclave.config.xml +++ b/cosmwasm/packages/wasmi-runtime/Enclave.config.xml @@ -3,7 +3,7 @@ 0 0 0x800000 - 0x4000000 + 0x10000000 1 1 0 diff --git a/cosmwasm/packages/wasmi-runtime/src/cosmwasm/system_error.rs b/cosmwasm/packages/wasmi-runtime/src/cosmwasm/system_error.rs index 9637c236a..5ac65817d 100644 --- a/cosmwasm/packages/wasmi-runtime/src/cosmwasm/system_error.rs +++ b/cosmwasm/packages/wasmi-runtime/src/cosmwasm/system_error.rs @@ -23,6 +23,7 @@ pub enum SystemError { NoSuchContract { addr: HumanAddr }, Unknown {}, UnsupportedRequest { kind: String }, + ExceededRecursionLimit {}, } pub type SystemResult = Result; diff --git a/cosmwasm/packages/wasmi-runtime/src/exports.rs b/cosmwasm/packages/wasmi-runtime/src/exports.rs index 9da5daf03..b59e74b3c 100644 --- a/cosmwasm/packages/wasmi-runtime/src/exports.rs +++ b/cosmwasm/packages/wasmi-runtime/src/exports.rs @@ -13,7 +13,7 @@ use crate::results::{ result_query_success_to_queryresult, }; use crate::{ - oom_handler, + oom_handler, recursion_depth, utils::{validate_const_ptr, validate_mut_ptr}, }; @@ -116,6 +116,19 @@ pub unsafe extern "C" fn ecall_init( sig_info: *const u8, sig_info_len: usize, ) -> InitResult { + let _recursion_guard = match recursion_depth::guard() { + Ok(rg) => rg, + Err(err) => { + // https://github.com/enigmampc/SecretNetwork/pull/517#discussion_r481924571 + // I believe that this error condition is currently unreachable. + // I think we can safely remove it completely right now, and have + // recursion_depth::increment() simply increment the counter with no further checks, + // but i wanted to stay on the safe side here, in case something changes in the + // future, and we can easily spot that we forgot to add a limit somewhere. + error!("recursion limit exceeded, can not perform init!"); + return InitResult::Failure { err }; + } + }; if let Err(err) = oom_handler::register_oom_handler() { error!("Could not register OOM handler!"); return InitResult::Failure { err }; @@ -200,6 +213,19 @@ pub unsafe extern "C" fn ecall_handle( sig_info: *const u8, sig_info_len: usize, ) -> HandleResult { + let _recursion_guard = match recursion_depth::guard() { + Ok(rg) => rg, + Err(err) => { + // https://github.com/enigmampc/SecretNetwork/pull/517#discussion_r481924571 + // I believe that this error condition is currently unreachable. + // I think we can safely remove it completely right now, and have + // recursion_depth::increment() simply increment the counter with no further checks, + // but i wanted to stay on the safe side here, in case something changes in the + // future, and we can easily spot that we forgot to add a limit somewhere. + error!("recursion limit exceeded, can not perform handle!"); + return HandleResult::Failure { err }; + } + }; if let Err(err) = oom_handler::register_oom_handler() { error!("Could not register OOM handler!"); return HandleResult::Failure { err }; @@ -280,6 +306,19 @@ pub unsafe extern "C" fn ecall_query( msg: *const u8, msg_len: usize, ) -> QueryResult { + let _recursion_guard = match recursion_depth::guard() { + Ok(rg) => rg, + Err(err) => { + // https://github.com/enigmampc/SecretNetwork/pull/517#discussion_r481924571 + // I believe that this error condition is currently unreachable. + // I think we can safely remove it completely right now, and have + // recursion_depth::increment() simply increment the counter with no further checks, + // but i wanted to stay on the safe side here, in case something changes in the + // future, and we can easily spot that we forgot to add a limit somewhere. + error!("recursion limit exceeded, can not perform query!"); + return QueryResult::Failure { err }; + } + }; if let Err(err) = oom_handler::register_oom_handler() { error!("Could not register OOM handler!"); return QueryResult::Failure { err }; diff --git a/cosmwasm/packages/wasmi-runtime/src/lib.rs b/cosmwasm/packages/wasmi-runtime/src/lib.rs index e726753ce..4e9a3dbff 100644 --- a/cosmwasm/packages/wasmi-runtime/src/lib.rs +++ b/cosmwasm/packages/wasmi-runtime/src/lib.rs @@ -21,6 +21,7 @@ pub mod exports; pub mod imports; pub mod logger; mod oom_handler; +mod recursion_depth; pub mod registration; use std::env; diff --git a/cosmwasm/packages/wasmi-runtime/src/oom_handler.rs b/cosmwasm/packages/wasmi-runtime/src/oom_handler.rs index 4caa9d091..40dd9c6de 100644 --- a/cosmwasm/packages/wasmi-runtime/src/oom_handler.rs +++ b/cosmwasm/packages/wasmi-runtime/src/oom_handler.rs @@ -72,12 +72,13 @@ impl SafetyBuffer { } lazy_static! { - /// SAFETY_BUFFER is a 32 MiB of SafetyBuffer. This is occupying 50% of available memory - /// to be extra sure this is enough. + /// SAFETY_BUFFER is a 4 MiB of SafetyBuffer. This is twice the bare minimum to unwind after + /// a best-case OOM event. thanks to the recursion limit on queries, together with other memory + /// limits, we don't expect to hit OOM, and this mechanism remains in place just in case. /// 2 MiB is the minimum allowed buffer. If we don't succeed to allocate 2 MiB, we throw a panic, - /// if we do succeed to allocate 2 MiB but less than 32 MiB than we move on and will try to allocate + /// if we do succeed to allocate 2 MiB but less than 4 MiB than we move on and will try to allocate /// the rest on the next entry to the enclave. - static ref SAFETY_BUFFER: SgxMutex = SgxMutex::new(SafetyBuffer::new(16 * 1024, 2 * 1024)); + static ref SAFETY_BUFFER: SgxMutex = SgxMutex::new(SafetyBuffer::new(4 * 1024, 2 * 1024)); } static OOM_HAPPENED: AtomicBool = AtomicBool::new(false); diff --git a/cosmwasm/packages/wasmi-runtime/src/recursion_depth.rs b/cosmwasm/packages/wasmi-runtime/src/recursion_depth.rs new file mode 100644 index 000000000..8d7e201f9 --- /dev/null +++ b/cosmwasm/packages/wasmi-runtime/src/recursion_depth.rs @@ -0,0 +1,55 @@ +use std::sync::SgxMutex; + +use lazy_static::lazy_static; + +use enclave_ffi_types::EnclaveError; + +const RECURSION_LIMIT: u8 = 5; + +lazy_static! { + /// This counter tracks the recursion depth of queries, + /// and effectively the amount of loaded instances of WASMI. + /// + /// It is incremented before each computation begins and is decremented after each computation ends. + static ref RECURSION_DEPTH: SgxMutex = SgxMutex::new(0); +} + +fn increment() -> Result<(), EnclaveError> { + let mut depth = RECURSION_DEPTH.lock().unwrap(); + if *depth == RECURSION_LIMIT { + return Err(EnclaveError::ExceededRecursionLimit); + } + *depth = depth.saturating_add(1); + Ok(()) +} + +fn decrement() { + let mut depth = RECURSION_DEPTH.lock().unwrap(); + *depth = depth.saturating_sub(1); +} + +/// Returns whether or not this is the last possible level of recursion +pub fn limit_reached() -> bool { + *RECURSION_DEPTH.lock().unwrap() == RECURSION_LIMIT +} + +pub struct RecursionGuard { + _private: (), // prevent direct instantiation outside this module +} + +impl RecursionGuard { + pub fn new() -> Result { + increment()?; + Ok(Self { _private: () }) + } +} + +impl Drop for RecursionGuard { + fn drop(&mut self) { + decrement(); + } +} + +pub fn guard() -> Result { + RecursionGuard::new() +} diff --git a/cosmwasm/packages/wasmi-runtime/src/wasm/query_chain.rs b/cosmwasm/packages/wasmi-runtime/src/wasm/query_chain.rs index 464833708..ce7656c9c 100644 --- a/cosmwasm/packages/wasmi-runtime/src/wasm/query_chain.rs +++ b/cosmwasm/packages/wasmi-runtime/src/wasm/query_chain.rs @@ -1,11 +1,12 @@ use super::errors::WasmEngineError; use crate::crypto::Ed25519PublicKey; +use crate::recursion_depth; use crate::wasm::types::{IoNonce, SecretMessage}; use crate::{exports, imports}; -use crate::cosmwasm::encoding::Binary; -use crate::cosmwasm::query::{QueryRequest, WasmQuery}; use crate::cosmwasm::{ + encoding::Binary, + query::{QueryRequest, WasmQuery}, std_error::{StdError, StdResult}, system_error::{SystemError, SystemResult}, }; @@ -22,6 +23,10 @@ pub fn encrypt_and_query_chain( gas_used: &mut u64, gas_limit: u64, ) -> Result, WasmEngineError> { + if let Some(answer) = check_recursion_limit() { + return serialize_error_response(&answer); + } + let mut query_struct: QueryRequest = match serde_json::from_slice(query) { Ok(query_struct) => query_struct, Err(err) => { @@ -182,6 +187,21 @@ fn query_chain( (Ok(value), gas_used) } +/// Check whether the query is allowed to run. +/// +/// We make sure that a recursion limit is in place in order to +/// mitigate cases where the enclave runs out of memory. +fn check_recursion_limit() -> Option>> { + if recursion_depth::limit_reached() { + debug!( + "Recursion limit reached while performing nested queries. Returning error to contract." + ); + Some(Err(SystemError::ExceededRecursionLimit {})) + } else { + None + } +} + fn system_error_invalid_request(request: &[u8], err: T) -> Result, WasmEngineError> where T: std::fmt::Debug + ToString, @@ -196,16 +216,7 @@ where error: err.to_string(), }); - serde_json::to_vec(&answer).map_err(|err| { - // this should never happen - debug!( - "encrypt_and_query_chain() got an error while trying to serialize the error {:?} returned to WASM: {:?}", - answer, - err - ); - - WasmEngineError::SerializationError - }) + serialize_error_response(&answer) } fn system_error_invalid_response(response: Vec, err: T) -> Result, WasmEngineError> @@ -217,7 +228,13 @@ where error: err.to_string(), }); - serde_json::to_vec(&answer).map_err(|err| { + serialize_error_response(&answer) +} + +fn serialize_error_response( + answer: &SystemResult>, +) -> Result, WasmEngineError> { + serde_json::to_vec(answer).map_err(|err| { // this should never happen debug!( "encrypt_and_query_chain() got an error while trying to serialize the error {:?} returned to WASM: {:?}", diff --git a/go-cosmwasm/types/systemerror.go b/go-cosmwasm/types/systemerror.go index 827cd92b9..bdfd6639e 100644 --- a/go-cosmwasm/types/systemerror.go +++ b/go-cosmwasm/types/systemerror.go @@ -12,6 +12,7 @@ type SystemError struct { NoSuchContract *NoSuchContract `json:"no_such_contract,omitempty"` Unknown *Unknown `json:"unknown,omitempty"` UnsupportedRequest *UnsupportedRequest `json:"unsupported_request,omitempty"` + ExceededRecursionLimit *ExceededRecursionLimit `json:"exceeded_recursion_limit,omitempty"` } var ( @@ -21,6 +22,7 @@ var ( _ error = NoSuchContract{} _ error = Unknown{} _ error = UnsupportedRequest{} + _ error = ExceededRecursionLimit{} ) func (a SystemError) Error() string { @@ -35,6 +37,8 @@ func (a SystemError) Error() string { return a.Unknown.Error() case a.UnsupportedRequest != nil: return a.UnsupportedRequest.Error() + case a.ExceededRecursionLimit != nil: + return a.ExceededRecursionLimit.Error() default: panic("unknown error variant") } @@ -80,6 +84,12 @@ func (e UnsupportedRequest) Error() string { return fmt.Sprintf("unsupported request: %s", e.Kind) } +type ExceededRecursionLimit struct{} + +func (e ExceededRecursionLimit) Error() string { + return "unknown system error" +} + // ToSystemError will try to convert the given error to an SystemError. // This is important to returning any Go error back to Rust. // @@ -118,6 +128,10 @@ func ToSystemError(err error) *SystemError { return &SystemError{UnsupportedRequest: &t} case *UnsupportedRequest: return &SystemError{UnsupportedRequest: t} + case ExceededRecursionLimit: + return &SystemError{ExceededRecursionLimit: &t} + case *ExceededRecursionLimit: + return &SystemError{ExceededRecursionLimit: t} default: return nil } diff --git a/x/compute/internal/keeper/recurse_test.go b/x/compute/internal/keeper/recurse_test.go index 0d66d833c..7468734b8 100644 --- a/x/compute/internal/keeper/recurse_test.go +++ b/x/compute/internal/keeper/recurse_test.go @@ -281,6 +281,7 @@ func TestLimitRecursiveQueryGas(t *testing.T) { expectedGas uint64 expectOutOfGas bool expectOOM bool + expectRecursionLimit bool }{ "no recursion, lots of work": { gasLimit: 4_000_000, @@ -291,6 +292,18 @@ func TestLimitRecursiveQueryGas(t *testing.T) { expectQueriesFromContract: 0, expectedGas: GasWork2k, }, + "recursion 4, lots of work": { + gasLimit: 4_000_000, + msg: Recurse{ + Depth: 4, + Work: 2000, + }, + expectQueriesFromContract: 4, + expectedGas: GasWork2k + 9*(GasWork2k+GasReturnHashed), + expectOutOfGas: false, + expectOOM: false, + expectRecursionLimit: false, + }, "recursion 9, lots of work": { gasLimit: 4_000_000, msg: Recurse{ @@ -300,7 +313,8 @@ func TestLimitRecursiveQueryGas(t *testing.T) { expectQueriesFromContract: 9, expectedGas: GasWork2k + 9*(GasWork2k+GasReturnHashed), expectOutOfGas: false, - expectOOM: true, + expectOOM: false, + expectRecursionLimit: true, }, // this is where we expect an error... // it has enough gas to run 4 times and die on the 5th (4th time dispatching to sub-contract) @@ -314,7 +328,8 @@ func TestLimitRecursiveQueryGas(t *testing.T) { }, expectQueriesFromContract: 4, expectOutOfGas: false, - expectOOM: true, + expectOOM: false, + expectRecursionLimit: true, }, } @@ -354,6 +369,14 @@ func TestLimitRecursiveQueryGas(t *testing.T) { return } + if tc.expectRecursionLimit { + _, qErr := queryHelper(t, keeper, ctx, contractAddr, string(msg), true, tc.gasLimit) + require.NotEmpty(t, qErr) + require.NotNil(t, qErr.GenericErr) + require.Contains(t, qErr.GenericErr.Msg, "Querier system error: Query recursion limit exceeded") + return + } + // otherwise, we expect a successful call _, qErr := queryHelper(t, keeper, ctx, contractAddr, string(msg), true, tc.gasLimit) require.Empty(t, qErr) diff --git a/x/compute/internal/keeper/secret_contracts_test.go b/x/compute/internal/keeper/secret_contracts_test.go index ad2459180..9ec12f178 100644 --- a/x/compute/internal/keeper/secret_contracts_test.go +++ b/x/compute/internal/keeper/secret_contracts_test.go @@ -1278,7 +1278,8 @@ func TestMsgSenderInCallback(t *testing.T) { require.Equal(t, []ContractEvent{ { {Key: "contract_address", Value: addr.String()}, - {Key: "hi", Value: "hey"}}, + {Key: "hi", Value: "hey"}, + }, { {Key: "contract_address", Value: addr.String()}, {Key: "msg.sender", Value: addr.String()}, @@ -1287,6 +1288,7 @@ func TestMsgSenderInCallback(t *testing.T) { } func TestInfiniteQueryLoopKilledGracefullyByOOM(t *testing.T) { + t.SkipNow() // We no longer expect to hit OOM trivially ctx, keeper, tempDir, codeID, codeHash, walletA, privKeyA, _, _ := setupTest(t, "./testdata/test-contract/contract.wasm") defer os.RemoveAll(tempDir) @@ -1300,6 +1302,58 @@ func TestInfiniteQueryLoopKilledGracefullyByOOM(t *testing.T) { require.Equal(t, err.GenericErr.Msg, "query contract failed: Execution error: Enclave: enclave ran out of heap memory") } +func TestQueryRecursionLimitEnforcedInQueries(t *testing.T) { + ctx, keeper, tempDir, codeID, codeHash, walletA, privKeyA, _, _ := setupTest(t, "./testdata/test-contract/contract.wasm") + defer os.RemoveAll(tempDir) + + addr, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, defaultGasForTests) + require.Empty(t, err) + + data, err := queryHelper(t, keeper, ctx, addr, fmt.Sprintf(`{"send_external_query_recursion_limit":{"to":"%s","code_hash":"%s", "depth":1}}`, addr.String(), codeHash), true, defaultGasForTests) + + require.NotEmpty(t, data) + require.Equal(t, data, "\"Recursion limit was correctly enforced\"") + + require.Nil(t, err.GenericErr) +} + +func TestQueryRecursionLimitEnforcedInHandles(t *testing.T) { + ctx, keeper, tempDir, codeID, codeHash, walletA, privKeyA, _, _ := setupTest(t, "./testdata/test-contract/contract.wasm") + defer os.RemoveAll(tempDir) + + addr, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, defaultGasForTests) + require.Empty(t, err) + + data, _, err := execHelper(t, keeper, ctx, addr, walletA, privKeyA, fmt.Sprintf(`{"send_external_query_recursion_limit":{"to":"%s","code_hash":"%s", "depth":1}}`, addr.String(), codeHash), true, defaultGasForTests, 0) + + require.NotEmpty(t, data) + require.Equal(t, string(data), "\"Recursion limit was correctly enforced\"") + + require.Nil(t, err.GenericErr) +} + +func TestQueryRecursionLimitEnforcedInInits(t *testing.T) { + ctx, keeper, tempDir, codeID, codeHash, walletA, privKeyA, _, _ := setupTest(t, "./testdata/test-contract/contract.wasm") + defer os.RemoveAll(tempDir) + + // Initialize a contract that we will be querying + addr, _, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, `{"nop":{}}`, true, defaultGasForTests) + require.Empty(t, err) + + // Initialize the contract that will be running the test + addr, events, err := initHelper(t, keeper, ctx, codeID, walletA, privKeyA, fmt.Sprintf(`{"send_external_query_recursion_limit":{"to":"%s","code_hash":"%s", "depth":1}}`, addr.String(), codeHash), true, defaultGasForTests) + require.Empty(t, err) + + require.Nil(t, err.GenericErr) + + require.Equal(t, []ContractEvent{ + { + {Key: "contract_address", Value: addr.String()}, + {Key: "message", Value: "Recursion limit was correctly enforced"}, + }, + }, events) +} + func TestWriteToStorageDuringQuery(t *testing.T) { ctx, keeper, tempDir, codeID, _, walletA, privKeyA, _, _ := setupTest(t, "./testdata/test-contract/contract.wasm") defer os.RemoveAll(tempDir) diff --git a/x/compute/internal/keeper/testdata/burner.wasm b/x/compute/internal/keeper/testdata/burner.wasm index 84d92113a..ed5bbd5e9 100644 Binary files a/x/compute/internal/keeper/testdata/burner.wasm and b/x/compute/internal/keeper/testdata/burner.wasm differ diff --git a/x/compute/internal/keeper/testdata/contract.wasm b/x/compute/internal/keeper/testdata/contract.wasm index 053954be2..58fbb1cd0 100644 Binary files a/x/compute/internal/keeper/testdata/contract.wasm and b/x/compute/internal/keeper/testdata/contract.wasm differ diff --git a/x/compute/internal/keeper/testdata/contract.wasm.gzip b/x/compute/internal/keeper/testdata/contract.wasm.gzip index 096adb6a5..62388abfc 100644 Binary files a/x/compute/internal/keeper/testdata/contract.wasm.gzip and b/x/compute/internal/keeper/testdata/contract.wasm.gzip differ diff --git a/x/compute/internal/keeper/testdata/dist.wasm b/x/compute/internal/keeper/testdata/dist.wasm index 2176d09b5..d8222afd7 100644 Binary files a/x/compute/internal/keeper/testdata/dist.wasm and b/x/compute/internal/keeper/testdata/dist.wasm differ diff --git a/x/compute/internal/keeper/testdata/erc20.wasm b/x/compute/internal/keeper/testdata/erc20.wasm index 51a0be32b..80ffeddfb 100644 Binary files a/x/compute/internal/keeper/testdata/erc20.wasm and b/x/compute/internal/keeper/testdata/erc20.wasm differ diff --git a/x/compute/internal/keeper/testdata/gov.wasm b/x/compute/internal/keeper/testdata/gov.wasm index 2cd397ece..37eacf599 100644 Binary files a/x/compute/internal/keeper/testdata/gov.wasm and b/x/compute/internal/keeper/testdata/gov.wasm differ diff --git a/x/compute/internal/keeper/testdata/mint.wasm b/x/compute/internal/keeper/testdata/mint.wasm index 8486c673e..2daf99564 100644 Binary files a/x/compute/internal/keeper/testdata/mint.wasm and b/x/compute/internal/keeper/testdata/mint.wasm differ diff --git a/x/compute/internal/keeper/testdata/reflect.wasm b/x/compute/internal/keeper/testdata/reflect.wasm index 97218d224..9a267b31d 100644 Binary files a/x/compute/internal/keeper/testdata/reflect.wasm and b/x/compute/internal/keeper/testdata/reflect.wasm differ diff --git a/x/compute/internal/keeper/testdata/staking.wasm b/x/compute/internal/keeper/testdata/staking.wasm index 4287a2231..2f6709925 100644 Binary files a/x/compute/internal/keeper/testdata/staking.wasm and b/x/compute/internal/keeper/testdata/staking.wasm differ diff --git a/x/compute/internal/keeper/testdata/test-contract/contract.wasm b/x/compute/internal/keeper/testdata/test-contract/contract.wasm index 289892cbf..a178c64ff 100644 Binary files a/x/compute/internal/keeper/testdata/test-contract/contract.wasm and b/x/compute/internal/keeper/testdata/test-contract/contract.wasm differ diff --git a/x/compute/internal/keeper/testdata/test-contract/contract_with_floats.wasm b/x/compute/internal/keeper/testdata/test-contract/contract_with_floats.wasm index 547e43634..aefde53f9 100644 Binary files a/x/compute/internal/keeper/testdata/test-contract/contract_with_floats.wasm and b/x/compute/internal/keeper/testdata/test-contract/contract_with_floats.wasm differ diff --git a/x/compute/internal/keeper/testdata/test-contract/src/contract.rs b/x/compute/internal/keeper/testdata/test-contract/src/contract.rs index 792cdaef0..06661a3a5 100644 --- a/x/compute/internal/keeper/testdata/test-contract/src/contract.rs +++ b/x/compute/internal/keeper/testdata/test-contract/src/contract.rs @@ -44,6 +44,11 @@ pub enum InitMsg { depth: u8, code_hash: String, }, + SendExternalQueryRecursionLimit { + to: HumanAddr, + depth: u8, + code_hash: String, + }, CallToInit { code_id: u64, code_hash: String, @@ -171,6 +176,11 @@ pub enum HandleMsg { code_hash: String, depth: u8, }, + SendExternalQueryRecursionLimit { + to: HumanAddr, + code_hash: String, + depth: u8, + }, WithFloats { x: u8, y: u8, @@ -214,6 +224,11 @@ pub enum QueryMsg { depth: u8, code_hash: String, }, + SendExternalQueryRecursionLimit { + to: HumanAddr, + depth: u8, + code_hash: String, + }, CallToQuery { addr: HumanAddr, code_hash: String, @@ -265,6 +280,17 @@ pub fn init( "", )], }), + InitMsg::SendExternalQueryRecursionLimit { + to, + depth, + code_hash, + } => Ok(InitResponse { + messages: vec![], + log: vec![log( + "message", + send_external_query_recursion_limit(deps, to, depth, code_hash)?, + )], + }), InitMsg::CallToInit { code_id, code_hash, @@ -457,6 +483,17 @@ pub fn handle( .into(), ), }), + HandleMsg::SendExternalQueryRecursionLimit { + to, + code_hash, + depth, + } => Ok(HandleResponse { + messages: vec![], + log: vec![], + data: Some(to_binary(&send_external_query_recursion_limit( + deps, to, depth, code_hash, + )?)?), + }), HandleMsg::SendExternalQueryPanic { to, code_hash } => { send_external_query_panic(deps, to, code_hash) } @@ -669,6 +706,45 @@ fn send_external_query_depth_counter( answer + 1 } +fn send_external_query_recursion_limit( + deps: &Extern, + contract_addr: HumanAddr, + depth: u8, + code_hash: String, +) -> StdResult { + let result = deps + .querier + .query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: contract_addr.clone(), + callback_code_hash: code_hash.clone(), + msg: Binary( + format!( + r#"{{"send_external_query_recursion_limit":{{"to":"{}","code_hash":"{}","depth":{}}}}}"#, + contract_addr.clone().to_string(), + code_hash.clone().to_string(), + depth + 1 + ) + .into_bytes(), + ), + })); + + // 5 is the current recursion limit. + if depth != 5 { + result + } else { + match result { + Err(StdError::GenericErr { msg, .. }) + if msg == "Querier system error: Query recursion limit exceeded" => + { + Ok(String::from("Recursion limit was correctly enforced")) + } + _ => Err(StdError::generic_err( + "Recursion limit was bypassed! this is a bug!", + )), + } + } +} + fn send_external_query_panic( deps: &mut Extern, contract_addr: HumanAddr, @@ -1106,6 +1182,13 @@ pub fn query( deps, to, depth, code_hash, )) .unwrap()), + QueryMsg::SendExternalQueryRecursionLimit { + to, + depth, + code_hash, + } => to_binary(&send_external_query_recursion_limit( + deps, to, depth, code_hash, + )?), QueryMsg::CallToQuery { addr, code_hash, diff --git a/x/compute/internal/keeper/testdata/test-contract/static-too-high-initial-memory.wasm b/x/compute/internal/keeper/testdata/test-contract/static-too-high-initial-memory.wasm index 9bfa97969..f6da6ad28 100644 Binary files a/x/compute/internal/keeper/testdata/test-contract/static-too-high-initial-memory.wasm and b/x/compute/internal/keeper/testdata/test-contract/static-too-high-initial-memory.wasm differ diff --git a/x/compute/internal/keeper/testdata/test-contract/too-high-initial-memory.wasm b/x/compute/internal/keeper/testdata/test-contract/too-high-initial-memory.wasm index 51099ccf6..6218dc2f4 100644 Binary files a/x/compute/internal/keeper/testdata/test-contract/too-high-initial-memory.wasm and b/x/compute/internal/keeper/testdata/test-contract/too-high-initial-memory.wasm differ