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:
committed by
mergify[bot]
parent
09d064c090
commit
0a3a18744f
@ -36,6 +36,8 @@ pub struct ComputeBudget {
|
|||||||
pub sha256_base_cost: u64,
|
pub sha256_base_cost: u64,
|
||||||
/// Incremental number of units consumed by SHA256 (based on bytes)
|
/// Incremental number of units consumed by SHA256 (based on bytes)
|
||||||
pub sha256_byte_cost: u64,
|
pub sha256_byte_cost: u64,
|
||||||
|
/// Maximum number of slices hashed per syscall
|
||||||
|
pub sha256_max_slices: u64,
|
||||||
/// Maximum BPF to BPF call depth
|
/// Maximum BPF to BPF call depth
|
||||||
pub max_call_depth: usize,
|
pub max_call_depth: usize,
|
||||||
/// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend
|
/// 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,
|
max_invoke_depth: 4,
|
||||||
sha256_base_cost: 85,
|
sha256_base_cost: 85,
|
||||||
sha256_byte_cost: 1,
|
sha256_byte_cost: 1,
|
||||||
|
sha256_max_slices: 20_000,
|
||||||
max_call_depth: 64,
|
max_call_depth: 64,
|
||||||
stack_frame_size: 4_096,
|
stack_frame_size: 4_096,
|
||||||
log_pubkey_units: 100,
|
log_pubkey_units: 100,
|
||||||
|
@ -1419,7 +1419,7 @@ fn assert_instruction_count() {
|
|||||||
("sanity", 2378),
|
("sanity", 2378),
|
||||||
("sanity++", 2278),
|
("sanity++", 2278),
|
||||||
("secp256k1_recover", 25383),
|
("secp256k1_recover", 25383),
|
||||||
("sha", 1328),
|
("sha", 1895),
|
||||||
("struct_pass", 108),
|
("struct_pass", 108),
|
||||||
("struct_ret", 122),
|
("struct_ret", 122),
|
||||||
]);
|
]);
|
||||||
@ -1441,7 +1441,7 @@ fn assert_instruction_count() {
|
|||||||
("solana_bpf_rust_rand", 418),
|
("solana_bpf_rust_rand", 418),
|
||||||
("solana_bpf_rust_sanity", 9128),
|
("solana_bpf_rust_sanity", 9128),
|
||||||
("solana_bpf_rust_secp256k1_recover", 25707),
|
("solana_bpf_rust_secp256k1_recover", 25707),
|
||||||
("solana_bpf_rust_sha", 26467),
|
("solana_bpf_rust_sha", 27033),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,8 @@ pub enum SyscallError {
|
|||||||
CopyOverlapping,
|
CopyOverlapping,
|
||||||
#[error("Return data too large ({0} > {1})")]
|
#[error("Return data too large ({0} > {1})")]
|
||||||
ReturnDataTooLarge(u64, u64),
|
ReturnDataTooLarge(u64, u64),
|
||||||
|
#[error("Hashing too many sequences")]
|
||||||
|
TooManySlices,
|
||||||
}
|
}
|
||||||
impl From<SyscallError> for EbpfError<BpfError> {
|
impl From<SyscallError> for EbpfError<BpfError> {
|
||||||
fn from(error: SyscallError) -> Self {
|
fn from(error: SyscallError) -> Self {
|
||||||
@ -1064,6 +1066,20 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSha256<'a, 'b> {
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
let compute_budget = invoke_context.get_compute_budget();
|
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!(
|
question_mark!(
|
||||||
invoke_context
|
invoke_context
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
@ -1092,7 +1108,18 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallSha256<'a, 'b> {
|
|||||||
),
|
),
|
||||||
result
|
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);
|
question_mark!(invoke_context.get_compute_meter().consume(cost), result);
|
||||||
hasher.hash(bytes);
|
hasher.hash(bytes);
|
||||||
}
|
}
|
||||||
@ -1268,6 +1295,20 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallKeccak256<'a, 'b> {
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
let compute_budget = invoke_context.get_compute_budget();
|
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!(
|
question_mark!(
|
||||||
invoke_context
|
invoke_context
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
@ -1301,9 +1342,19 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallKeccak256<'a, 'b> {
|
|||||||
),
|
),
|
||||||
result
|
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);
|
question_mark!(invoke_context.get_compute_meter().consume(cost), result);
|
||||||
|
|
||||||
hasher.hash(bytes);
|
hasher.hash(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1819,6 +1870,20 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallBlake3<'a, 'b> {
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
let compute_budget = invoke_context.get_compute_budget();
|
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!(
|
question_mark!(
|
||||||
invoke_context
|
invoke_context
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
@ -1852,10 +1917,19 @@ impl<'a, 'b> SyscallObject<BpfError> for SyscallBlake3<'a, 'b> {
|
|||||||
),
|
),
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
|
let cost = if invoke_context
|
||||||
let cost = compute_budget.sha256_byte_cost * (val.len() as u64 / 2);
|
.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);
|
question_mark!(invoke_context.get_compute_meter().consume(cost), result);
|
||||||
|
|
||||||
hasher.hash(bytes);
|
hasher.hash(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3856,8 +3930,12 @@ mod tests {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.mock_set_remaining(
|
.mock_set_remaining(
|
||||||
(invoke_context.get_compute_budget().sha256_base_cost
|
(invoke_context.get_compute_budget().sha256_base_cost
|
||||||
+ ((bytes1.len() + bytes2.len()) as u64 / 2)
|
+ invoke_context.get_compute_budget().mem_op_base_cost.max(
|
||||||
* invoke_context.get_compute_budget().sha256_byte_cost)
|
invoke_context
|
||||||
|
.get_compute_budget()
|
||||||
|
.sha256_byte_cost
|
||||||
|
.saturating_mul((bytes1.len() + bytes2.len()) as u64 / 2),
|
||||||
|
))
|
||||||
* 4,
|
* 4,
|
||||||
);
|
);
|
||||||
let mut syscall = SyscallSha256 {
|
let mut syscall = SyscallSha256 {
|
||||||
|
Reference in New Issue
Block a user