diff --git a/fvm/src/kernel/default.rs b/fvm/src/kernel/default.rs index 66b3cddb42..151676853b 100644 --- a/fvm/src/kernel/default.rs +++ b/fvm/src/kernel/default.rs @@ -1,6 +1,5 @@ // Copyright 2021-2023 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use byteorder::{BigEndian, ByteOrder}; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; use std::panic::{self, UnwindSafe}; @@ -18,7 +17,7 @@ use fvm_shared::consensus::ConsensusFault; use fvm_shared::crypto::signature; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ErrorNumber; -use fvm_shared::event::{ActorEvent, Entry, Flags}; +use fvm_shared::event::{ActorEvent, Entry, EntryFixed, Flags}; use fvm_shared::piece::{zero_piece_commitment, PaddedPieceSize}; use fvm_shared::sector::RegisteredPoStProof::{StackedDRGWindow32GiBV1, StackedDRGWindow32GiBV1P1}; use fvm_shared::sector::{RegisteredPoStProof, SectorInfo}; @@ -1050,49 +1049,55 @@ where // parse the fixed sized fields from the raw_evt buffer // - let flags = match Flags::from_bits(BigEndian::read_u64(&view[..8])) { - Some(f) => f, - None => return Err(syscall_error!(IllegalArgument, "invalid flag").into()), + let entry_fixed: EntryFixed = unsafe { + std::mem::transmute::<[u8; BYTES_PER_ENTRY], EntryFixed>(view.try_into().unwrap()) }; - let key_len = BigEndian::read_u32(&view[8..12]); - let codec = BigEndian::read_u64(&view[12..20]); - let val_len = BigEndian::read_u32(&view[20..24]); // make sure that the fixed parsed values are within bounds before we do any allocation // - if key_len > MAX_KEY_LEN as u32 { - return Err(syscall_error!(IllegalArgument; "event key exceeded max size: {} > {MAX_KEY_LEN}", key_len).into()); + let flags = entry_fixed.flags; + if Flags::from_bits(flags.bits()).is_none() { + return Err( + syscall_error!(IllegalArgument; "event flags are invalid: {}", flags.bits()) + .into(), + ); } - if val_len > MAX_VALUE_LEN as u32 { - return Err(syscall_error!(IllegalArgument; "event value exceeded max size: {} > {MAX_VALUE_LEN}", val_len).into()); + if entry_fixed.key_len > MAX_KEY_LEN as u32 { + return Err(syscall_error!(IllegalArgument; "event key exceeded max size: {} > {MAX_KEY_LEN}", entry_fixed.key_len as u32).into()); } - if codec != IPLD_RAW { + if entry_fixed.val_len > MAX_VALUE_LEN as u32 { + return Err(syscall_error!(IllegalArgument; "event value exceeded max size: {} > {MAX_VALUE_LEN}", entry_fixed.val_len as u32).into()); + } + if entry_fixed.codec != IPLD_RAW { return Err( - syscall_error!(IllegalCodec; "event codec must be IPLD_RAW, was: {}", codec) + syscall_error!(IllegalCodec; "event codec must be IPLD_RAW, was: {}", entry_fixed.codec as u64) .into(), ); } // parse the variable sized fields from the raw_key/raw_val buffers // - let key = match std::str::from_utf8(&raw_key[key_offset..key_offset + key_len as usize]) - { + let key = match std::str::from_utf8( + &raw_key[key_offset..key_offset + entry_fixed.key_len as usize], + ) { Ok(s) => s, - Err(_) => return Err(syscall_error!(IllegalArgument, "event key").into()), + Err(e) => { + return Err(syscall_error!(IllegalArgument, "invalid event key: {}", e).into()) + } }; - let value = &raw_val[val_offset..val_offset + val_len as usize]; + let value = &raw_val[val_offset..val_offset + entry_fixed.val_len as usize]; // we have all we need to construct a new Entry let entry = Entry { - flags, + flags: entry_fixed.flags, key: key.to_string(), - codec, + codec: entry_fixed.codec, value: value.to_vec(), }; // shift the key/value offsets - key_offset += key_len as usize; - val_offset += val_len as usize; + key_offset += entry_fixed.key_len as usize; + val_offset += entry_fixed.val_len as usize; entries.push(entry); } diff --git a/sdk/src/event.rs b/sdk/src/event.rs index 4c72e132df..de3140ce20 100644 --- a/sdk/src/event.rs +++ b/sdk/src/event.rs @@ -1,56 +1,50 @@ +use std::mem::size_of; + // Copyright 2021-2023 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT use crate::{sys, SyscallResult}; -use byteorder::{BigEndian, ByteOrder}; -use fvm_shared::event::ActorEvent; +use fvm_shared::event::{ActorEvent, EntryFixed}; pub fn emit_event(evt: &ActorEvent) -> SyscallResult<()> { // we manually serialize the ActorEvent (not using CBOR) into three byte arrays so // we can accurately charge gas without needing to parse anything inside the FVM - const BYTES_PER_ENTRY: usize = 24; - let mut total_key_len: usize = 0; let mut total_val_len: usize = 0; - let mut v = vec![0u8; evt.entries.len() * BYTES_PER_ENTRY]; + let mut fixed_entries = Vec::with_capacity(evt.entries.len()); for i in 0..evt.entries.len() { let e = &evt.entries[i]; - let offset = i * BYTES_PER_ENTRY; - let view = &mut v[offset..offset + BYTES_PER_ENTRY]; - BigEndian::write_u64(&mut view[..8], e.flags.bits()); - BigEndian::write_u32(&mut view[8..12], e.key.len() as u32); - BigEndian::write_u64(&mut view[12..20], e.codec); - BigEndian::write_u32(&mut view[20..24], e.value.len() as u32); + fixed_entries.push(EntryFixed { + flags: e.flags, + codec: e.codec, + key_len: e.key.len() as u32, + val_len: e.value.len() as u32, + }); total_key_len += e.key.len(); total_val_len += e.value.len(); } - let mut keys = vec![0u8; total_key_len]; - let mut offset: usize = 0; + let mut keys = Vec::with_capacity(total_key_len); for i in 0..evt.entries.len() { - let e = &evt.entries[i]; - keys[offset..offset + e.key.len()].copy_from_slice(e.key.as_bytes()); - offset += e.key.len(); + keys.extend_from_slice(evt.entries[i].key.as_bytes()); } - let mut values = vec![0u8; total_val_len]; - let mut offset: usize = 0; + let mut values = Vec::with_capacity(total_val_len); for i in 0..evt.entries.len() { - let e = &evt.entries[i]; - values[offset..offset + e.value.len()].copy_from_slice(e.value.as_slice()); - offset += e.value.len(); + values.extend_from_slice(evt.entries[i].value.as_slice()); } unsafe { + let fixed_size_in_bytes = fixed_entries.len() * size_of::(); sys::event::emit_event( - v.as_slice().as_ptr(), - v.as_slice().len() as u32, - keys.as_slice().as_ptr(), - keys.as_slice().len() as u32, - values.as_slice().as_ptr(), - values.as_slice().len() as u32, + fixed_entries.as_ptr() as *const u8, + fixed_size_in_bytes as u32, + keys.as_ptr(), + keys.len() as u32, + values.as_ptr(), + values.len() as u32, ) } } diff --git a/shared/src/event/mod.rs b/shared/src/event/mod.rs index a57d2097f2..faab88faee 100644 --- a/shared/src/event/mod.rs +++ b/shared/src/event/mod.rs @@ -60,3 +60,12 @@ pub struct Entry { #[serde(with = "strict_bytes")] pub value: Vec, } + +// A fixed sized struct for serializing Entry separately from the key/value bytes. +#[repr(C, packed)] +pub struct EntryFixed { + pub flags: Flags, + pub codec: u64, + pub key_len: u32, + pub val_len: u32, +}