From 9803e19500da9b6995cc3d16869862a058c1f199 Mon Sep 17 00:00:00 2001 From: Dmitri Makarov Date: Wed, 23 Feb 2022 19:09:09 -0800 Subject: [PATCH] Update the consumed compute units cost for hashing syscalls (backport #23124) --- programs/bpf/tests/programs.rs | 2 +- programs/bpf_loader/src/syscalls.rs | 57 ++++++++++++++++++++++------- sdk/src/process_instruction.rs | 3 ++ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 34179aca2e..fadf22f387 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1384,7 +1384,7 @@ fn assert_instruction_count() { ("sanity", 1255), ("sanity++", 1260), ("secp256k1_recover", 25383), - ("sha", 1328), + ("sha", 1895), ("struct_pass", 108), ("struct_ret", 28), ]); diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 344ce2775e..66c6dc4ede 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -86,6 +86,8 @@ pub enum SyscallError { CopyOverlapping, #[error("Return data too large ({0} > {1})")] ReturnDataTooLarge(u64, u64), + #[error("Hashing too many sequences")] + TooManySlices, } impl From for EbpfError { fn from(error: SyscallError) -> Self { @@ -304,9 +306,13 @@ pub fn bind_syscall_context_objects<'a>( Box::new(SyscallSha256 { sha256_base_cost: bpf_compute_budget.sha256_base_cost, sha256_byte_cost: bpf_compute_budget.sha256_byte_cost, + sha256_max_slices: bpf_compute_budget.sha256_max_slices, + mem_op_base_cost: bpf_compute_budget.mem_op_base_cost, compute_meter: invoke_context.get_compute_meter(), loader_id, enforce_aligned_host_addrs, + update_syscall_base_costs: invoke_context + .is_feature_active(&update_syscall_base_costs::id()), }), None, )?; @@ -317,8 +323,12 @@ pub fn bind_syscall_context_objects<'a>( Box::new(SyscallKeccak256 { base_cost: bpf_compute_budget.sha256_base_cost, byte_cost: bpf_compute_budget.sha256_byte_cost, + max_slices: bpf_compute_budget.sha256_max_slices, + mem_op_base_cost: bpf_compute_budget.mem_op_base_cost, compute_meter: invoke_context.get_compute_meter(), loader_id, + update_syscall_base_costs: invoke_context + .is_feature_active(&update_syscall_base_costs::id()), }), ); @@ -1109,9 +1119,12 @@ impl<'a> SyscallObject for SyscallTryFindProgramAddress<'a> { pub struct SyscallSha256<'a> { sha256_base_cost: u64, sha256_byte_cost: u64, + sha256_max_slices: u64, + mem_op_base_cost: u64, compute_meter: Rc>, loader_id: &'a Pubkey, enforce_aligned_host_addrs: bool, + update_syscall_base_costs: bool, } impl<'a> SyscallObject for SyscallSha256<'a> { fn call( @@ -1124,6 +1137,10 @@ impl<'a> SyscallObject for SyscallSha256<'a> { memory_mapping: &MemoryMapping, result: &mut Result>, ) { + if self.update_syscall_base_costs && self.sha256_max_slices < vals_len { + *result = Err(SyscallError::TooManySlices.into()); + return; + } question_mark!(self.compute_meter.consume(self.sha256_base_cost), result); let hash_result = question_mark!( translate_slice_mut::( @@ -1158,11 +1175,13 @@ impl<'a> SyscallObject for SyscallSha256<'a> { ), result ); - question_mark!( - self.compute_meter - .consume(self.sha256_byte_cost * (val.len() as u64 / 2)), - result - ); + let cost = if self.update_syscall_base_costs { + self.mem_op_base_cost + .max(self.sha256_byte_cost.saturating_mul(val.len() as u64 / 2)) + } else { + self.sha256_byte_cost * (val.len() as u64 / 2) + }; + question_mark!(self.compute_meter.consume(cost), result); hasher.hash(bytes); } } @@ -1323,8 +1342,11 @@ impl<'a> SyscallObject for SyscallGetRentSysvar<'a> { pub struct SyscallKeccak256<'a> { base_cost: u64, byte_cost: u64, + max_slices: u64, + mem_op_base_cost: u64, compute_meter: Rc>, loader_id: &'a Pubkey, + update_syscall_base_costs: bool, } impl<'a> SyscallObject for SyscallKeccak256<'a> { fn call( @@ -1337,6 +1359,10 @@ impl<'a> SyscallObject for SyscallKeccak256<'a> { memory_mapping: &MemoryMapping, result: &mut Result>, ) { + if self.update_syscall_base_costs && self.max_slices < vals_len { + *result = Err(SyscallError::TooManySlices.into()); + return; + } question_mark!(self.compute_meter.consume(self.base_cost), result); let hash_result = question_mark!( translate_slice_mut::( @@ -1365,11 +1391,13 @@ impl<'a> SyscallObject for SyscallKeccak256<'a> { ), result ); - question_mark!( - self.compute_meter - .consume(self.byte_cost * (val.len() as u64 / 2)), - result - ); + let cost = if self.update_syscall_base_costs { + self.mem_op_base_cost + .max(self.byte_cost.saturating_mul(val.len() as u64 / 2)) + } else { + self.byte_cost * (val.len() as u64 / 2) + }; + question_mark!(self.compute_meter.consume(cost), result); hasher.hash(bytes); } } @@ -3646,14 +3674,17 @@ mod tests { .unwrap(); let compute_meter: Rc> = Rc::new(RefCell::new(MockComputeMeter { - remaining: (bytes1.len() + bytes2.len()) as u64, + remaining: (85 + 10u64.max((bytes1.len() + bytes2.len()) as u64 / 2)) * 4, })); let mut syscall = SyscallSha256 { - sha256_base_cost: 0, - sha256_byte_cost: 2, + sha256_base_cost: 85, + sha256_byte_cost: 1, + sha256_max_slices: 20_000, + mem_op_base_cost: 10, compute_meter, loader_id: &bpf_loader_deprecated::id(), enforce_aligned_host_addrs: true, + update_syscall_base_costs: true, }; let mut result: Result> = Ok(0); diff --git a/sdk/src/process_instruction.rs b/sdk/src/process_instruction.rs index 78aebdad8c..bbf44c8615 100644 --- a/sdk/src/process_instruction.rs +++ b/sdk/src/process_instruction.rs @@ -164,6 +164,8 @@ pub struct BpfComputeBudget { pub sha256_base_cost: u64, /// Incremental number of units consumed by SHA256 (based on bytes) pub sha256_byte_cost: u64, + /// Maximum number of slices hashed per syscall + pub sha256_max_slices: u64, /// Maximum BPF to BPF call depth pub max_call_depth: usize, /// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend @@ -205,6 +207,7 @@ impl BpfComputeBudget { max_invoke_depth: 4, sha256_base_cost: 85, sha256_byte_cost: 1, + sha256_max_slices: 20_000, max_call_depth: 64, stack_frame_size: 4_096, log_pubkey_units: 100,