Просмотр исходного кода

sdk-util: Add size limits on data; comment code

Change `data.len() as u32` to `u32::try_from`. This will return an error
in the case where the length of the data exceeds u32. (Using an `as`
conversion will cause the value to overflow and write the wrong amount
of data).

Add a new ContractError enum `DataTooLarge` to handle this case.

Add a similar check to the `parse_ret` auxiliary function to ensure the
ret i64 fits into the u32 type.

Add code comments to the utility functions to explain what they do.
y 2 лет назад
Родитель
Сommit
14661fb3f7
2 измененных файлов с 42 добавлено и 8 удалено
  1. 9 0
      src/sdk/src/error.rs
  2. 33 8
      src/sdk/src/util.rs

+ 9 - 0
src/sdk/src/error.rs

@@ -83,6 +83,12 @@ pub enum ContractError {
 
     #[error("Error retrieving system time")]
     GetSystemTimeFailed,
+
+    // Provide feedback when the data sent is too large. For example,
+    // if a user tries to send > u32::MAX bytes, we can limit the
+    // size and present this error.
+    #[error("Data too large")]
+    DataTooLarge,
 }
 
 /// Builtin return values occupy the upper 32 bits
@@ -111,6 +117,7 @@ pub const DB_DEL_FAILED: i64 = to_builtin!(16);
 pub const SMT_INVALID_LEAF: i64 = to_builtin!(17);
 pub const SMT_INVALID_PATH_NODES: i64 = to_builtin!(18);
 pub const GET_SYSTEM_TIME_FAILED: i64 = to_builtin!(19);
+pub const DATA_TOO_LARGE: i64 = to_builtin!(20);
 
 impl From<ContractError> for i64 {
     fn from(err: ContractError) -> Self {
@@ -133,6 +140,7 @@ impl From<ContractError> for i64 {
             ContractError::SmtInvalidLeaf => SMT_INVALID_LEAF,
             ContractError::SmtInvalidPathNodes => SMT_INVALID_PATH_NODES,
             ContractError::GetSystemTimeFailed => GET_SYSTEM_TIME_FAILED,
+            ContractError::DataTooLarge => DATA_TOO_LARGE,
             ContractError::Custom(error) => {
                 if error == 0 {
                     CUSTOM_ZERO
@@ -166,6 +174,7 @@ impl From<i64> for ContractError {
             SMT_INVALID_LEAF => Self::SmtInvalidLeaf,
             SMT_INVALID_PATH_NODES => Self::SmtInvalidPathNodes,
             GET_SYSTEM_TIME_FAILED => Self::GetSystemTimeFailed,
+            DATA_TOO_LARGE => Self::DataTooLarge,
             _ => Self::Custom(error as u32),
         }
     }

+ 33 - 8
src/sdk/src/util.rs

@@ -21,17 +21,31 @@ use super::{
     error::{ContractError, GenericResult},
 };
 
+/// Calls the `set_return_data` WASM function. Returns Ok(()) on success.
+/// Otherwise, convert the i64 error code into a [`ContractError`].
 pub fn set_return_data(data: &[u8]) -> Result<(), ContractError> {
-    unsafe {
-        match set_return_data_(data.as_ptr(), data.len() as u32) {
-            0 => Ok(()),
-            errcode => Err(ContractError::from(errcode)),
-        }
+    // Ensure that the number of bytes fits within the u32 data type.
+    match u32::try_from(data.len()) {
+        Ok(len) => {
+            unsafe {
+                match set_return_data_(data.as_ptr(), len) {
+                    0 => Ok(()),
+                    errcode => Err(ContractError::from(errcode)),
+                }
+            }
+        },
+        Err(_) => Err(ContractError::DataTooLarge),
     }
 }
 
-pub fn put_object_bytes(data: &[u8]) -> i64 {
-    unsafe { put_object_bytes_(data.as_ptr(), data.len() as u32) }
+pub fn put_object_bytes(data: &[u8]) -> Result<i64, ContractError> {
+    // Ensure that the number of bytes fits within the u32 data type.
+    match u32::try_from(data.len()) {
+        Ok(len) => {
+            unsafe { Ok(put_object_bytes_(data.as_ptr(), len)) }
+        },
+        Err(_) => Err(ContractError::DataTooLarge),
+    }
 }
 
 pub fn get_object_bytes(data: &mut [u8], object_index: u32) -> i64 {
@@ -43,7 +57,10 @@ pub fn get_object_size(object_index: u32) -> i64 {
 }
 
 /// Auxiliary function to parse db_get and get_slot return value.
+/// If either of these functions returns a negative integer error code,
+/// convert it into a [`ContractError`].
 pub(crate) fn parse_ret(ret: i64) -> GenericResult<Option<Vec<u8>>> {
+    // Negative values represent an error code.
     if ret < 0 {
         match ret {
             CALLER_ACCESS_DENIED => return Err(ContractError::CallerAccessDenied),
@@ -53,7 +70,15 @@ pub(crate) fn parse_ret(ret: i64) -> GenericResult<Option<Vec<u8>>> {
         }
     }
 
-    let obj = ret as u32;
+    // Ensure that the returned value fits into the u32 datatype.
+    // Note that any negative cases should be caught by the `unimplemented`
+    // match arm above.
+    let obj = match u32::try_from(ret) {
+        Ok(obj) => obj,
+        Err(_) => {
+            return Err(ContractError::SetRetvalError)
+        }
+    };
     let obj_size = get_object_size(obj);
     let mut buf = vec![0u8; obj_size as usize];
     get_object_bytes(&mut buf, obj);