forked from nervosnetwork/ckb
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add spawn fuzzing test * Fix offset error
- Loading branch information
1 parent
6c28298
commit 8f40da9
Showing
8 changed files
with
264 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,7 @@ target | |
corpus | ||
artifacts | ||
coverage | ||
flamegraph.svg | ||
perf.data | ||
perf.data.old | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
#![no_main] | ||
use libfuzzer_sys::fuzz_target; | ||
|
||
use ckb_chain_spec::consensus::ConsensusBuilder; | ||
use ckb_script::{TransactionScriptsVerifier, TxVerifyEnv}; | ||
use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider}; | ||
use ckb_types::{ | ||
bytes::Bytes, | ||
core::{ | ||
capacity_bytes, | ||
cell::{CellMetaBuilder, ResolvedTransaction}, | ||
hardfork::{HardForks, CKB2021, CKB2023}, | ||
Capacity, HeaderView, ScriptHashType, TransactionBuilder, TransactionInfo, | ||
}, | ||
h256, | ||
packed::{ | ||
self, Byte32, CellInput, CellOutput, CellOutputBuilder, OutPoint, Script, | ||
TransactionInfoBuilder, TransactionKeyBuilder, | ||
}, | ||
prelude::*, | ||
}; | ||
|
||
#[derive(Default, PartialEq, Eq, Clone)] | ||
struct MockDataLoader {} | ||
|
||
impl CellDataProvider for MockDataLoader { | ||
fn get_cell_data(&self, _out_point: &OutPoint) -> Option<Bytes> { | ||
None | ||
} | ||
|
||
fn get_cell_data_hash(&self, _out_point: &OutPoint) -> Option<Byte32> { | ||
None | ||
} | ||
} | ||
|
||
impl HeaderProvider for MockDataLoader { | ||
fn get_header(&self, _block_hash: &Byte32) -> Option<HeaderView> { | ||
None | ||
} | ||
} | ||
|
||
impl ExtensionProvider for MockDataLoader { | ||
fn get_block_extension(&self, _hash: &Byte32) -> Option<packed::Bytes> { | ||
None | ||
} | ||
} | ||
|
||
fn mock_transaction_info() -> TransactionInfo { | ||
TransactionInfoBuilder::default() | ||
.block_number(1u64.pack()) | ||
.block_epoch(0u64.pack()) | ||
.key( | ||
TransactionKeyBuilder::default() | ||
.block_hash(Byte32::zero()) | ||
.index(1u32.pack()) | ||
.build(), | ||
) | ||
.build() | ||
.unpack() | ||
} | ||
|
||
static PROGRAM_DATA: &[u8] = include_bytes!("../../testdata/spawn_fuzzing"); | ||
|
||
fn run(data: &[u8]) { | ||
if data.len() < 8 { | ||
return; | ||
} | ||
let split_offset = data[0] as usize; | ||
let split_offset = usize::min(split_offset, data.len() - 1); | ||
let parent_witness = Bytes::copy_from_slice(&data[0..split_offset]); | ||
let child_witness = Bytes::copy_from_slice(&data[split_offset..]); | ||
let witnesses = vec![parent_witness.pack(), child_witness.pack()]; | ||
|
||
let transaction = TransactionBuilder::default() | ||
.input(CellInput::new(OutPoint::null(), 0)) | ||
.set_witnesses(witnesses) | ||
.build(); | ||
|
||
let data: Bytes = (Vec::from(PROGRAM_DATA)).into(); | ||
let script = Script::new_builder() | ||
.hash_type(ScriptHashType::Data2.into()) | ||
.code_hash(CellOutput::calc_data_hash(&data)) | ||
.build(); | ||
let dep_cell = CellMetaBuilder::from_cell_output( | ||
CellOutput::new_builder() | ||
.capacity(Capacity::bytes(data.len()).unwrap().pack()) | ||
.build(), | ||
data, | ||
) | ||
.transaction_info(mock_transaction_info()) | ||
.out_point(OutPoint::new(h256!("0x0").pack(), 0)) | ||
.build(); | ||
|
||
let input_cell = CellMetaBuilder::from_cell_output( | ||
CellOutputBuilder::default() | ||
.capacity(capacity_bytes!(100).pack()) | ||
.lock(script) | ||
.build(), | ||
Bytes::new(), | ||
) | ||
.transaction_info(mock_transaction_info()) | ||
.build(); | ||
|
||
let rtx = ResolvedTransaction { | ||
transaction, | ||
resolved_cell_deps: vec![dep_cell], | ||
resolved_inputs: vec![input_cell], | ||
resolved_dep_groups: vec![], | ||
}; | ||
|
||
let provider = MockDataLoader {}; | ||
let hardfork_switch = HardForks { | ||
ckb2021: CKB2021::new_mirana().as_builder().build().unwrap(), | ||
ckb2023: CKB2023::new_mirana() | ||
.as_builder() | ||
.rfc_0049(0) | ||
.build() | ||
.unwrap(), | ||
}; | ||
let consensus = ConsensusBuilder::default() | ||
.hardfork_switch(hardfork_switch) | ||
.build(); | ||
let tx_verify_env = | ||
TxVerifyEnv::new_submit(&HeaderView::new_advanced_builder().epoch(0.pack()).build()); | ||
let verifier = TransactionScriptsVerifier::new( | ||
rtx.into(), | ||
provider, | ||
consensus.into(), | ||
tx_verify_env.into(), | ||
); | ||
let _ = verifier.verify(70_000_000); | ||
} | ||
|
||
fuzz_target!(|data: &[u8]| { | ||
run(data); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#include "spawn_utils.h" | ||
|
||
typedef enum SyscallId { SyscallRead, SyscallWrite, SyscallClose } SyscallId; | ||
|
||
typedef struct Command { | ||
SyscallId id; | ||
uint64_t buf_ptr; | ||
uint64_t len_ptr; | ||
size_t fd_index; | ||
} Command; | ||
|
||
typedef struct Data { | ||
uint8_t* ptr; | ||
uint64_t offset; | ||
uint64_t total_size; | ||
} Data; | ||
|
||
int extract_command(Data* data, Command* cmd) { | ||
if ((data->offset + 1) > data->total_size) { | ||
return -1; | ||
} | ||
uint8_t id = data->ptr[0]; | ||
|
||
if (id > 250) { | ||
cmd->id = SyscallClose; | ||
cmd->fd_index = (size_t)(id % 2); | ||
data->offset += 1; | ||
} else if (id > 128) { | ||
if ((data->offset + 7) > data->total_size) { | ||
return -1; | ||
} | ||
cmd->id = SyscallRead; | ||
memcpy(&cmd->buf_ptr, &data->ptr[data->offset + 1], 3); | ||
memcpy(&cmd->len_ptr, &data->ptr[data->offset + 4], 3); | ||
data->offset += 7; | ||
} else { | ||
if ((data->offset + 7) > data->total_size) { | ||
return -1; | ||
} | ||
cmd->id = SyscallWrite; | ||
memcpy(&cmd->buf_ptr, &data->ptr[data->offset + 1], 3); | ||
memcpy(&cmd->len_ptr, &data->ptr[data->offset + 4], 3); | ||
data->offset += 7; | ||
} | ||
return 0; | ||
} | ||
|
||
int random_read_write(uint64_t fds[2], size_t index) { | ||
int err = 0; | ||
uint8_t cmd_buf[4096] = {0}; | ||
uint64_t cmd_len = sizeof(cmd_buf); | ||
|
||
err = ckb_load_witness(cmd_buf, &cmd_len, 0, index, CKB_SOURCE_INPUT); | ||
CHECK(err); | ||
Data data = {.ptr = cmd_buf, .total_size = cmd_len, .offset = 0}; | ||
|
||
while (true) { | ||
Command cmd = {0}; | ||
err = extract_command(&data, &cmd); | ||
if (err) break; | ||
if (cmd.id == SyscallRead) { | ||
ckb_read(fds[CKB_STDIN], (void*)cmd.buf_ptr, (uint64_t*)cmd.len_ptr); | ||
// ignore error | ||
} else if (cmd.id == SyscallWrite) { | ||
ckb_write(fds[CKB_STDOUT], (void*)cmd.buf_ptr, (uint64_t*)cmd.len_ptr); | ||
// ignore error | ||
} else if (cmd.id == SyscallClose) { | ||
ckb_close(fds[cmd.fd_index]); | ||
// ignore error | ||
} else { | ||
CHECK2(false, -1); | ||
} | ||
} | ||
exit: | ||
return err; | ||
} | ||
|
||
int parent_entry() { | ||
int err = 0; | ||
uint64_t pid = 0; | ||
const char* argv[] = {"", 0}; | ||
uint64_t fds[2] = {0}; | ||
|
||
err = full_spawn(0, 1, argv, fds, &pid); | ||
CHECK(err); | ||
random_read_write(fds, 0); | ||
|
||
int8_t exit_code = 0; | ||
err = ckb_wait(pid, &exit_code); | ||
CHECK(err); | ||
CHECK(exit_code); | ||
exit: | ||
return err; | ||
} | ||
|
||
int child_entry() { | ||
int err = 0; | ||
uint64_t inherited_fds[2]; | ||
size_t inherited_fds_length = 2; | ||
err = ckb_inherited_file_descriptors(inherited_fds, &inherited_fds_length); | ||
CHECK(err); | ||
random_read_write(inherited_fds, 0); | ||
|
||
exit: | ||
return err; | ||
} | ||
|
||
int main(int argc, const char* argv[]) { | ||
if (argc > 0) { | ||
return child_entry(); | ||
} else { | ||
return parent_entry(); | ||
} | ||
} |