mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-27 17:20:54 +00:00
Compare commits
3 Commits
Wasm-vm-re
...
audit_wasm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2605b4a78b | ||
|
|
00488bf0b5 | ||
|
|
ecbfb8ea06 |
@@ -129,7 +129,7 @@ host_functions! {
|
||||
fn get_tx_nested_field(&self, locator: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The serialized bytes of a nested field of the current (escrow) ledger object,
|
||||
/// reached by a `locator`, as with [`Self::get_tx_nested_field`].
|
||||
/// reached by a `locator`, as with [`HostFunctions::get_tx_nested_field`].
|
||||
#[gas = 110]
|
||||
#[wasm_name = "home_le_inner"]
|
||||
fn get_current_ledger_obj_nested_field(
|
||||
@@ -157,7 +157,7 @@ host_functions! {
|
||||
fn get_tx_array_len(&self, field: i32) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in an array field of the current (escrow) ledger
|
||||
/// object, as with [`Self::get_tx_array_len`].
|
||||
/// object, as with [`HostFunctions::get_tx_array_len`].
|
||||
#[gas = 40]
|
||||
#[wasm_name = "home_le_arr_len"]
|
||||
fn get_current_ledger_obj_array_len(&self, field: i32) -> HostResult<i32>;
|
||||
@@ -175,7 +175,7 @@ host_functions! {
|
||||
fn get_tx_nested_array_len(&self, locator: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in a nested array field of the current (escrow) ledger
|
||||
/// object, reached by a `locator`, as with [`Self::get_tx_nested_array_len`].
|
||||
/// object, reached by a `locator`, as with [`HostFunctions::get_tx_nested_array_len`].
|
||||
#[gas = 70]
|
||||
#[wasm_name = "home_le_inner_arr_len"]
|
||||
fn get_current_ledger_obj_nested_array_len(&self, locator: &[u8]) -> HostResult<i32>;
|
||||
|
||||
@@ -281,6 +281,12 @@ const EXPONENT_BYTES: usize = 4;
|
||||
/// to a scratch buffer, so the input stays borrowed rather than copied. The two output
|
||||
/// regions are judged after the input, and the mantissa's region before the exponent's,
|
||||
/// so the first fault reported is the leftmost.
|
||||
///
|
||||
/// The two widths are the ABI's rather than the guest's, so the length the host reports
|
||||
/// is checked against their sum for equality rather than as a bound, and ahead of the
|
||||
/// output regions: a wrong total means there is no answer to place, whatever the guest
|
||||
/// declared. That is a fatal error and not a status, since the guest asked for nothing
|
||||
/// wrong.
|
||||
pub(crate) fn write_mant_exp(
|
||||
caller: &mut Caller<'_, VmState<'_>>,
|
||||
mantissa_out: Region,
|
||||
@@ -299,6 +305,14 @@ pub(crate) fn write_mant_exp(
|
||||
|
||||
let total = call(host, data, mant_buf, exp_buf)?;
|
||||
|
||||
// Both buffers are fixed-width and were offered whole, so the only length the host
|
||||
// can correctly report is their sum. Anything else is the host contradicting the
|
||||
// ABI: with the widths in doubt, part of what would be copied out is whatever the
|
||||
// previous call left in the buffer, so none of it is copied.
|
||||
if total != MANTISSA_BYTES + EXPONENT_BYTES {
|
||||
return Err(HostError::InternalFatal.into());
|
||||
}
|
||||
|
||||
// Copy the mantissa, then the exponent, each only if its whole value fits its
|
||||
// region — a region too small is `BufferTooSmall`, with nothing written.
|
||||
let mant_range = mantissa_out.range()?;
|
||||
@@ -324,7 +338,7 @@ pub(crate) fn write_mant_exp(
|
||||
#[expect(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
reason = "the total is 12, far inside i32"
|
||||
reason = "a total other than 12 returned above, and 12 is far inside i32"
|
||||
)]
|
||||
let total = total as i32;
|
||||
Ok(total)
|
||||
|
||||
@@ -935,6 +935,24 @@ fn float_to_mant_exp_writes_both_regions() {
|
||||
assert_eq!(status(&wat, &host), 9, "the exponent's first byte");
|
||||
}
|
||||
|
||||
/// The widths that call writes are the ABI's, so a host reporting any other total has
|
||||
/// contradicted it: the regions are wide enough and the guest asked for nothing wrong,
|
||||
/// yet the mantissa is short of its eight bytes, so the rest of what would be copied is
|
||||
/// whatever the buffer already held. The run stops instead.
|
||||
#[test]
|
||||
fn float_to_mant_exp_with_a_wrong_total_stops_the_run() {
|
||||
let host = FakeHost::new().answering_float_mant_exp(vec![1, 2, 3, 4], vec![9, 10, 11, 12]);
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_TO_MANT_EXP, ONE_PAGE],
|
||||
"(call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 80) (i32.const 4))",
|
||||
);
|
||||
assert!(
|
||||
matches!(failure(&wat, &host).error, RunError::Internal),
|
||||
"a total that is not the two widths must stop the run"
|
||||
);
|
||||
}
|
||||
|
||||
/// A comparison that reads two float regions and returns a scalar verdict, no output
|
||||
/// region involved.
|
||||
#[test]
|
||||
@@ -964,7 +982,7 @@ fn float_add_reads_both_operands_and_the_mode() {
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the result length");
|
||||
assert_eq!(
|
||||
*host.float_binops_asked.borrow(),
|
||||
*host.float_binary_ops_asked.borrow(),
|
||||
vec![("add", vec![0u8; 8], vec![0u8; 8], 2)]
|
||||
);
|
||||
}
|
||||
@@ -981,7 +999,7 @@ fn float_root_reads_the_float_the_degree_and_the_mode() {
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the result length");
|
||||
assert_eq!(
|
||||
*host.float_unops_asked.borrow(),
|
||||
*host.float_unary_ops_asked.borrow(),
|
||||
vec![("root", vec![0u8; 8], 3, 1)]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,26 @@ pub struct Trace {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// The `(message, signature, pubkey)` `check_signature` takes.
|
||||
pub type SigCheck = (Vec<u8>, Vec<u8>, Vec<u8>);
|
||||
|
||||
/// The `(subject, issuer, type)` `credential_keylet` takes.
|
||||
pub type CredentialKey = (Vec<u8>, Vec<u8>, Vec<u8>);
|
||||
|
||||
/// The `(account1, account2, currency)` `trust_line_keylet` takes.
|
||||
pub type TrustLineKey = (Vec<u8>, Vec<u8>, Vec<u8>);
|
||||
|
||||
/// The `(account, destination, seq)` `paychannel_keylet` takes.
|
||||
pub type PaychannelKey = (Vec<u8>, Vec<u8>, i32);
|
||||
|
||||
/// One call to a float operator over two floats — `float_add`, `float_subtract`,
|
||||
/// `float_multiply`, `float_divide` — as `(operator, x, y, mode)`.
|
||||
pub type FloatBinaryCall = (&'static str, Vec<u8>, Vec<u8>, i32);
|
||||
|
||||
/// One call to a float operator over a float and an integer — `float_root`,
|
||||
/// `float_power` — as `(operator, x, n, mode)`.
|
||||
pub type FloatUnaryCall = (&'static str, Vec<u8>, i32, i32);
|
||||
|
||||
/// A `HostFunctions` implementation that answers from what the test put in it and
|
||||
/// records what it was asked. The ABI's receiver is `&self`, so the recording goes
|
||||
/// behind `RefCell`, as a real mutating host's would.
|
||||
@@ -172,7 +192,7 @@ pub struct FakeHost {
|
||||
/// What `check_signature` answers, whatever it is given.
|
||||
pub sig_valid: HostResult<i32>,
|
||||
/// Every (message, signature, pubkey) `check_signature` was asked to verify.
|
||||
pub sigs_checked: RefCell<Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
|
||||
pub sigs_checked: RefCell<Vec<SigCheck>>,
|
||||
/// What `account_keylet` answers, by account bytes. An unlisted account answers
|
||||
/// `InvalidAccount`.
|
||||
pub account_keylets: HashMap<Vec<u8>, Answer>,
|
||||
@@ -190,9 +210,9 @@ pub struct FakeHost {
|
||||
pub check_keylets_asked: RefCell<Vec<(Vec<u8>, i32)>>,
|
||||
/// What `credential_keylet` answers, by (subject, issuer, type) bytes. An unlisted
|
||||
/// key answers `InvalidAccount`.
|
||||
pub credential_keylets: HashMap<(Vec<u8>, Vec<u8>, Vec<u8>), Answer>,
|
||||
pub credential_keylets: HashMap<CredentialKey, Answer>,
|
||||
/// Every (subject, issuer, type) `credential_keylet` was asked for.
|
||||
pub credential_keylets_asked: RefCell<Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
|
||||
pub credential_keylets_asked: RefCell<Vec<CredentialKey>>,
|
||||
/// What `delegate_keylet` answers, by (account, authorize) bytes. An unlisted key
|
||||
/// answers `InvalidAccount`.
|
||||
pub delegate_keylets: HashMap<(Vec<u8>, Vec<u8>), Answer>,
|
||||
@@ -215,9 +235,9 @@ pub struct FakeHost {
|
||||
pub escrow_keylets_asked: RefCell<Vec<(Vec<u8>, i32)>>,
|
||||
/// What `trust_line_keylet` answers, by (account1, account2, currency) bytes. An
|
||||
/// unlisted key answers `InvalidAccount`.
|
||||
pub trust_line_keylets: HashMap<(Vec<u8>, Vec<u8>, Vec<u8>), Answer>,
|
||||
pub trust_line_keylets: HashMap<TrustLineKey, Answer>,
|
||||
/// Every (account1, account2, currency) `trust_line_keylet` was asked for.
|
||||
pub trust_line_keylets_asked: RefCell<Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
|
||||
pub trust_line_keylets_asked: RefCell<Vec<TrustLineKey>>,
|
||||
/// What `mptoken_issuance_keylet` answers, by (issuer bytes, seq). An unlisted key
|
||||
/// answers `InvalidAccount`.
|
||||
pub mpt_issuance_keylets: HashMap<(Vec<u8>, i32), Answer>,
|
||||
@@ -245,9 +265,9 @@ pub struct FakeHost {
|
||||
pub oracle_keylets_asked: RefCell<Vec<(Vec<u8>, i32)>>,
|
||||
/// What `paychannel_keylet` answers, by (account, destination, seq). An unlisted
|
||||
/// key answers `InvalidAccount`.
|
||||
pub paychannel_keylets: HashMap<(Vec<u8>, Vec<u8>, i32), Answer>,
|
||||
pub paychannel_keylets: HashMap<PaychannelKey, Answer>,
|
||||
/// Every (account, destination, seq) `paychannel_keylet` was asked for.
|
||||
pub paychannel_keylets_asked: RefCell<Vec<(Vec<u8>, Vec<u8>, i32)>>,
|
||||
pub paychannel_keylets_asked: RefCell<Vec<PaychannelKey>>,
|
||||
/// What `permissioned_domain_keylet` answers, by (account bytes, seq). An unlisted
|
||||
/// key answers `InvalidAccount`.
|
||||
pub domain_keylets: HashMap<(Vec<u8>, i32), Answer>,
|
||||
@@ -335,10 +355,10 @@ pub struct FakeHost {
|
||||
pub float_compare_asked: RefCell<Vec<(Vec<u8>, Vec<u8>)>>,
|
||||
/// Every `(x, y, mode)` the four binary float operators were asked for, tagged by
|
||||
/// operator name.
|
||||
pub float_binops_asked: RefCell<Vec<(&'static str, Vec<u8>, Vec<u8>, i32)>>,
|
||||
pub float_binary_ops_asked: RefCell<Vec<FloatBinaryCall>>,
|
||||
/// Every `(x, n, mode)` `float_root` and `float_power` were asked for, tagged by
|
||||
/// operator name.
|
||||
pub float_unops_asked: RefCell<Vec<(&'static str, Vec<u8>, i32, i32)>>,
|
||||
pub float_unary_ops_asked: RefCell<Vec<FloatUnaryCall>>,
|
||||
}
|
||||
|
||||
impl Default for FakeHost {
|
||||
@@ -453,8 +473,8 @@ impl Default for FakeHost {
|
||||
float_from_mant_exp_asked: RefCell::new(Vec::new()),
|
||||
float_compare_answer: Ok(0),
|
||||
float_compare_asked: RefCell::new(Vec::new()),
|
||||
float_binops_asked: RefCell::new(Vec::new()),
|
||||
float_unops_asked: RefCell::new(Vec::new()),
|
||||
float_binary_ops_asked: RefCell::new(Vec::new()),
|
||||
float_unary_ops_asked: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1327,42 +1347,42 @@ impl HostFunctions for FakeHost {
|
||||
}
|
||||
|
||||
fn float_add(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
self.float_binops_asked
|
||||
self.float_binary_ops_asked
|
||||
.borrow_mut()
|
||||
.push(("add", x.to_vec(), y.to_vec(), mode));
|
||||
self.float_answer.fill(out)
|
||||
}
|
||||
|
||||
fn float_subtract(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
self.float_binops_asked
|
||||
self.float_binary_ops_asked
|
||||
.borrow_mut()
|
||||
.push(("sub", x.to_vec(), y.to_vec(), mode));
|
||||
self.float_answer.fill(out)
|
||||
}
|
||||
|
||||
fn float_multiply(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
self.float_binops_asked
|
||||
self.float_binary_ops_asked
|
||||
.borrow_mut()
|
||||
.push(("mult", x.to_vec(), y.to_vec(), mode));
|
||||
self.float_answer.fill(out)
|
||||
}
|
||||
|
||||
fn float_divide(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
self.float_binops_asked
|
||||
self.float_binary_ops_asked
|
||||
.borrow_mut()
|
||||
.push(("div", x.to_vec(), y.to_vec(), mode));
|
||||
self.float_answer.fill(out)
|
||||
}
|
||||
|
||||
fn float_root(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
self.float_unops_asked
|
||||
self.float_unary_ops_asked
|
||||
.borrow_mut()
|
||||
.push(("root", x.to_vec(), n, mode));
|
||||
self.float_answer.fill(out)
|
||||
}
|
||||
|
||||
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
self.float_unops_asked
|
||||
self.float_unary_ops_asked
|
||||
.borrow_mut()
|
||||
.push(("pow", x.to_vec(), n, mode));
|
||||
self.float_answer.fill(out)
|
||||
|
||||
Reference in New Issue
Block a user