Skip to content

Commit be94b5e

Browse files
committed
fix: get required bytes & grow memory before writing
1 parent d8e6e8b commit be94b5e

File tree

1 file changed

+77
-17
lines changed

1 file changed

+77
-17
lines changed

clarity/src/vm/clarity_wasm.rs

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,17 @@ pub fn call_function<'a>(
549549
}
550550
let mut in_mem_offset = offset + arg_size;
551551

552+
// Ensure that the memory has enough space for the arguments
553+
let mut total_required_bytes = 0;
554+
for (arg, ty) in args.iter().zip(func_types.get_arg_types()) {
555+
total_required_bytes += get_required_bytes(ty, arg)?;
556+
}
557+
ensure_memory(
558+
&memory,
559+
&mut store,
560+
total_required_bytes + in_mem_offset as usize,
561+
)?;
562+
552563
// Convert the args into wasmtime values
553564
let mut wasm_args = vec![];
554565
for (arg, ty) in args.iter().zip(func_types.get_arg_types()) {
@@ -1505,6 +1516,72 @@ fn ensure_memory(
15051516
Ok(())
15061517
}
15071518

1519+
/// Get the number of bytes required to write the given value to memory.
1520+
/// This is used to ensure that the memory has enough space for the arguments.
1521+
fn get_required_bytes(ty: &TypeSignature, value: &Value) -> Result<usize, Error> {
1522+
match value {
1523+
Value::UInt(_) | Value::Int(_) | Value::Bool(_) => {
1524+
// These types don't require memory allocation
1525+
Ok(0)
1526+
}
1527+
Value::Optional(o) => {
1528+
let TypeSignature::OptionalType(inner_ty) = ty else {
1529+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1530+
};
1531+
1532+
if let Some(inner_value) = o.data.as_ref() {
1533+
get_required_bytes(inner_ty, inner_value)
1534+
} else {
1535+
Ok(0)
1536+
}
1537+
}
1538+
Value::Response(r) => {
1539+
let TypeSignature::ResponseType(inner_tys) = ty else {
1540+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1541+
};
1542+
get_required_bytes(
1543+
if r.committed {
1544+
&inner_tys.0
1545+
} else {
1546+
&inner_tys.1
1547+
},
1548+
&r.data,
1549+
)
1550+
}
1551+
Value::Sequence(SequenceData::String(CharType::ASCII(s))) => Ok(s.data.len()),
1552+
Value::Sequence(SequenceData::String(CharType::UTF8(s))) => Ok(s.data.len()),
1553+
Value::Sequence(SequenceData::Buffer(b)) => Ok(b.data.len()),
1554+
Value::Sequence(SequenceData::List(l)) => {
1555+
let TypeSignature::SequenceType(SequenceSubtype::ListType(ltd)) = ty else {
1556+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1557+
};
1558+
let total_bytes = l
1559+
.data
1560+
.iter()
1561+
.map(|_| get_type_in_memory_size(ltd.get_list_item_type(), true))
1562+
.sum::<i32>() as usize;
1563+
Ok(total_bytes)
1564+
}
1565+
Value::Principal(PrincipalData::Standard(_)) => Ok(STANDARD_PRINCIPAL_BYTES),
1566+
Value::Principal(PrincipalData::Contract(p))
1567+
| Value::CallableContract(CallableData {
1568+
contract_identifier: p,
1569+
..
1570+
}) => Ok(PRINCIPAL_BYTES + 1 + p.name.len() as usize),
1571+
Value::Tuple(TupleData { data_map, .. }) => {
1572+
let TypeSignature::TupleType(tuple_ty) = ty else {
1573+
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
1574+
};
1575+
1576+
let mut total_bytes = 0;
1577+
for (name, ty) in tuple_ty.get_type_map() {
1578+
total_bytes += get_required_bytes(ty, &data_map[name])?;
1579+
}
1580+
Ok(total_bytes)
1581+
}
1582+
}
1583+
}
1584+
15081585
/// Convert a Clarity `Value` into one or more Wasm `Val`. If this value
15091586
/// requires writing into the Wasm memory, write it to the provided `offset`.
15101587
/// Return a vector of `Val`s that can be passed to a Wasm function, and the
@@ -1604,9 +1681,6 @@ fn pass_argument_to_wasm(
16041681
Ok((buffer, new_offset, new_in_mem_offset))
16051682
}
16061683
Value::Sequence(SequenceData::String(CharType::ASCII(s))) => {
1607-
let required_bytes = (in_mem_offset as usize) + s.data.len();
1608-
ensure_memory(&memory, &mut store, required_bytes)?;
1609-
16101684
// For a string, write the bytes into the memory, then pass the
16111685
// offset and length to the Wasm function.
16121686
let buffer = vec![Val::I32(in_mem_offset), Val::I32(s.data.len() as i32)];
@@ -1621,9 +1695,6 @@ fn pass_argument_to_wasm(
16211695
Ok((buffer, offset, adjusted_in_mem_offset))
16221696
}
16231697
Value::Sequence(SequenceData::String(CharType::UTF8(s))) => {
1624-
let required_bytes = (in_mem_offset as usize) + s.data.len();
1625-
ensure_memory(&memory, &mut store, required_bytes)?;
1626-
16271698
// For a utf8 string, convert the chars to big-endian i32, convert this into a list of
16281699
// bytes, then pass the offset and length to the wasm function
16291700
let bytes: Vec<u8> = String::from_utf8(s.items().iter().flatten().copied().collect())
@@ -1639,9 +1710,6 @@ fn pass_argument_to_wasm(
16391710
Ok((buffer, offset, adjusted_in_mem_offset))
16401711
}
16411712
Value::Sequence(SequenceData::Buffer(b)) => {
1642-
let required_bytes = (in_mem_offset as usize) + b.data.len();
1643-
ensure_memory(&memory, &mut store, required_bytes)?;
1644-
16451713
// For a buffer, write the bytes into the memory, then pass the
16461714
// offset and length to the Wasm function.
16471715
let buffer = vec![Val::I32(in_mem_offset), Val::I32(b.data.len() as i32)];
@@ -1659,14 +1727,6 @@ fn pass_argument_to_wasm(
16591727
let TypeSignature::SequenceType(SequenceSubtype::ListType(ltd)) = ty else {
16601728
return Err(Error::Wasm(WasmError::ValueTypeMismatch));
16611729
};
1662-
let total_bytes = l
1663-
.data
1664-
.iter()
1665-
.map(|_| get_type_in_memory_size(ltd.get_list_item_type(), true))
1666-
.sum::<i32>() as usize;
1667-
let required_bytes = (in_mem_offset as usize) + total_bytes;
1668-
ensure_memory(&memory, &mut store, required_bytes)?;
1669-
16701730
let mut buffer = vec![Val::I32(offset)];
16711731
let mut written = 0;
16721732
let mut in_mem_written = 0;

0 commit comments

Comments
 (0)