Update the consumed compute units cost for hashing syscalls
This change prevents zero-cost computation of hash functions on unbound number of zero-length slices of data. The cost for each slice is at least equal to the base cost of a memory operation, but could be more for longer slices.
This commit is contained in:
parent
09d064c090
commit
0a3a18744f
|
@ -36,6 +36,8 @@ pub struct ComputeBudget {
|
|||
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
|
||||
|
@ -84,6 +86,7 @@ impl ComputeBudget {
|
|||
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,
|
||||
|
|
|
@ -1419,7 +1419,7 @@ fn assert_instruction_count() {
|
|||
("sanity", 2378),
|
||||
("sanity++", 2278),
|
||||
("secp256k1_recover", 25383),
|
||||
("sha", 1328),
|
||||
("sha", 1895),
|
||||
("struct_pass", 108),
|
||||
("struct_ret", 122),
|
||||
]);
|
||||
|
@ -1441,7 +1441,7 @@ fn assert_instruction_count() {
|
|||
("solana_bpf_rust_rand", 418),
|
||||
("solana_bpf_rust_sanity", 9128),
|
||||
("solana_bpf_rust_secp256k1_recover", 25707),
|
||||
("solana_bpf_rust_sha", 26467),
|
||||
("solana_bpf_rust_sha", 27033),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -90,6 +90,8 @@ pub enum SyscallError {
|
|||
CopyOverlapping,
|
||||
#[error("Return data too large ({0} > {1})")]
|
||||
ReturnDataTooLarge(u64, u64),
|
||||
#[error("Hashing too many sequences")]
|
||||
TooManySlices,
|
||||
}
|
||||
impl From<SyscallError> for EbpfError<BpfError> {
|
||||
fn from(error: SyscallError) -> Self {
|
||||
|
@ -1064,6 +1066,20 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSha256<'a, 'b> {
|
|||
result
|
||||
);
|
||||
let compute_budget = invoke_context.get_compute_budget();
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&update_syscall_base_costs::id())
|
||||
&& compute_budget.sha256_max_slices < vals_len
|
||||
{
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Sha256 hashing {} sequences in one syscall is over the limit {}",
|
||||
vals_len,
|
||||
compute_budget.sha256_max_slices,
|
||||
);
|
||||
*result = Err(SyscallError::TooManySlices.into());
|
||||
return;
|
||||
}
|
||||
question_mark!(
|
||||
invoke_context
|
||||
.get_compute_meter()
|
||||
|
@ -1092,7 +1108,18 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSha256<'a, 'b> {
|
|||
),
|
||||
result
|
||||
);
|
||||
let cost = compute_budget.sha256_byte_cost * (val.len() as u64 / 2);
|
||||
let cost = if invoke_context
|
||||
.feature_set
|
||||
.is_active(&update_syscall_base_costs::id())
|
||||
{
|
||||
compute_budget.mem_op_base_cost.max(
|
||||
compute_budget
|
||||
.sha256_byte_cost
|
||||
.saturating_mul(val.len() as u64 / 2),
|
||||
)
|
||||
} else {
|
||||
compute_budget.sha256_byte_cost * (val.len() as u64 / 2)
|
||||
};
|
||||
question_mark!(invoke_context.get_compute_meter().consume(cost), result);
|
||||
hasher.hash(bytes);
|
||||
}
|
||||
|
@ -1268,6 +1295,20 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallKeccak256<'a, 'b> {
|
|||
result
|
||||
);
|
||||
let compute_budget = invoke_context.get_compute_budget();
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&update_syscall_base_costs::id())
|
||||
&& compute_budget.sha256_max_slices < vals_len
|
||||
{
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Keccak256 hashing {} sequences in one syscall is over the limit {}",
|
||||
vals_len,
|
||||
compute_budget.sha256_max_slices,
|
||||
);
|
||||
*result = Err(SyscallError::TooManySlices.into());
|
||||
return;
|
||||
}
|
||||
question_mark!(
|
||||
invoke_context
|
||||
.get_compute_meter()
|
||||
|
@ -1301,9 +1342,19 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallKeccak256<'a, 'b> {
|
|||
),
|
||||
result
|
||||
);
|
||||
let cost = compute_budget.sha256_byte_cost * (val.len() as u64 / 2);
|
||||
let cost = if invoke_context
|
||||
.feature_set
|
||||
.is_active(&update_syscall_base_costs::id())
|
||||
{
|
||||
compute_budget.mem_op_base_cost.max(
|
||||
compute_budget
|
||||
.sha256_byte_cost
|
||||
.saturating_mul(val.len() as u64 / 2),
|
||||
)
|
||||
} else {
|
||||
compute_budget.sha256_byte_cost * (val.len() as u64 / 2)
|
||||
};
|
||||
question_mark!(invoke_context.get_compute_meter().consume(cost), result);
|
||||
|
||||
hasher.hash(bytes);
|
||||
}
|
||||
}
|
||||
|
@ -1819,6 +1870,20 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallBlake3<'a, 'b> {
|
|||
result
|
||||
);
|
||||
let compute_budget = invoke_context.get_compute_budget();
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&update_syscall_base_costs::id())
|
||||
&& compute_budget.sha256_max_slices < vals_len
|
||||
{
|
||||
ic_msg!(
|
||||
invoke_context,
|
||||
"Blake3 hashing {} sequences in one syscall is over the limit {}",
|
||||
vals_len,
|
||||
compute_budget.sha256_max_slices,
|
||||
);
|
||||
*result = Err(SyscallError::TooManySlices.into());
|
||||
return;
|
||||
}
|
||||
question_mark!(
|
||||
invoke_context
|
||||
.get_compute_meter()
|
||||
|
@ -1852,10 +1917,19 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallBlake3<'a, 'b> {
|
|||
),
|
||||
result
|
||||
);
|
||||
|
||||
let cost = compute_budget.sha256_byte_cost * (val.len() as u64 / 2);
|
||||
let cost = if invoke_context
|
||||
.feature_set
|
||||
.is_active(&update_syscall_base_costs::id())
|
||||
{
|
||||
compute_budget.mem_op_base_cost.max(
|
||||
compute_budget
|
||||
.sha256_byte_cost
|
||||
.saturating_mul(val.len() as u64 / 2),
|
||||
)
|
||||
} else {
|
||||
compute_budget.sha256_byte_cost * (val.len() as u64 / 2)
|
||||
};
|
||||
question_mark!(invoke_context.get_compute_meter().consume(cost), result);
|
||||
|
||||
hasher.hash(bytes);
|
||||
}
|
||||
}
|
||||
|
@ -3856,8 +3930,12 @@ mod tests {
|
|||
.borrow_mut()
|
||||
.mock_set_remaining(
|
||||
(invoke_context.get_compute_budget().sha256_base_cost
|
||||
+ ((bytes1.len() + bytes2.len()) as u64 / 2)
|
||||
* invoke_context.get_compute_budget().sha256_byte_cost)
|
||||
+ invoke_context.get_compute_budget().mem_op_base_cost.max(
|
||||
invoke_context
|
||||
.get_compute_budget()
|
||||
.sha256_byte_cost
|
||||
.saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2),
|
||||
))
|
||||
* 4,
|
||||
);
|
||||
let mut syscall = SyscallSha256 {
|
||||
|
|
Loading…
Reference in New Issue