From ba37dcc5dd6e7c1e806964d6622ad71fb3074df9 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Jun 2026 06:46:35 +0100 Subject: [PATCH 1/2] implementing the code changes --- crates/core/src/decode/host_error.rs | 9 +- crates/core/src/decode/mappings/mod.rs | 1 + crates/core/src/decode/mappings/object.rs | 77 +++++++++ crates/core/src/taxonomy/data/object.toml | 184 ++++++++++++++++++++-- 4 files changed, 257 insertions(+), 14 deletions(-) create mode 100644 crates/core/src/decode/mappings/object.rs diff --git a/crates/core/src/decode/host_error.rs b/crates/core/src/decode/host_error.rs index bc6b0384..bcb58c9a 100644 --- a/crates/core/src/decode/host_error.rs +++ b/crates/core/src/decode/host_error.rs @@ -88,9 +88,12 @@ impl HostError { format!("Value error (code {code}): a host value could not be converted or validated.") } }, - Self::Object { code } => match code { - 0 => "Index out of bounds: the contract accessed a vector or byte array with an index beyond its length.".to_string(), - _ => format!("Object error (code {code}): an operation on a host object (vector, map, bytes) failed."), + Self::Object { code } => { + if let Some(detail) = crate::decode::mappings::object::lookup(*code) { + detail.summary.to_string() + } else { + format!("Object error (code {code}): an operation on a host object (vector, map, bytes) failed.") + } }, Self::Crypto { code } => match code { 0 => "Invalid cryptographic input: a public key, signature, or hash input has the wrong length or format.".to_string(), diff --git a/crates/core/src/decode/mappings/mod.rs b/crates/core/src/decode/mappings/mod.rs index 9703a4d9..b72ef716 100644 --- a/crates/core/src/decode/mappings/mod.rs +++ b/crates/core/src/decode/mappings/mod.rs @@ -3,5 +3,6 @@ pub mod auth; pub mod budget; pub mod context; +pub mod object; pub mod storage; pub mod value; diff --git a/crates/core/src/decode/mappings/object.rs b/crates/core/src/decode/mappings/object.rs new file mode 100644 index 00000000..d93bb837 --- /dev/null +++ b/crates/core/src/decode/mappings/object.rs @@ -0,0 +1,77 @@ +use crate::types::report::Severity; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ObjectErrorDetail { + pub code: u32, + pub name: &'static str, + /// Short explanation of the failure. + pub summary: &'static str, + pub severity: Severity, +} + +pub const OBJECT_ERROR_DETAILS: &[ObjectErrorDetail] = &[ + ObjectErrorDetail { + code: 0, + name: "UnknownError", + summary: "An unknown or unclassified host object error occurred.", + severity: Severity::Error, + }, + ObjectErrorDetail { + code: 1, + name: "UnknownReference", + summary: "The provided object handle does not reference a valid host object.", + severity: Severity::Error, + }, + ObjectErrorDetail { + code: 2, + name: "UnexpectedType", + summary: "The host object's type does not match the expected type for the operation.", + severity: Severity::Error, + }, + ObjectErrorDetail { + code: 3, + name: "ObjectCountExceedsU32Max", + summary: "The total number of host objects allocated in this transaction has exceeded the maximum capacity.", + severity: Severity::Error, + }, + ObjectErrorDetail { + code: 4, + name: "ObjectNotExist", + summary: "The requested host object does not exist.", + severity: Severity::Error, + }, + ObjectErrorDetail { + code: 5, + name: "VecIndexOutOfBound", + summary: "An index out of bounds was used when accessing a host vector or byte array.", + severity: Severity::Error, + }, + ObjectErrorDetail { + code: 6, + name: "ContractHashWrongLength", + summary: "The provided contract hash or address has an incorrect length.", + severity: Severity::Error, + }, +]; + +pub fn lookup(code: u32) -> Option<&'static ObjectErrorDetail> { + OBJECT_ERROR_DETAILS.iter().find(|detail| detail.code == code) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lookup_returns_vec_index_out_of_bound_detail() { + let detail = lookup(5).expect("vec index out of bound detail"); + assert_eq!(detail.name, "VecIndexOutOfBound"); + assert!(detail.summary.contains("index out of bounds") || detail.summary.contains("index out of bounds")); + } + + #[test] + fn table_covers_known_object_codes() { + assert_eq!(OBJECT_ERROR_DETAILS.len(), 7); + assert!(lookup(99).is_none()); + } +} diff --git a/crates/core/src/taxonomy/data/object.toml b/crates/core/src/taxonomy/data/object.toml index 6e8fc8c7..7d8fa13d 100644 --- a/crates/core/src/taxonomy/data/object.toml +++ b/crates/core/src/taxonomy/data/object.toml @@ -7,31 +7,193 @@ description = "Errors triggered during host object creation, access, or manipula source_module = "soroban-env-host/src/host/mem_helper.rs" [[errors]] -id = "host.object.out_of_bounds" +id = "host.object.unknown_error" category = "object" code = 0 -name = "IndexBounds" +name = "UnknownError" severity = "Error" since_protocol = 20 -summary = "An index out of bounds was used when accessing a host object (vector, bytes)." +summary = "An unknown or unclassified host object error occurred." detailed_explanation = """ -When a contract accesses a vector element, byte slice, or map entry with an index or key \ -that is outside the valid range, the host raises this error. This is analogous to an \ -array-out-of-bounds error in traditional programming. +The host encountered an error while interacting with or managing an object that does not map to \ +any other known object error code. This typically points to an internal state discrepancy, \ +memory allocation failure, or an unhandled edge case within the host environment's object subsystem. """ +[[errors.common_causes]] +description = "Internal host memory management or allocator errors" +likelihood = "low" +[[errors.common_causes]] +description = "Using an outdated Soroban runtime version that does not correctly parse newer host objects" +likelihood = "low" +[[errors.suggested_fixes]] +description = "Examine the diagnostic events emitted prior to the failure to find more context" +difficulty = "easy" +requires_upgrade = false +[[errors.suggested_fixes]] +description = "Update the Soroban SDK and test environment to the latest version" +difficulty = "easy" +requires_upgrade = false +[[errors]] +id = "host.object.unknown_reference" +category = "object" +code = 1 +name = "UnknownReference" +severity = "Error" +since_protocol = 20 +summary = "The provided object handle does not reference a valid host object." +detailed_explanation = """ +Contracts interact with host-managed memory via object handles (such as Vec, Map, Bytes, Address). \ +If a contract provides a handle identifier that does not exist in the host's object registry for the \ +current invocation, this error is raised. +""" [[errors.common_causes]] -description = "Accessing a vector or bytes object with an index >= length" +description = "Passing an uninitialized or fabricated object handle to a host function" +likelihood = "high" +[[errors.common_causes]] +description = "Attempting to use a handle that belonged to a different transaction context or cross-contract call that has finished" +likelihood = "medium" +[[errors.suggested_fixes]] +description = "Verify that all objects are instantiated correctly using the SDK constructors before calling host functions" +difficulty = "easy" +requires_upgrade = false +[[errors.suggested_fixes]] +description = "Avoid manually creating or editing raw u32 object handles in contract code" +difficulty = "easy" +requires_upgrade = false + +[[errors]] +id = "host.object.unexpected_type" +category = "object" +code = 2 +name = "UnexpectedType" +severity = "Error" +since_protocol = 20 +summary = "The host object's type does not match the expected type for the operation." +detailed_explanation = """ +A host function expected a host object of a specific type (e.g. Map), but the contract supplied \ +a handle to a different type of host object (e.g. Vector). The host validates object types at the \ +FFI boundary and aborts execution upon mismatch. +""" +[[errors.common_causes]] +description = "Passing a Vector handle to a function expecting a Map handle, or vice versa" likelihood = "high" +[[errors.common_causes]] +description = "Type mismatch when casting or using unsafe conversion functions on raw object handles" +likelihood = "medium" +[[errors.suggested_fixes]] +description = "Check the contract signature and host function arguments to ensure correct collection types are used" +difficulty = "easy" +requires_upgrade = false +[[errors.suggested_fixes]] +description = "Use the strongly-typed wrappers provided by the Soroban SDK instead of raw handles" +difficulty = "easy" +requires_upgrade = false +[[errors]] +id = "host.object.object_count_exceeds_u32_max" +category = "object" +code = 3 +name = "ObjectCountExceedsU32Max" +severity = "Error" +since_protocol = 20 +summary = "The total number of host objects allocated in this transaction has exceeded the maximum capacity." +detailed_explanation = """ +The host environment tracks all active objects using a list indexed by a u32 handle. If a contract \ +attempts to allocate more host objects than the maximum capacity of a u32 index, the host throws \ +this error to prevent memory exhaustion and overflow. +""" [[errors.common_causes]] -description = "Off-by-one errors in loop bounds when iterating over collections" +description = "Unbounded allocation of objects (e.g. nested vectors or maps) inside a large loop" likelihood = "high" +[[errors.common_causes]] +description = "Infinite recursion resulting in continuous allocation of local variables/objects" +likelihood = "medium" +[[errors.suggested_fixes]] +description = "Refactor loops to avoid creating new collections in each iteration; reuse existing collections where possible" +difficulty = "medium" +requires_upgrade = false +[[errors.suggested_fixes]] +description = "Add pagination or chunking to process data in smaller batches across multiple transactions" +difficulty = "medium" +requires_upgrade = false + +[[errors]] +id = "host.object.object_not_exist" +category = "object" +code = 4 +name = "ObjectNotExist" +severity = "Error" +since_protocol = 20 +summary = "The requested host object does not exist." +detailed_explanation = """ +Similar to UnknownReference, but specifically raised when attempting to retrieve or dereference an \ +object that is expected to exist in the host state but is missing. This can occur if the host object \ +registry has been corrupted, or if the handle was invalidated during host-side optimization. +""" +[[errors.common_causes]] +description = "Using an invalid or expired object handle that has been cleared from host memory" +likelihood = "medium" +[[errors.common_causes]] +description = "Passing a handle that was never registered in the host's current object storage" +likelihood = "medium" +[[errors.suggested_fixes]] +description = "Ensure the host object was correctly created and not discarded or out of scope" +difficulty = "easy" +requires_upgrade = false +[[errors]] +id = "host.object.vec_index_out_of_bound" +category = "object" +code = 5 +name = "VecIndexOutOfBound" +severity = "Error" +since_protocol = 20 +summary = "An index out of bounds was used when accessing a host vector or byte array." +detailed_explanation = """ +When a contract accesses a vector element, byte slice, or array entry with an index that is \ +greater than or equal to the collection's length, the host raises this error. This prevents \ +invalid memory access in the host's WebAssembly execution environment. +""" +[[errors.common_causes]] +description = "Accessing a vector or bytes object with an index >= length" +likelihood = "high" +[[errors.common_causes]] +description = "Off-by-one errors in loop bounds when iterating over collections" +likelihood = "high" [[errors.suggested_fixes]] description = "Check the length of the collection before accessing elements by index" difficulty = "easy" -requires_upgrade = true +requires_upgrade = false +[[errors.suggested_fixes]] +description = "Use safe accessors (like `get`) that return `Option` instead of direct index access" +difficulty = "easy" +requires_upgrade = false -related_errors = ["host.value.invalid_input"] -source_file = "soroban-env-host/src/host/mem_helper.rs" +[[errors]] +id = "host.object.contract_hash_wrong_length" +category = "object" +code = 6 +name = "ContractHashWrongLength" +severity = "Error" +since_protocol = 20 +summary = "The provided contract hash or address has an incorrect length." +detailed_explanation = """ +Soroban contract hashes, addresses, and cryptographic keys are represented by specific byte lengths \ +(typically exactly 32 bytes). If a contract passes a byte array object representing a hash or public key \ +with an incorrect length, the host rejects it immediately to ensure cryptographic validity. +""" +[[errors.common_causes]] +description = "Passing a contract address or hash with a length other than 32 bytes" +likelihood = "high" +[[errors.common_causes]] +description = "Truncation or padding errors when manipulating hex strings or byte vectors before passing them to the host" +likelihood = "medium" +[[errors.suggested_fixes]] +description = "Ensure that hex decoding or byte array construction outputs exactly 32 bytes" +difficulty = "easy" +requires_upgrade = false +[[errors.suggested_fixes]] +description = "Validate the byte length of the address or hash before invoking the target host function" +difficulty = "easy" +requires_upgrade = false From 0c87cb17aa49122734a9c856a32726f64a8fd50f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Jun 2026 07:14:40 +0100 Subject: [PATCH 2/2] Fixed all Object Error Code Mappings --- crates/core/src/decode/host_error.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/core/src/decode/host_error.rs b/crates/core/src/decode/host_error.rs index bcb58c9a..5705a156 100644 --- a/crates/core/src/decode/host_error.rs +++ b/crates/core/src/decode/host_error.rs @@ -320,7 +320,11 @@ mod tests { ); assert_eq!( HostError::Object { code: 0 }.summary(), - "Index out of bounds: the contract accessed a vector or byte array with an index beyond its length." + "An unknown or unclassified host object error occurred." + ); + assert_eq!( + HostError::Object { code: 5 }.summary(), + "An index out of bounds was used when accessing a host vector or byte array." ); assert_eq!( HostError::Crypto { code: 0 }.summary(),