Repository: CosmWasm/cosmwasm
Pull request #954 (2021-06-14 14:08:47)

Increase read limits for contract results
and make the distiction between read limit and deserialization limit very clear.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 367cfdd7d..c85c13f0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,13 @@ and this project adheres to
 
 - cosmwasm-std: Rename the `send` function parameter to `funds` in `WasmMsg` for
   consistency with the wasmd message types.
+- cosmwasm-vm: Increase read limit of contract execution results from 100,000
+  bytes to 64 MiB. JSON deserializers should have their own limit to protect
+  against large deserializations.
+- cosmwasm-vm: Create `VmError::DeserializationLimitExceeded`; Add limit
+  argument to `from_slice`; Increase deserialization limit of contract execution
+  results from 100,000 bytes to 256 KiB. This probably only affects internal
+  testing as well as integration tests of smart contracts.
 
 ## [0.14.1] - 2021-06-14
 
diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs
index 2199f5d95..a875ca5cd 100644
--- a/packages/vm/src/calls.rs
+++ b/packages/vm/src/calls.rs
@@ -15,36 +15,85 @@ use crate::errors::{VmError, VmResult};
 use crate::instance::Instance;
 use crate::serde::{from_slice, to_vec};
 
-/// Max length (in bytes) of the result data from an instantiate call.
-const RESULT_LIMIT_INSTANTIATE: usize = 100_000;
-/// Max length (in bytes) of the result data from an execute call.
-const RESULT_LIMIT_EXECUTE: usize = 100_000;
-/// Max length (in bytes) of the result data from a migrate call.
-const RESULT_LIMIT_MIGRATE: usize = 100_000;
-/// Max length (in bytes) of the result data from a sudo call.
-const RESULT_LIMIT_SUDO: usize = 100_000;
-/// Max length (in bytes) of the result data from a reply call.
-const RESULT_LIMIT_REPLY: usize = 100_000;
-/// Max length (in bytes) of the result data from a query call.
-const RESULT_LIMIT_QUERY: usize = 100_000;
-/// Max length (in bytes) of the result data from a ibc_channel_open call.
-#[cfg(feature = "stargate")]
-const RESULT_LIMIT_IBC_CHANNEL_OPEN: usize = 100_000;
-/// Max length (in bytes) of the result data from a ibc_channel_connect call.
-#[cfg(feature = "stargate")]
-const RESULT_LIMIT_IBC_CHANNEL_CONNECT: usize = 100_000;
-/// Max length (in bytes) of the result data from a ibc_channel_close call.
-#[cfg(feature = "stargate")]
-const RESULT_LIMIT_IBC_CHANNEL_CLOSE: usize = 100_000;
-/// Max length (in bytes) of the result data from a ibc_packet_receive call.
-#[cfg(feature = "stargate")]
-const RESULT_LIMIT_IBC_PACKET_RECEIVE: usize = 100_000;
-/// Max length (in bytes) of the result data from a ibc_packet_ack call.
-#[cfg(feature = "stargate")]
-const RESULT_LIMIT_IBC_PACKET_ACK: usize = 100_000;
-/// Max length (in bytes) of the result data from a ibc_packet_timeout call.
-#[cfg(feature = "stargate")]
-const RESULT_LIMIT_IBC_PACKET_TIMEOUT: usize = 100_000;
+/// The limits in here protect the host from allocating an unreasonable amount of memory
+/// and copying an unreasonable amount of data.
+///
+/// A JSON deserializer would want to set the limit to a much smaller value because
+/// deserializing JSON is more expensive. As a consequence, any sane contract should hit
+/// the deserializer limit before the read limit.
+mod read_limits {
+    /// A mibi (mega binary)
+    const MI: usize = 1024 * 1024;
+    /// Max length (in bytes) of the result data from an instantiate call.
+    pub const RESULT_INSTANTIATE: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from an execute call.
+    pub const RESULT_EXECUTE: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a migrate call.
+    pub const RESULT_MIGRATE: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a sudo call.
+    pub const RESULT_SUDO: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a reply call.
+    pub const RESULT_REPLY: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a query call.
+    pub const RESULT_QUERY: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a ibc_channel_open call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_CHANNEL_OPEN: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a ibc_channel_connect call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_CHANNEL_CONNECT: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a ibc_channel_close call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_CHANNEL_CLOSE: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a ibc_packet_receive call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_PACKET_RECEIVE: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a ibc_packet_ack call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_PACKET_ACK: usize = 64 * MI;
+    /// Max length (in bytes) of the result data from a ibc_packet_timeout call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_PACKET_TIMEOUT: usize = 64 * MI;
+}
+
+/// The limits for the JSON deserialization.
+///
+/// Those limits are not used when the Rust JSON deserializer is bypassed by using the
+/// public `call_*_raw` functions directly.
+mod deserialization_limits {
+    /// A kibi (kilo binary)
+    const KI: usize = 1024;
+    /// Max length (in bytes) of the result data from an instantiate call.
+    pub const RESULT_INSTANTIATE: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from an execute call.
+    pub const RESULT_EXECUTE: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a migrate call.
+    pub const RESULT_MIGRATE: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a sudo call.
+    pub const RESULT_SUDO: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a reply call.
+    pub const RESULT_REPLY: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a query call.
+    pub const RESULT_QUERY: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a ibc_channel_open call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_CHANNEL_OPEN: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a ibc_channel_connect call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_CHANNEL_CONNECT: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a ibc_channel_close call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_CHANNEL_CLOSE: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a ibc_packet_receive call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_PACKET_RECEIVE: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a ibc_packet_ack call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_PACKET_ACK: usize = 256 * KI;
+    /// Max length (in bytes) of the result data from a ibc_packet_timeout call.
+    #[cfg(feature = "stargate")]
+    pub const RESULT_IBC_PACKET_TIMEOUT: usize = 256 * KI;
+}
 
 pub fn call_instantiate<A, S, Q, U>(
     instance: &mut Instance<A, S, Q>,
@@ -61,7 +110,8 @@ where
     let env = to_vec(env)?;
     let info = to_vec(info)?;
     let data = call_instantiate_raw(instance, &env, &info, msg)?;
-    let result: ContractResult<Response<U>> = from_slice(&data)?;
+    let result: ContractResult<Response<U>> =
+        from_slice(&data, deserialization_limits::RESULT_INSTANTIATE)?;
     Ok(result)
 }
 
@@ -80,7 +130,8 @@ where
     let env = to_vec(env)?;
     let info = to_vec(info)?;
     let data = call_execute_raw(instance, &env, &info, msg)?;
-    let result: ContractResult<Response<U>> = from_slice(&data)?;
+    let result: ContractResult<Response<U>> =
+        from_slice(&data, deserialization_limits::RESULT_EXECUTE)?;
     Ok(result)
 }
 
@@ -97,7 +148,8 @@ where
 {
     let env = to_vec(env)?;
     let data = call_migrate_raw(instance, &env, msg)?;
-    let result: ContractResult<Response<U>> = from_slice(&data)?;
+    let result: ContractResult<Response<U>> =
+        from_slice(&data, deserialization_limits::RESULT_MIGRATE)?;
     Ok(result)
 }
 
@@ -114,7 +166,8 @@ where
 {
     let env = to_vec(env)?;
     let data = call_sudo_raw(instance, &env, msg)?;
-    let result: ContractResult<Response<U>> = from_slice(&data)?;
+    let result: ContractResult<Response<U>> =
+        from_slice(&data, deserialization_limits::RESULT_SUDO)?;
     Ok(result)
 }
 
@@ -132,7 +185,8 @@ where
     let env = to_vec(env)?;
     let msg = to_vec(msg)?;
     let data = call_reply_raw(instance, &env, &msg)?;
-    let result: ContractResult<Response<U>> = from_slice(&data)?;
+    let result: ContractResult<Response<U>> =
+        from_slice(&data, deserialization_limits::RESULT_REPLY)?;
     Ok(result)
 }
 
@@ -148,7 +202,8 @@ where
 {
     let env = to_vec(env)?;
     let data = call_query_raw(instance, &env, msg)?;
-    let result: ContractResult<QueryResponse> = from_slice(&data)?;
+    let result: ContractResult<QueryResponse> =
+        from_slice(&data, deserialization_limits::RESULT_QUERY)?;
     // Ensure query response is valid JSON
     if let ContractResult::Ok(binary_response) = &result {
         serde_json::from_slice::<serde_json::Value>(binary_response.as_slice()).map_err(|e| {
@@ -173,7 +228,8 @@ where
     let env = to_vec(env)?;
     let channel = to_vec(channel)?;
     let data = call_ibc_channel_open_raw(instance, &env, &channel)?;
-    let result: ContractResult<()> = from_slice(&data)?;
+    let result: ContractResult<()> =
+        from_slice(&data, deserialization_limits::RESULT_IBC_CHANNEL_OPEN)?;
     Ok(result)
 }
 
@@ -192,7 +248,7 @@ where
     let env = to_vec(env)?;
     let channel = to_vec(channel)?;
     let data = call_ibc_channel_connect_raw(instance, &env, &channel)?;
-    let result = from_slice(&data)?;
+    let result = from_slice(&data, deserialization_limits::RESULT_IBC_CHANNEL_CONNECT)?;
     Ok(result)
 }
 
@@ -211,7 +267,7 @@ where
     let env = to_vec(env)?;
     let channel = to_vec(channel)?;
     let data = call_ibc_channel_close_raw(instance, &env, &channel)?;
-    let result = from_slice(&data)?;
+    let result = from_slice(&data, deserialization_limits::RESULT_IBC_CHANNEL_CLOSE)?;
     Ok(result)
 }
 
@@ -230,7 +286,7 @@ where
     let env = to_vec(env)?;
     let packet = to_vec(packet)?;
     let data = call_ibc_packet_receive_raw(instance, &env, &packet)?;
-    let result = from_slice(&data)?;
+    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_RECEIVE)?;
     Ok(result)
 }
 
@@ -249,7 +305,7 @@ where
     let env = to_vec(env)?;
     let ack = to_vec(ack)?;
     let data = call_ibc_packet_ack_raw(instance, &env, &ack)?;
-    let result = from_slice(&data)?;
+    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_ACK)?;
     Ok(result)
 }
 
@@ -268,7 +324,7 @@ where
     let env = to_vec(env)?;
     let packet = to_vec(packet)?;
     let data = call_ibc_packet_timeout_raw(instance, &env, &packet)?;
-    let result = from_slice(&data)?;
+    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_TIMEOUT)?;
     Ok(result)
 }
 
@@ -290,7 +346,7 @@ where
         instance,
         "instantiate",
         &[env, info, msg],
-        RESULT_LIMIT_INSTANTIATE,
+        read_limits::RESULT_INSTANTIATE,
     )
 }
 
@@ -308,7 +364,12 @@ where
     Q: Querier + 'static,
 {
     instance.set_storage_readonly(false);
-    call_raw(instance, "execute", &[env, info, msg], RESULT_LIMIT_EXECUTE)
+    call_raw(
+        instance,
+        "execute",
+        &[env, info, msg],
+        read_limits::RESULT_EXECUTE,
+    )
 }
 
 /// Calls Wasm export "migrate" and returns raw data from the contract.
@@ -324,7 +385,12 @@ where
     Q: Querier + 'static,
 {
     instance.set_storage_readonly(false);
-    call_raw(instance, "migrate", &[env, msg], RESULT_LIMIT_MIGRATE)
+    call_raw(
+        instance,
+        "migrate",
+        &[env, msg],
+        read_limits::RESULT_MIGRATE,
+    )
 }
 
 /// Calls Wasm export "sudo" and returns raw data from the contract.
@@ -340,7 +406,7 @@ where
     Q: Querier + 'static,
 {
     instance.set_storage_readonly(false);
-    call_raw(instance, "sudo", &[env, msg], RESULT_LIMIT_SUDO)
+    call_raw(instance, "sudo", &[env, msg], read_limits::RESULT_SUDO)
 }
 
 /// Calls Wasm export "reply" and returns raw data from the contract.
@@ -356,7 +422,7 @@ where
     Q: Querier + 'static,
 {
     instance.set_storage_readonly(false);
-    call_raw(instance, "reply", &[env, msg], RESULT_LIMIT_REPLY)
+    call_raw(instance, "reply", &[env, msg], read_limits::RESULT_REPLY)
 }
 
 /// Calls Wasm export "query" and returns raw data from the contract.
@@ -372,7 +438,7 @@ where
     Q: Querier + 'static,
 {
     instance.set_storage_readonly(true);
-    call_raw(instance, "query", &[env, msg], RESULT_LIMIT_QUERY)
+    call_raw(instance, "query", &[env, msg], read_limits::RESULT_QUERY)
 }
 
 #[cfg(feature = "stargate")]
@@ -391,7 +457,7 @@ where
         instance,
         "ibc_channel_open",
         &[env, channel],
-        RESULT_LIMIT_IBC_CHANNEL_OPEN,
+        read_limits::RESULT_IBC_CHANNEL_OPEN,
     )
 }
 
@@ -411,7 +477,7 @@ where
         instance,
         "ibc_channel_connect",
         &[env, channel],
-        RESULT_LIMIT_IBC_CHANNEL_CONNECT,
+        read_limits::RESULT_IBC_CHANNEL_CONNECT,
     )
 }
 
@@ -431,7 +497,7 @@ where
         instance,
         "ibc_channel_close",
         &[env, channel],
-        RESULT_LIMIT_IBC_CHANNEL_CLOSE,
+        read_limits::RESULT_IBC_CHANNEL_CLOSE,
     )
 }
 
@@ -451,7 +517,7 @@ where
         instance,
         "ibc_packet_receive",
         &[env, packet],
-        RESULT_LIMIT_IBC_PACKET_RECEIVE,
+        read_limits::RESULT_IBC_PACKET_RECEIVE,
     )
 }
 
@@ -471,7 +537,7 @@ where
         instance,
         "ibc_packet_ack",
         &[env, ack],
-        RESULT_LIMIT_IBC_PACKET_ACK,
+        read_limits::RESULT_IBC_PACKET_ACK,
     )
 }
 
@@ -491,7 +557,7 @@ where
         instance,
         "ibc_packet_timeout",
         &[env, packet],
-        RESULT_LIMIT_IBC_PACKET_TIMEOUT,
+        read_limits::RESULT_IBC_PACKET_TIMEOUT,
     )
 }
 
diff --git a/packages/vm/src/errors/vm_error.rs b/packages/vm/src/errors/vm_error.rs
index d1d0bea81..b9bb506c7 100644
--- a/packages/vm/src/errors/vm_error.rs
+++ b/packages/vm/src/errors/vm_error.rs
@@ -81,6 +81,14 @@ pub enum VmError {
         #[cfg(feature = "backtraces")]
         backtrace: Backtrace,
     },
+    #[error("Data too long for deserialization. Got: {length} bytes; limit: {max_length} bytes")]
+    DeserializationLimitExceeded {
+        /// the target type that was attempted
+        length: usize,
+        max_length: usize,
+        #[cfg(feature = "backtraces")]
+        backtrace: Backtrace,
+    },
     #[error("Error serializing type {source_type}: {msg}")]
     SerializeErr {
         /// the source type that was attempted
@@ -219,6 +227,15 @@ impl VmError {
         }
     }
 
+    pub(crate) fn deserialization_limit_exceeded(length: usize, max_length: usize) -> Self {
+        VmError::DeserializationLimitExceeded {
+            length,
+            max_length,
+            #[cfg(feature = "backtraces")]
+            backtrace: Backtrace::capture(),
+        }
+    }
+
     pub(crate) fn serialize_err<S: Into<String>, M: Display>(source: S, msg: M) -> Self {
         VmError::SerializeErr {
             source_type: source.into(),
diff --git a/packages/vm/src/serde.rs b/packages/vm/src/serde.rs
index 8e1640028..c884dd1b8 100644
--- a/packages/vm/src/serde.rs
+++ b/packages/vm/src/serde.rs
@@ -7,10 +7,20 @@ use std::any::type_name;
 
 use crate::errors::{VmError, VmResult};
 
-pub fn from_slice<'a, T>(value: &'a [u8]) -> VmResult<T>
+/// Deserializes JSON data into a document of type `T`.
+///
+/// The deserialization limit ensure it is not possible to slow down the execution by
+/// providing overly large JSON documents.
+pub fn from_slice<'a, T>(value: &'a [u8], deserialization_limit: usize) -> VmResult<T>
 where
     T: Deserialize<'a>,
 {
+    if value.len() > deserialization_limit {
+        return Err(VmError::deserialization_limit_exceeded(
+            value.len(),
+            deserialization_limit,
+        ));
+    }
     serde_json::from_slice(value).map_err(|e| VmError::parse_err(type_name::<T>(), e))
 }
 
@@ -20,3 +30,100 @@ where
 {
     serde_json::to_vec(data).map_err(|e| VmError::serialize_err(type_name::<T>(), e))
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use serde::Deserialize;
+
+    const LIMIT: usize = 20_000;
+
+    #[derive(Serialize, Deserialize, Debug, PartialEq)]
+    #[serde(rename_all = "snake_case")]
+    enum SomeMsg {
+        Refund {},
+        ReleaseAll {
+            image: String,
+            amount: u32,
+            time: u64,
+            karma: i32,
+        },
+        Cowsay {
+            text: String,
+        },
+    }
+
+    #[test]
+    fn from_slice_works() {
+        let deserialized: SomeMsg = from_slice(br#"{"refund":{}}"#, LIMIT).unwrap();
+        assert_eq!(deserialized, SomeMsg::Refund {});
+
+        let deserialized: SomeMsg = from_slice(
+            br#"{"release_all":{"image":"foo","amount":42,"time":18446744073709551615,"karma":-17}}"#, LIMIT
+        )
+        .unwrap();
+        assert_eq!(
+            deserialized,
+            SomeMsg::ReleaseAll {
+                image: "foo".to_string(),
+                amount: 42,
+                time: 18446744073709551615,
+                karma: -17
+            }
+        );
+    }
+
+    #[test]
+    fn from_slice_works_for_special_chars() {
+        let deserialized: SomeMsg =
+            from_slice(br#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#, LIMIT).unwrap();
+        assert_eq!(
+            deserialized,
+            SomeMsg::Cowsay {
+                text: "foo\"bar\\\"bla".to_string(),
+            }
+        );
+    }
+
+    #[test]
+    fn from_slice_errors_when_exceeding_deserialization_limit() {
+        let result = from_slice::<SomeMsg>(br#"{"refund":{}}"#, 5);
+        match result.unwrap_err() {
+            VmError::DeserializationLimitExceeded {
+                length, max_length, ..
+            } => {
+                assert_eq!(length, 13);
+                assert_eq!(max_length, 5);
+            }
+            err => panic!("Unexpected error: {:?}", err),
+        }
+    }
+
+    #[test]
+    fn to_vec_works() {
+        let msg = SomeMsg::Refund {};
+        let serialized = to_vec(&msg).unwrap();
+        assert_eq!(serialized, br#"{"refund":{}}"#);
+
+        let msg = SomeMsg::ReleaseAll {
+            image: "foo".to_string(),
+            amount: 42,
+            time: 9007199254740999, // Number.MAX_SAFE_INTEGER + 7
+            karma: -17,
+        };
+        let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap();
+        assert_eq!(
+            serialized,
+            r#"{"release_all":{"image":"foo","amount":42,"time":9007199254740999,"karma":-17}}"#
+        );
+    }
+
+    #[test]
+    fn to_vec_works_for_special_chars() {
+        let msg = SomeMsg::Cowsay {
+            text: "foo\"bar\\\"bla".to_string(),
+        };
+        let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap();
+        assert_eq!(serialized, r#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#);
+    }
+}


Test patch:
diff --git a/contracts/crypto-verify/tests/integration.rs b/contracts/crypto-verify/tests/integration.rs
index 494b5e03d..4cdf2f374 100644
--- a/contracts/crypto-verify/tests/integration.rs
+++ b/contracts/crypto-verify/tests/integration.rs
@@ -54,6 +54,8 @@ const ED25519_SIGNATURE2_HEX: &str = "92a009a9f0d4cab8720e820b5f642540a2b27b5416
 const ED25519_PUBLIC_KEY2_HEX: &str =
     "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c";
 
+const DESERIALIZATION_LIMIT: usize = 20_000;
+
 fn setup() -> Instance<MockApi, MockStorage, MockQuerier> {
     let mut deps = mock_instance(WASM, &[]);
     let msg = InstantiateMsg {};
@@ -83,7 +85,7 @@ fn cosmos_signature_verify_works() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: true });
 }
@@ -105,7 +107,7 @@ fn cosmos_signature_verify_fails() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: false });
 }
@@ -144,7 +146,7 @@ fn ethereum_signature_verify_works() {
         signer_address: signer_address.into(),
     };
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: true });
 }
@@ -164,7 +166,7 @@ fn ethereum_signature_verify_fails_for_corrupted_message() {
         signer_address: signer_address.into(),
     };
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: false });
 }
@@ -185,7 +187,7 @@ fn ethereum_signature_verify_fails_for_corrupted_signature() {
         signer_address: signer_address.into(),
     };
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(res, VerifyResponse { verifies: false });
 
     // Broken signature
@@ -247,7 +249,7 @@ fn verify_ethereum_transaction_works() {
         v,
     };
     let raw = query(&mut deps, mock_env(), msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(res, VerifyResponse { verifies: true });
 }
 
@@ -266,7 +268,7 @@ fn tendermint_signature_verify_works() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: true });
 }
@@ -288,7 +290,7 @@ fn tendermint_signature_verify_fails() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: false });
 }
@@ -337,7 +339,7 @@ fn tendermint_signatures_batch_verify_works() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: true });
 }
@@ -370,7 +372,7 @@ fn tendermint_signatures_batch_verify_message_multisig_works() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: true });
 }
@@ -403,7 +405,7 @@ fn tendermint_signatures_batch_verify_single_public_key_works() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: true });
 }
@@ -434,7 +436,7 @@ fn tendermint_signatures_batch_verify_fails() {
     };
 
     let raw = query(&mut deps, mock_env(), verify_msg).unwrap();
-    let res: VerifyResponse = from_slice(&raw).unwrap();
+    let res: VerifyResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(res, VerifyResponse { verifies: false });
 }
@@ -476,7 +478,7 @@ fn query_works() {
     let query_msg = QueryMsg::ListVerificationSchemes {};
 
     let raw = query(&mut deps, mock_env(), query_msg).unwrap();
-    let res: ListVerificationsResponse = from_slice(&raw).unwrap();
+    let res: ListVerificationsResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
 
     assert_eq!(
         res,
diff --git a/contracts/hackatom/tests/integration.rs b/contracts/hackatom/tests/integration.rs
index 64a353a42..1d535a1a7 100644
--- a/contracts/hackatom/tests/integration.rs
+++ b/contracts/hackatom/tests/integration.rs
@@ -35,6 +35,8 @@ use hackatom::state::{State, CONFIG_KEY};
 
 static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/hackatom.wasm");
 
+const DESERIALIZATION_LIMIT: usize = 20_000;
+
 fn make_init_msg() -> (InstantiateMsg, String) {
     let verifier = String::from("verifies");
     let beneficiary = String::from("benefits");
@@ -81,7 +83,7 @@ fn proper_initialization() {
                 .0
                 .expect("error reading db")
                 .expect("no data stored");
-            from_slice(&data)
+            from_slice(&data, DESERIALIZATION_LIMIT)
         })
         .unwrap();
     assert_eq!(state, expected_state);
@@ -296,7 +298,7 @@ fn execute_release_fails_for_wrong_sender() {
                 .expect("no data stored"))
         })
         .unwrap();
-    let state: State = from_slice(&data).unwrap();
+    let state: State = from_slice(&data, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(
         state,
         State {
diff --git a/contracts/ibc-reflect-send/tests/integration.rs b/contracts/ibc-reflect-send/tests/integration.rs
index 60267c994..ff881ddc1 100644
--- a/contracts/ibc-reflect-send/tests/integration.rs
+++ b/contracts/ibc-reflect-send/tests/integration.rs
@@ -38,6 +38,8 @@ static WASM: &[u8] =
 
 const CREATOR: &str = "creator";
 
+const DESERIALIZATION_LIMIT: usize = 20_000;
+
 fn setup() -> Instance<MockApi, MockStorage, MockQuerier> {
     let mut deps = mock_instance(WASM, &[]);
     let msg = InstantiateMsg {};
@@ -92,7 +94,7 @@ fn who_am_i_response<T: Into<String>>(
 fn instantiate_works() {
     let mut deps = setup();
     let r = query(&mut deps, mock_env(), QueryMsg::Admin {}).unwrap();
-    let admin: AdminResponse = from_slice(&r).unwrap();
+    let admin: AdminResponse = from_slice(&r, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(CREATOR, admin.admin.as_str());
 }
 
@@ -118,7 +120,7 @@ fn get_account(
         channel_id: channel_id.into(),
     };
     let r = query(deps, mock_env(), msg).unwrap();
-    from_slice(&r).unwrap()
+    from_slice(&r, DESERIALIZATION_LIMIT).unwrap()
 }
 
 #[test]
diff --git a/contracts/ibc-reflect/tests/integration.rs b/contracts/ibc-reflect/tests/integration.rs
index 54f5da742..c85f3ff92 100644
--- a/contracts/ibc-reflect/tests/integration.rs
+++ b/contracts/ibc-reflect/tests/integration.rs
@@ -43,6 +43,8 @@ const REFLECT_ID: u64 = 101;
 // address of first reflect contract instance that we created
 const REFLECT_ADDR: &str = "reflect-acct-1";
 
+const DESERIALIZATION_LIMIT: usize = 20_000;
+
 fn setup() -> Instance<MockApi, MockStorage, MockQuerier> {
     let mut deps = mock_instance(WASM, &[]);
     let msg = InstantiateMsg {
@@ -159,7 +161,7 @@ fn proper_handshake_flow() {
 
     // no accounts set yet
     let raw = query(&mut deps, mock_env(), QueryMsg::ListAccounts {}).unwrap();
-    let res: ListAccountsResponse = from_slice(&raw).unwrap();
+    let res: ListAccountsResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(0, res.accounts.len());
 
     // we get the callback from reflect
@@ -175,7 +177,7 @@ fn proper_handshake_flow() {
 
     // ensure this is now registered
     let raw = query(&mut deps, mock_env(), QueryMsg::ListAccounts {}).unwrap();
-    let res: ListAccountsResponse = from_slice(&raw).unwrap();
+    let res: ListAccountsResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(1, res.accounts.len());
     assert_eq!(
         &res.accounts[0],
@@ -194,7 +196,7 @@ fn proper_handshake_flow() {
         },
     )
     .unwrap();
-    let res: AccountResponse = from_slice(&raw).unwrap();
+    let res: AccountResponse = from_slice(&raw, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(res.account.unwrap(), REFLECT_ADDR);
 }
 
@@ -219,7 +221,8 @@ fn handle_dispatch_packet() {
     // we didn't dispatch anything
     assert_eq!(0, res.messages.len());
     // acknowledgement is an error
-    let ack: AcknowledgementMsg<DispatchResponse> = from_slice(&res.acknowledgement).unwrap();
+    let ack: AcknowledgementMsg<DispatchResponse> =
+        from_slice(&res.acknowledgement, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(
         ack.unwrap_err(),
         "invalid packet: cosmwasm_std::addresses::Addr not found"
@@ -237,7 +240,8 @@ fn handle_dispatch_packet() {
     );
 
     // assert app-level success
-    let ack: AcknowledgementMsg<DispatchResponse> = from_slice(&res.acknowledgement).unwrap();
+    let ack: AcknowledgementMsg<DispatchResponse> =
+        from_slice(&res.acknowledgement, DESERIALIZATION_LIMIT).unwrap();
     ack.unwrap();
 
     // and we dispatch the BankMsg
@@ -255,7 +259,7 @@ fn handle_dispatch_packet() {
         assert_eq!(account, contract_addr.as_str());
         assert_eq!(0, funds.len());
         // parse the message - should callback with proper channel_id
-        let rmsg: ReflectExecuteMsg = from_slice(&msg).unwrap();
+        let rmsg: ReflectExecuteMsg = from_slice(&msg, DESERIALIZATION_LIMIT).unwrap();
         assert_eq!(
             rmsg,
             ReflectExecuteMsg::ReflectMsg {
@@ -275,6 +279,7 @@ fn handle_dispatch_packet() {
     // we didn't dispatch anything
     assert_eq!(0, res.submessages.len());
     // acknowledgement is an error
-    let ack: AcknowledgementMsg<DispatchResponse> = from_slice(&res.acknowledgement).unwrap();
+    let ack: AcknowledgementMsg<DispatchResponse> =
+        from_slice(&res.acknowledgement, DESERIALIZATION_LIMIT).unwrap();
     assert_eq!(ack.unwrap_err(), "invalid packet: Error parsing into type ibc_reflect::msg::PacketMsg: unknown variant `reflect_code_id`, expected one of `dispatch`, `who_am_i`, `balances`");
 }