1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4
5pub mod generated {
6 pub mod traces {
7 use once_cell::sync::Lazy;
8 use prost_reflect::DescriptorPool;
9
10 pub static DESCRIPTOR_POOL: Lazy<DescriptorPool> = Lazy::new(|| {
11 DescriptorPool::decode(include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin")).as_ref())
12 .unwrap()
13 });
14
15 include!(concat!(env!("OUT_DIR"), "/generated.traces.rs"));
16 }
17}
18
19use crate::generated::traces;
20use alloc::{boxed::Box, collections::VecDeque, vec::Vec};
21use ckb_std::{
22 ckb_constants::{CellField, HeaderField, InputField, Place, Source},
23 syscalls::traits::{Bounds, Error, IoResult, SyscallImpls},
24};
25use ckb_vm::SupportMachine;
26use ckb_vm_fuzzing_utils::{CkbvmRunnerImpls, exit_with_panic, flatten_args};
27use core::ffi::CStr;
28use core::marker::PhantomData;
29use prost::Message;
30use spin::Mutex;
31
32pub const UNEXPECTED: u64 = 19;
33pub const UNEXPECTED_ERROR: Error = Error::Other(UNEXPECTED);
34pub const UNEXPECTED_RESULT: IoResult = IoResult::Error(UNEXPECTED_ERROR);
35
36#[cfg(feature = "std")]
37pub fn entry<F>(data: &[u8], f: F) -> i8
38where
39 F: Fn() -> i8 + std::panic::UnwindSafe,
40{
41 let Some(impls) = ProtobufImpls::new_with_bytes(data) else {
42 return UNEXPECTED as i8;
43 };
44 let (argc, argv) = flatten_args(impls.args());
45 let argv = unsafe { core::slice::from_raw_parts(argv.as_ptr() as *const _, argc) };
46 ckb_vm_fuzzing_utils::entry(impls, f, argv)
47}
48
49pub struct ProtobufImpls {
50 syscalls: Mutex<VecDeque<traces::Syscall>>,
51 args: Vec<Vec<u8>>,
52 debug_printer: Box<dyn Fn(&str) + Send + Sync>,
53}
54
55impl ProtobufImpls {
56 fn new(syscalls: traces::Syscalls) -> Option<Self> {
57 Some(Self {
58 syscalls: Mutex::new(syscalls.syscalls.into()),
59 args: syscalls.args,
60 #[allow(unused_variables)]
61 debug_printer: Box::new(|message| {
62 #[cfg(feature = "print-debug-messages")]
63 eprintln!("Script debug message: {}", message);
64 }),
65 })
66 }
67
68 pub fn new_with_bytes<B: AsRef<[u8]>>(bytes: B) -> Option<Self> {
69 #[cfg(feature = "text-format")]
70 let bytes = {
71 let descriptor = crate::generated::traces::DESCRIPTOR_POOL
72 .get_message_by_name("generated.traces.Syscalls")
73 .expect("extracting protobuf message descriptor");
74 let Ok(s) = str::from_utf8(bytes.as_ref()) else {
75 return None;
76 };
77 let Ok(dmessage) = prost_reflect::DynamicMessage::parse_text_format(descriptor, s) else {
78 return None;
79 };
80 let Ok(message) = dmessage.transcode_to::<traces::Syscalls>() else {
81 return None;
82 };
83 message.encode_to_vec()
84 };
85 traces::Syscalls::decode(bytes.as_ref()).ok().and_then(Self::new)
86 }
87
88 #[cfg(feature = "std")]
89 pub fn new_with_file<P: AsRef<std::path::Path>>(path: P) -> Option<Self> {
90 Self::new_with_bytes(std::fs::read(path).expect("read trace file"))
91 }
92
93 pub fn args(&self) -> &[Vec<u8>] {
94 &self.args
95 }
96
97 fn syscall(&self) -> Option<traces::syscall::Value> {
98 let mut syscalls = self.syscalls.lock();
99 syscalls.pop_front().and_then(|s| s.value)
100 }
101
102 fn io_syscall(&self, buf: &mut [u8], offset: usize, expected_length: Option<usize>) -> IoResult {
103 match self.syscall() {
104 Some(traces::syscall::Value::ReturnWithCode(code)) => {
105 let Ok(e): Result<Error, _> = (code as u64).try_into() else {
106 return UNEXPECTED_RESULT;
107 };
108 e.into()
109 }
110 Some(traces::syscall::Value::IoData(io_data)) => {
111 if let Some(length) = expected_length {
112 if offset > length {
113 return UNEXPECTED_RESULT;
114 }
115 if io_data.available_data.len() != length - offset {
116 return UNEXPECTED_RESULT;
117 }
118 }
119 let result = if buf.len() > io_data.available_data.len() {
120 if io_data.additional_length > 0 {
121 return UNEXPECTED_RESULT;
122 }
123 IoResult::FullyLoaded(io_data.available_data.len())
124 } else if (buf.len() < io_data.available_data.len()) || (io_data.additional_length > 0) {
125 IoResult::PartialLoaded {
126 loaded: buf.len(),
127 available: io_data.available_data.len() + io_data.additional_length as usize,
128 }
129 } else {
130 IoResult::FullyLoaded(buf.len())
133 };
134 if let Some(read) = result.loaded() {
135 if read > 0 {
136 buf[0..read].copy_from_slice(&io_data.available_data[0..read]);
137 }
138 }
139 result
140 }
141 _ => UNEXPECTED_RESULT,
142 }
143 }
144}
145
146impl SyscallImpls for ProtobufImpls {
147 fn debug(&self, s: &CStr) {
148 (self.debug_printer)(s.to_str().unwrap_or("utf8 error"));
149 }
150
151 fn exit(&self, code: i8) -> ! {
152 exit_with_panic(code);
153 }
154
155 fn load_cell(&self, buf: &mut [u8], offset: usize, _index: usize, _source: Source) -> IoResult {
156 self.io_syscall(buf, offset, None)
157 }
158
159 fn load_cell_by_field(
160 &self,
161 buf: &mut [u8],
162 offset: usize,
163 _index: usize,
164 _source: Source,
165 field: CellField,
166 ) -> IoResult {
167 let expected_length = match field {
168 CellField::Capacity => Some(8),
169 CellField::DataHash => Some(32),
170 CellField::LockHash => Some(32),
171 CellField::TypeHash => Some(32),
172 CellField::OccupiedCapacity => Some(8),
173 _ => None,
174 };
175 self.io_syscall(buf, offset, expected_length)
176 }
177
178 fn load_cell_code(
179 &self,
180 _buf_ptr: *mut u8,
181 _len: usize,
182 _content_offset: usize,
183 _content_size: usize,
184 _index: usize,
185 _source: Source,
186 ) -> Result<(), Error> {
187 panic!("Load cell data as code is not suported!");
188 }
189
190 fn load_cell_data(&self, buf: &mut [u8], offset: usize, _index: usize, _source: Source) -> IoResult {
191 self.io_syscall(buf, offset, None)
192 }
193
194 fn load_header(&self, buf: &mut [u8], offset: usize, _index: usize, _source: Source) -> IoResult {
195 self.io_syscall(buf, offset, None)
196 }
197
198 fn load_header_by_field(
199 &self,
200 buf: &mut [u8],
201 offset: usize,
202 _index: usize,
203 _source: Source,
204 _field: HeaderField,
205 ) -> IoResult {
206 self.io_syscall(buf, offset, Some(8))
207 }
208
209 fn load_input(&self, buf: &mut [u8], offset: usize, _index: usize, _source: Source) -> IoResult {
210 self.io_syscall(buf, offset, None)
211 }
212
213 fn load_input_by_field(
214 &self,
215 buf: &mut [u8],
216 offset: usize,
217 _index: usize,
218 _source: Source,
219 field: InputField,
220 ) -> IoResult {
221 let expected_length = match field {
222 InputField::Since => Some(8),
223 _ => None,
224 };
225 self.io_syscall(buf, offset, expected_length)
226 }
227
228 fn load_script(&self, buf: &mut [u8], offset: usize) -> IoResult {
229 self.io_syscall(buf, offset, None)
230 }
231
232 fn load_script_hash(&self, buf: &mut [u8], offset: usize) -> IoResult {
233 self.io_syscall(buf, offset, Some(32))
234 }
235
236 fn load_transaction(&self, buf: &mut [u8], offset: usize) -> IoResult {
237 self.io_syscall(buf, offset, None)
238 }
239
240 fn load_tx_hash(&self, buf: &mut [u8], offset: usize) -> IoResult {
241 self.io_syscall(buf, offset, Some(32))
242 }
243
244 fn load_witness(&self, buf: &mut [u8], offset: usize, _index: usize, _source: Source) -> IoResult {
245 self.io_syscall(buf, offset, None)
246 }
247
248 fn vm_version(&self) -> u64 {
249 match self.syscall() {
250 Some(traces::syscall::Value::ReturnWithCode(code)) => code as u64,
251 _ => UNEXPECTED,
252 }
253 }
254
255 fn current_cycles(&self) -> u64 {
256 match self.syscall() {
257 Some(traces::syscall::Value::ReturnWithCode(code)) => code as u64,
258 _ => UNEXPECTED,
259 }
260 }
261
262 fn exec(
263 &self,
264 _index: usize,
265 _source: Source,
266 _place: Place,
267 _bounds: Bounds,
268 _argv: &[&CStr],
269 ) -> Result<(), Error> {
270 match self.syscall() {
271 Some(traces::syscall::Value::ReturnWithCode(code)) => {
272 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
273 }
274 Some(traces::syscall::Value::Terminated(_)) => {
275 self.exit(0);
276 }
277 _ => Err(UNEXPECTED_ERROR),
278 }
279 }
280
281 fn spawn(
282 &self,
283 _index: usize,
284 _source: Source,
285 _place: Place,
286 _bounds: Bounds,
287 _argv: &[&CStr],
288 _inherited_fds: &[u64],
289 ) -> Result<u64, Error> {
290 match self.syscall() {
291 Some(traces::syscall::Value::ReturnWithCode(code)) => {
292 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
293 }
294 Some(traces::syscall::Value::SuccessOutputData(output)) => Ok(output),
295 _ => Err(UNEXPECTED_ERROR),
296 }
297 }
298
299 fn pipe(&self) -> Result<(u64, u64), Error> {
300 match self.syscall() {
301 Some(traces::syscall::Value::ReturnWithCode(code)) => {
302 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
303 }
304 Some(traces::syscall::Value::Fds(fds)) => {
305 if fds.fds.len() != 2 {
306 return Err(UNEXPECTED_ERROR);
307 }
308 Ok((fds.fds[0], fds.fds[1]))
309 }
310 _ => Err(UNEXPECTED_ERROR),
311 }
312 }
313
314 fn inherited_fds(&self, out_fds: &mut [u64]) -> Result<usize, Error> {
315 match self.syscall() {
316 Some(traces::syscall::Value::ReturnWithCode(code)) => {
317 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
318 }
319 Some(traces::syscall::Value::Fds(fds)) => {
320 let count = core::cmp::min(out_fds.len(), fds.fds.len());
321 out_fds[..count].copy_from_slice(&fds.fds[..count]);
322 Ok(fds.fds.len())
323 }
324 _ => Err(UNEXPECTED_ERROR),
325 }
326 }
327
328 fn read(&self, _fd: u64, buffer: &mut [u8]) -> Result<usize, Error> {
329 match self.syscall() {
330 Some(traces::syscall::Value::ReturnWithCode(code)) => {
331 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
332 }
333 Some(traces::syscall::Value::IoData(io_data)) => {
334 let read = core::cmp::min(io_data.available_data.len(), buffer.len());
335 buffer[0..read].copy_from_slice(&io_data.available_data[0..read]);
336 Ok(read)
337 }
338 _ => Err(UNEXPECTED_ERROR),
339 }
340 }
341
342 fn write(&self, _fd: u64, _buffer: &[u8]) -> Result<usize, Error> {
343 match self.syscall() {
344 Some(traces::syscall::Value::ReturnWithCode(code)) => {
345 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
346 }
347 Some(traces::syscall::Value::SuccessOutputData(output)) => Ok(output as usize),
348 _ => Err(UNEXPECTED_ERROR),
349 }
350 }
351
352 fn close(&self, _fd: u64) -> Result<(), Error> {
353 match self.syscall() {
354 Some(traces::syscall::Value::ReturnWithCode(code)) => {
355 if code == 0 {
356 Ok(())
357 } else {
358 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
359 }
360 }
361 _ => Err(UNEXPECTED_ERROR),
362 }
363 }
364
365 fn wait(&self, _pid: u64) -> Result<i8, Error> {
366 match self.syscall() {
367 Some(traces::syscall::Value::ReturnWithCode(code)) => {
368 Err((code as u64).try_into().unwrap_or(UNEXPECTED_ERROR))
369 }
370 Some(traces::syscall::Value::SuccessOutputData(output)) => Ok(output as i8),
371 _ => Err(UNEXPECTED_ERROR),
372 }
373 }
374
375 fn process_id(&self) -> u64 {
376 match self.syscall() {
377 Some(traces::syscall::Value::ReturnWithCode(code)) => code as u64,
378 _ => UNEXPECTED,
379 }
380 }
381
382 fn load_block_extension(&self, buf: &mut [u8], offset: usize, _index: usize, _source: Source) -> IoResult {
383 self.io_syscall(buf, offset, None)
384 }
385}
386
387pub struct ProtobufVmRunnerImpls<M> {
388 inner: ProtobufImpls,
389 _marker: PhantomData<M>,
390}
391
392impl<M> ProtobufVmRunnerImpls<M> {
393 pub fn new_with_bytes<B: AsRef<[u8]>>(bytes: B) -> Option<Self> {
394 ProtobufImpls::new_with_bytes(bytes).map(|inner| Self { inner, _marker: PhantomData })
395 }
396
397 #[cfg(feature = "std")]
398 pub fn new_with_file<P: AsRef<std::path::Path>>(path: P) -> Option<Self> {
399 ProtobufImpls::new_with_file(path).map(|inner| Self { inner, _marker: PhantomData })
400 }
401
402 pub fn args(&self) -> &[Vec<u8>] {
403 self.inner.args()
404 }
405
406 pub fn set_debug_printer<F>(&mut self, f: F)
407 where
408 F: Fn(&str) + Send + Sync + 'static,
409 {
410 self.inner.debug_printer = Box::new(f);
411 }
412}
413
414impl<M> SyscallImpls for ProtobufVmRunnerImpls<M> {
415 fn debug(&self, s: &CStr) {
416 self.inner.debug(s);
417 }
418
419 fn exit(&self, code: i8) -> ! {
420 self.inner.exit(code)
421 }
422
423 fn load_cell(&self, buf: &mut [u8], offset: usize, index: usize, source: Source) -> IoResult {
424 self.inner.load_cell(buf, offset, index, source)
425 }
426
427 fn load_cell_by_field(
428 &self,
429 buf: &mut [u8],
430 offset: usize,
431 index: usize,
432 source: Source,
433 field: CellField,
434 ) -> IoResult {
435 self.inner.load_cell_by_field(buf, offset, index, source, field)
436 }
437
438 fn load_cell_code(
439 &self,
440 buf_ptr: *mut u8,
441 len: usize,
442 content_offset: usize,
443 content_size: usize,
444 index: usize,
445 source: Source,
446 ) -> Result<(), Error> {
447 self.inner.load_cell_code(buf_ptr, len, content_offset, content_size, index, source)
448 }
449
450 fn load_cell_data(&self, buf: &mut [u8], offset: usize, index: usize, source: Source) -> IoResult {
451 self.inner.load_cell_data(buf, offset, index, source)
452 }
453
454 fn load_header(&self, buf: &mut [u8], offset: usize, index: usize, source: Source) -> IoResult {
455 self.inner.load_header(buf, offset, index, source)
456 }
457
458 fn load_header_by_field(
459 &self,
460 buf: &mut [u8],
461 offset: usize,
462 index: usize,
463 source: Source,
464 field: HeaderField,
465 ) -> IoResult {
466 self.inner.load_header_by_field(buf, offset, index, source, field)
467 }
468
469 fn load_input(&self, buf: &mut [u8], offset: usize, index: usize, source: Source) -> IoResult {
470 self.inner.load_input(buf, offset, index, source)
471 }
472
473 fn load_input_by_field(
474 &self,
475 buf: &mut [u8],
476 offset: usize,
477 index: usize,
478 source: Source,
479 field: InputField,
480 ) -> IoResult {
481 self.inner.load_input_by_field(buf, offset, index, source, field)
482 }
483
484 fn load_script(&self, buf: &mut [u8], offset: usize) -> IoResult {
485 self.inner.load_script(buf, offset)
486 }
487
488 fn load_script_hash(&self, buf: &mut [u8], offset: usize) -> IoResult {
489 self.inner.load_script_hash(buf, offset)
490 }
491
492 fn load_transaction(&self, buf: &mut [u8], offset: usize) -> IoResult {
493 self.inner.load_transaction(buf, offset)
494 }
495
496 fn load_tx_hash(&self, buf: &mut [u8], offset: usize) -> IoResult {
497 self.inner.load_tx_hash(buf, offset)
498 }
499
500 fn load_witness(&self, buf: &mut [u8], offset: usize, index: usize, source: Source) -> IoResult {
501 self.inner.load_witness(buf, offset, index, source)
502 }
503
504 fn vm_version(&self) -> u64 {
505 self.inner.vm_version()
506 }
507
508 fn current_cycles(&self) -> u64 {
509 self.inner.current_cycles()
510 }
511
512 fn exec(&self, index: usize, source: Source, place: Place, bounds: Bounds, argv: &[&CStr]) -> Result<(), Error> {
513 self.inner.exec(index, source, place, bounds, argv)
514 }
515
516 fn spawn(
517 &self,
518 index: usize,
519 source: Source,
520 place: Place,
521 bounds: Bounds,
522 argv: &[&CStr],
523 inherited_fds: &[u64],
524 ) -> Result<u64, Error> {
525 self.inner.spawn(index, source, place, bounds, argv, inherited_fds)
526 }
527
528 fn pipe(&self) -> Result<(u64, u64), Error> {
529 self.inner.pipe()
530 }
531
532 fn inherited_fds(&self, out_fds: &mut [u64]) -> Result<usize, Error> {
533 self.inner.inherited_fds(out_fds)
534 }
535
536 fn read(&self, fd: u64, buffer: &mut [u8]) -> Result<usize, Error> {
537 self.inner.read(fd, buffer)
538 }
539
540 fn write(&self, fd: u64, buffer: &[u8]) -> Result<usize, Error> {
541 self.inner.write(fd, buffer)
542 }
543
544 fn close(&self, fd: u64) -> Result<(), Error> {
545 self.inner.close(fd)
546 }
547
548 fn wait(&self, pid: u64) -> Result<i8, Error> {
549 self.inner.wait(pid)
550 }
551
552 fn process_id(&self) -> u64 {
553 self.inner.process_id()
554 }
555
556 fn load_block_extension(&self, buf: &mut [u8], offset: usize, index: usize, source: Source) -> IoResult {
557 self.inner.load_block_extension(buf, offset, index, source)
558 }
559}
560
561impl<M: SupportMachine> CkbvmRunnerImpls<M> for ProtobufVmRunnerImpls<M> {
562 fn fetch_cell_code(
563 &self,
564 _content_offset: usize,
565 content_size: usize,
566 _index: usize,
567 _source: Source,
568 ) -> Result<Vec<u8>, Error> {
569 match self.inner.syscall() {
570 Some(traces::syscall::Value::ReturnWithCode(code)) => {
571 let Ok(e): Result<Error, _> = (code as u64).try_into() else {
572 return Err(UNEXPECTED_ERROR);
573 };
574 Err(e)
575 }
576 Some(traces::syscall::Value::IoData(io_data)) => {
577 if io_data.additional_length > 0 {
578 return Err(UNEXPECTED_ERROR);
579 }
580 if io_data.available_data.len() != content_size {
581 return Err(UNEXPECTED_ERROR);
582 }
583 Ok(io_data.available_data)
584 }
585 _ => Err(UNEXPECTED_ERROR),
586 }
587 }
588}