Skip to content

Commit d1742fa

Browse files
fix(ethereum-abi): Fix Ethereum ABI type parsing recursion (trustwallet#4597)
Cherry-picked ABI fix only from upstream a8ae674. CI/toolchain chore changes from the same upstream commit were excluded. Co-authored-by: Sergei Boiko <satoshiotomakan8@gmail.com>
1 parent 8756596 commit d1742fa

File tree

1 file changed

+35
-1
lines changed

1 file changed

+35
-1
lines changed

rust/tw_evm/src/abi/param_type/reader.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,31 @@ use crate::abi::{AbiError, AbiErrorKind, AbiResult};
99
use std::str::FromStr;
1010
use tw_coin_entry::error::prelude::*;
1111

12+
const MAX_RECURSION_DEPTH: usize = 20;
13+
1214
pub struct Reader;
1315

1416
impl Reader {
1517
/// Doesn't accept tuple types with specified parameters, e.g `(uint32, address)`.
1618
pub fn parse_type<T: TypeConstructor>(s: &str) -> AbiResult<T> {
19+
Self::parse_type_step(s, 0)
20+
}
21+
22+
/// Doesn't accept tuple types with specified parameters, e.g `(uint32, address)`.
23+
pub fn parse_type_step<T: TypeConstructor>(s: &str, current_depth: usize) -> AbiResult<T> {
24+
if current_depth > MAX_RECURSION_DEPTH {
25+
return AbiError::err(AbiErrorKind::Error_invalid_param_type)
26+
.with_context(|| format!("Max recursion depth exceeded: {MAX_RECURSION_DEPTH}"));
27+
}
28+
1729
// Array
1830
if let Some(remaining) = s.strip_suffix(']') {
1931
let Some((element_type_str, len_str)) = remaining.rsplit_once('[') else {
2032
return AbiError::err(AbiErrorKind::Error_invalid_param_type)
2133
.with_context(|| format!("Invalid array type: {s}"));
2234
};
2335

24-
let element_type = Reader::parse_type::<T>(element_type_str)
36+
let element_type = Reader::parse_type_step::<T>(element_type_str, current_depth + 1)
2537
.with_context(|| format!("Error parsing inner array type: {element_type_str}"))?;
2638
if let Some(len) = parse_len(len_str)
2739
.with_context(|| format!("Error parsing fixed_array length: {len_str}"))?
@@ -113,3 +125,25 @@ fn parse_usize(usize_str: &str) -> AbiResult<Option<usize>> {
113125
.tw_err(AbiErrorKind::Error_invalid_param_type)
114126
.with_context(|| format!("Expected a decimal string: {usize_str}"))
115127
}
128+
129+
#[cfg(test)]
130+
mod tests {
131+
use super::*;
132+
use crate::abi::param_type::ParamType;
133+
134+
#[test]
135+
fn test_parse_type_recursion() {
136+
let depth = MAX_RECURSION_DEPTH;
137+
let mut ty = "uint256".to_string();
138+
139+
// Append exactly MAX_RECURSION_DEPTH array brackets that must be allowed.
140+
for _ in 0..depth {
141+
ty.push_str("[]");
142+
}
143+
ParamType::try_from_type_short(&ty).expect("Should parse within recursion limit");
144+
145+
// Append one more array bracket to exceed the limit.
146+
ty.push_str("[]");
147+
ParamType::try_from_type_short(&ty).expect_err("Recursion limit exceeded");
148+
}
149+
}

0 commit comments

Comments
 (0)