add sha256 syscall (#12569)

This commit is contained in:
Jack May 2020-09-29 23:29:20 -07:00 committed by GitHub
parent 89424b29bf
commit 058bca6632
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 321 additions and 6 deletions

View File

@ -1912,6 +1912,13 @@ dependencies = [
"solana-sdk",
]
[[package]]
name = "solana-bpf-rust-sha256"
version = "1.4.0"
dependencies = [
"solana-sdk",
]
[[package]]
name = "solana-bpf-rust-sysval"
version = "1.4.0"

View File

@ -54,6 +54,7 @@ members = [
"rust/param_passing_dep",
"rust/rand",
"rust/sanity",
"rust/sha256",
"rust/sysval",
]

View File

@ -82,6 +82,7 @@ fn main() {
"param_passing",
"rand",
"sanity",
"sha256",
"sysval",
];
for program in rust_programs.iter() {

View File

@ -0,0 +1,25 @@
/**
* @brief SHA256 Syscall test
*/
#include <solana_sdk.h>
extern uint64_t entrypoint(const uint8_t *input) {
uint8_t result[SHA256_RESULT_LENGTH];
uint8_t expected[] = {0x9f, 0xa2, 0x7e, 0x8f, 0x7b, 0xc1, 0xec, 0xe8,
0xae, 0x7b, 0x9a, 0x91, 0x46, 0x53, 0x20, 0xf,
0x1c, 0x22, 0x8e, 0x56, 0x10, 0x30, 0x59, 0xfd,
0x35, 0x8d, 0x57, 0x54, 0x96, 0x47, 0x2c, 0xc9};
uint8_t bytes1[] = {'G', 'a', 'g', 'g', 'a', 'b', 'l', 'a',
'g', 'h', 'b', 'l', 'a', 'g', 'h', '!'};
uint8_t bytes2[] = {'f', 'l', 'u', 'r', 'b', 'o', 's'};
const SolBytes bytes[] = {{bytes1, SOL_ARRAY_SIZE(bytes1)},
{bytes2, SOL_ARRAY_SIZE(bytes2)}};
sol_sha256(bytes, SOL_ARRAY_SIZE(bytes), result);
sol_assert(0 == sol_memcmp(result, expected, SHA256_RESULT_LENGTH));
return SUCCESS;
}

View File

@ -18,6 +18,16 @@ extern uint64_t entrypoint(const uint8_t *input) {
*val = *val + 1;
}
}
// // Uncomment for SHA256 syscall
// {
// uint8_t result[SHA256_RESULT_LENGTH];
// uint8_t bytes1[1024];
// const SolBytes bytes[] = {{bytes1, SOL_ARRAY_SIZE(bytes1)}};
// sol_sha256(bytes, SOL_ARRAY_SIZE(bytes), result);
// *val = result[0];
// }
}
return *val;
}

View File

@ -0,0 +1,26 @@
# Note: This crate must be built using do.sh
[package]
name = "solana-bpf-rust-sha256"
version = "1.4.0"
description = "Solana BPF test program written in Rust"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../../../sdk/", version = "1.4.0", default-features = false }
[features]
program = ["solana-sdk/program"]
default = ["program", "solana-sdk/default"]
[lib]
name = "solana_bpf_rust_sha256"
crate-type = ["cdylib"]
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -0,0 +1,19 @@
//! @brief SHA256 Syscall test
extern crate solana_sdk;
use solana_sdk::{
hash::{hashv, Hasher},
info,
};
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
info!("sha256");
let vals = &["Gaggablaghblagh!".as_ref(), "flurbos".as_ref()];
let mut hasher = Hasher::default();
hasher.hashv(vals);
assert_eq!(hashv(vals), hasher.result());
0
}

View File

@ -136,6 +136,7 @@ fn test_program_bpf_sanity() {
("relative_call", true),
("sanity", true),
("sanity++", true),
("sha256", true),
("struct_pass", true),
("struct_ret", true),
]);
@ -154,6 +155,7 @@ fn test_program_bpf_sanity() {
("solana_bpf_rust_param_passing", true),
("solana_bpf_rust_rand", true),
("solana_bpf_rust_sanity", true),
("solana_bpf_rust_sha256", true),
("solana_bpf_rust_sysval", true),
]);
}

View File

@ -601,6 +601,8 @@ mod tests {
create_program_address_units: 1500,
invoke_units: 1000,
max_invoke_depth: 2,
sha256_base_cost: 85,
sha256_byte_cost: 1,
},
Rc::new(RefCell::new(Executors::default())),
None,

View File

@ -7,6 +7,7 @@ use solana_rbpf::{
vm::{EbpfVm, SyscallObject},
};
use solana_runtime::{
feature_set::sha256_syscall_enabled,
message_processor::MessageProcessor,
process_instruction::{ComputeMeter, InvokeContext, Logger},
};
@ -16,6 +17,7 @@ use solana_sdk::{
account_info::AccountInfo,
bpf_loader, bpf_loader_deprecated,
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
hash::{Hasher, HASH_BYTES},
instruction::{AccountMeta, Instruction, InstructionError},
message::Message,
program_error::ProgramError,
@ -117,6 +119,18 @@ pub fn register_syscalls<'a>(
}),
)?;
if invoke_context.is_feature_active(&sha256_syscall_enabled::id()) {
vm.register_syscall_with_context_ex(
"sol_sha256",
Box::new(SyscallSha256 {
sha256_base_cost: compute_budget.sha256_base_cost,
sha256_byte_cost: compute_budget.sha256_byte_cost,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
)?;
}
vm.register_syscall_with_context_ex(
"sol_create_program_address",
Box::new(SyscallCreateProgramAddress {
@ -460,6 +474,43 @@ impl<'a> SyscallObject<BPFError> for SyscallCreateProgramAddress<'a> {
}
}
/// SHA256
pub struct SyscallSha256<'a> {
sha256_base_cost: u64,
sha256_byte_cost: u64,
compute_meter: Rc<RefCell<dyn ComputeMeter>>,
loader_id: &'a Pubkey,
}
impl<'a> SyscallObject<BPFError> for SyscallSha256<'a> {
fn call(
&mut self,
vals_addr: u64,
vals_len: u64,
result_addr: u64,
_arg4: u64,
_arg5: u64,
ro_regions: &[MemoryRegion],
rw_regions: &[MemoryRegion],
) -> Result<u64, EbpfError<BPFError>> {
self.compute_meter.consume(self.sha256_base_cost)?;
let hash_result =
translate_slice_mut!(u8, result_addr, HASH_BYTES, rw_regions, self.loader_id)?;
let mut hasher = Hasher::default();
if vals_len > 0 {
let vals = translate_slice!(&[u8], vals_addr, vals_len, ro_regions, self.loader_id)?;
for val in vals.iter() {
let bytes =
translate_slice!(u8, val.as_ptr(), val.len(), ro_regions, self.loader_id)?;
self.compute_meter
.consume(self.sha256_byte_cost * (val.len() as u64 / 2))?;
hasher.hash(bytes);
}
}
hash_result.copy_from_slice(&hasher.result().to_bytes());
Ok(0)
}
}
// Cross-program invocation syscalls
struct AccountReferences<'a> {
@ -1095,6 +1146,7 @@ fn call<'a>(
mod tests {
use super::*;
use crate::tests::{MockComputeMeter, MockLogger};
use solana_sdk::hash::hashv;
macro_rules! assert_access_violation {
($result:expr, $va:expr, $len:expr) => {
@ -1449,4 +1501,115 @@ mod tests {
check_alignment::<u64>();
check_alignment::<u128>();
}
#[test]
fn test_syscall_sha256() {
let bytes1 = "Gaggablaghblagh!";
let bytes2 = "flurbos";
struct MockSlice {
pub addr: u64,
pub len: usize,
}
let mock_slice1 = MockSlice {
addr: 4096,
len: bytes1.len(),
};
let mock_slice2 = MockSlice {
addr: 8192,
len: bytes2.len(),
};
let bytes_to_hash = [mock_slice1, mock_slice2]; // TODO
let ro_len = bytes_to_hash.len() as u64;
let ro_va = 96;
let ro_regions = &mut [
MemoryRegion {
addr_host: bytes1.as_ptr() as *const _ as u64,
addr_vm: 4096,
len: bytes1.len() as u64,
},
MemoryRegion {
addr_host: bytes2.as_ptr() as *const _ as u64,
addr_vm: 8192,
len: bytes2.len() as u64,
},
MemoryRegion {
addr_host: bytes_to_hash.as_ptr() as *const _ as u64,
addr_vm: 96,
len: 32,
},
];
ro_regions.sort_by(|a, b| a.addr_vm.cmp(&b.addr_vm));
let hash_result = [0; HASH_BYTES];
let rw_va = 192;
let rw_regions = &[MemoryRegion {
addr_host: hash_result.as_ptr() as *const _ as u64,
addr_vm: rw_va,
len: HASH_BYTES as u64,
}];
let compute_meter: Rc<RefCell<dyn ComputeMeter>> =
Rc::new(RefCell::new(MockComputeMeter {
remaining: (bytes1.len() + bytes2.len()) as u64,
}));
let mut syscall = SyscallSha256 {
sha256_base_cost: 0,
sha256_byte_cost: 2,
compute_meter,
loader_id: &bpf_loader_deprecated::id(),
};
syscall
.call(ro_va, ro_len, rw_va, 0, 0, ro_regions, rw_regions)
.unwrap();
let hash_local = hashv(&[bytes1.as_ref(), bytes2.as_ref()]).to_bytes();
assert_eq!(hash_result, hash_local);
assert_access_violation!(
syscall.call(
ro_va - 1, // AccessViolation
ro_len,
rw_va,
0,
0,
ro_regions,
rw_regions
),
ro_va - 1,
ro_len
);
assert_access_violation!(
syscall.call(
ro_va,
ro_len + 1, // AccessViolation
rw_va,
0,
0,
ro_regions,
rw_regions
),
ro_va,
ro_len + 1
);
assert_access_violation!(
syscall.call(
ro_va,
ro_len,
rw_va - 1, // AccessViolation
0,
0,
ro_regions,
rw_regions
),
rw_va - 1,
HASH_BYTES as u64
);
assert_eq!(
Err(EbpfError::UserError(BPFError::SyscallError(
SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded)
))),
syscall.call(ro_va, ro_len, rw_va, 0, 0, ro_regions, rw_regions)
);
}
}

View File

@ -33,6 +33,10 @@ pub mod compute_budget_config2 {
solana_sdk::declare_id!("HxvjqDSiF5sYdSYuCXsUnS8UeAoWsMT9iGoFP8pgV1mB");
}
pub mod sha256_syscall_enabled {
solana_sdk::declare_id!("D7KfP7bZxpkYtD4Pc38t9htgs1k5k47Yhxe4rp6WDVi8");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -43,6 +47,7 @@ lazy_static! {
(spl_token_v2_multisig_fix::id(), "spl-token multisig fix"),
(bpf_loader2_program::id(), "bpf_loader2 program"),
(compute_budget_config2::id(), "1ms compute budget"),
(sha256_syscall_enabled::id(), "sha256 syscall")
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -427,6 +427,8 @@ impl MessageProcessor {
create_program_address_units: 0,
invoke_units: 0,
max_invoke_depth: 2,
sha256_base_cost: 0,
sha256_byte_cost: 0,
}
}
}

View File

@ -87,6 +87,10 @@ pub struct ComputeBudget {
pub invoke_units: u64,
/// Maximum cross-program invocation depth allowed including the orignal caller
pub max_invoke_depth: usize,
/// Base number of compute units consumed to call sha256
pub sha256_base_cost: u64,
/// Incremental number of units consumed by sha256 (based on bytes)
pub sha256_byte_cost: u64,
}
impl Default for ComputeBudget {
fn default() -> Self {
@ -98,6 +102,8 @@ impl Default for ComputeBudget {
create_program_address_units: 1500,
invoke_units: 1000,
max_invoke_depth: 2,
sha256_base_cost: 85,
sha256_byte_cost: 1,
}
}
}

View File

@ -391,6 +391,32 @@ static bool sol_deserialize(
return true;
}
/**
* Byte array pointer and string
*/
typedef struct {
const uint8_t *addr; /** bytes */
uint64_t len; /** number of bytes*/
} SolBytes;
/**
* Length of a sha256 hash result
*/
#define SHA256_RESULT_LENGTH 32
/**
* Sha256
*
* @param bytes Array of byte arrays
* @param bytes_len Number of byte arrays
* @param result 32 byte array to hold the result
*/
static uint64_t sol_sha256(
const SolBytes *bytes,
int bytes_len,
const uint8_t *result
);
/**
* Account Meta
*/
@ -428,7 +454,7 @@ typedef struct {
uint64_t len; /** Number of seeds */
} SolSignerSeeds;
/*
/**
* Create a program address
*
* @param seeds Seed bytes used to sign program accounts
@ -448,7 +474,7 @@ static uint64_t sol_create_program_address(
* * @{
*/
/*
/**
* Invoke another program and sign for some of the keys
*
* @param instruction Instruction to process
@ -480,7 +506,7 @@ static uint64_t sol_invoke_signed(
signers_seeds_len
);
}
/*
/**
* Invoke another program
*
* @param instruction Instruction to process

View File

@ -103,9 +103,27 @@ impl Hash {
/// Return a Sha256 hash for the given data.
pub fn hashv(vals: &[&[u8]]) -> Hash {
let mut hasher = Hasher::default();
hasher.hashv(vals);
hasher.result()
#[cfg(not(feature = "program"))]
{
let mut hasher = Hasher::default();
hasher.hashv(vals);
hasher.result()
}
#[cfg(feature = "program")]
{
extern "C" {
fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
};
let mut hash_result = [0; HASH_BYTES];
unsafe {
sol_sha256(
vals as *const _ as *const u8,
vals.len() as u64,
&mut hash_result as *mut _ as *mut u8,
);
}
Hash::new_from_array(hash_result)
}
}
/// Return a Sha256 hash for the given data.