Remove python bindings (#2005)
* delete python bindings * fix * example * fix
This commit is contained in:
parent
24fd098e66
commit
4901846588
25
.github/workflows/build_and_test.yml
vendored
25
.github/workflows/build_and_test.yml
vendored
@ -229,31 +229,6 @@ jobs:
|
|||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
run: ./libafl_concolic/test/smoke_test.sh
|
run: ./libafl_concolic/test/smoke_test.sh
|
||||||
|
|
||||||
bindings:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
- name: Remove existing clang and LLVM
|
|
||||||
run: sudo apt purge llvm* clang*
|
|
||||||
- name: Install LLVM and Clang
|
|
||||||
uses: KyleMayes/install-llvm-action@v1
|
|
||||||
with:
|
|
||||||
directory: ${{ runner.temp }}/llvm
|
|
||||||
version: 17
|
|
||||||
- name: Install deps
|
|
||||||
run: sudo apt-get install -y ninja-build python3-dev python3-pip python3-venv libz3-dev
|
|
||||||
- name: Install maturin
|
|
||||||
run: python3 -m pip install maturin
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Run a maturin build
|
|
||||||
run: export LLVM_CONFIG=llvm-config-16 && cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && pip install --upgrade --force-reinstall . && ./test.sh
|
|
||||||
- name: Run python test
|
|
||||||
run: . ./bindings/pylibafl/.env/bin/activate && cd ./fuzzers/baby_fuzzer && python3 baby_fuzzer.py 2>&1 | grep "Bye"
|
|
||||||
|
|
||||||
fuzzers:
|
fuzzers:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
1
bindings/pylibafl/.gitignore
vendored
1
bindings/pylibafl/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
dist/
|
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "pylibafl"
|
|
||||||
version = "0.11.2"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
pyo3 = { version = "0.18.3", features = ["extension-module"] }
|
|
||||||
pyo3-log = "0.8.1"
|
|
||||||
libafl_sugar = { path = "../../libafl_sugar", version = "0.11.2", features = ["python"] }
|
|
||||||
libafl = { path = "../../libafl", version = "0.11.2", features = ["python"] }
|
|
||||||
libafl_bolts = { path = "../../libafl_bolts", version = "0.11.2", features = ["python"] }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
libafl_qemu = { path = "../../libafl_qemu", version = "0.11.2", features = ["python"] }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
pyo3-build-config = { version = "0.17" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "pylibafl"
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
@ -1,31 +0,0 @@
|
|||||||
# How to use python bindings
|
|
||||||
## First time setup
|
|
||||||
```bash
|
|
||||||
# Install maturin
|
|
||||||
pip install maturin
|
|
||||||
# Create virtual environment
|
|
||||||
python3 -m venv .env
|
|
||||||
```
|
|
||||||
## Build bindings
|
|
||||||
```
|
|
||||||
# Activate virtual environment
|
|
||||||
source .env/bin/activate
|
|
||||||
# Build python module
|
|
||||||
maturin develop
|
|
||||||
```
|
|
||||||
This is going to install `pylibafl` python module into this venv.
|
|
||||||
|
|
||||||
## Use bindings
|
|
||||||
### Example: Running baby_fuzzer in fuzzers/baby_fuzzer/baby_fuzzer.py
|
|
||||||
First, make sure the python virtual environment is activated. If not, run `source .env/bin/activate
|
|
||||||
`. Running `pip freeze` at this point should display the following (versions may differ):
|
|
||||||
```
|
|
||||||
maturin==0.12.6
|
|
||||||
pylibafl==0.7.0
|
|
||||||
toml==0.10.2
|
|
||||||
```
|
|
||||||
Then simply run
|
|
||||||
```
|
|
||||||
python PATH_TO_BABY_FUZZER/baby_fuzzer.py
|
|
||||||
```
|
|
||||||
The crashes directory will be created in the directory from which you ran the command.
|
|
@ -1,26 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["maturin[patchelf]>=0.14.10,<0.15"]
|
|
||||||
build-backend = "maturin"
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "PyLibAFL"
|
|
||||||
version = "0.10.1"
|
|
||||||
description = "Advanced Fuzzing Library for Python"
|
|
||||||
readme = "README.md"
|
|
||||||
requires-python = ">=3.8"
|
|
||||||
license = {text = "Apache-2.0"}
|
|
||||||
classifiers = [
|
|
||||||
"License :: OSI Approved :: Apache Software License",
|
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Programming Language :: Rust",
|
|
||||||
"Topic :: Security",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
repository = "https://github.com/AFLplusplus/LibAFL.git"
|
|
||||||
|
|
||||||
[tool.maturin]
|
|
||||||
bindings = "pylibafl"
|
|
||||||
manifest-path = "Cargo.toml"
|
|
||||||
python-source = "python"
|
|
||||||
all-features = true
|
|
@ -1,120 +0,0 @@
|
|||||||
use pyo3::{prelude::*, types::PyDict};
|
|
||||||
|
|
||||||
const LIBAFL_CODE: &str = r#"
|
|
||||||
class BaseObserver:
|
|
||||||
def flush(self):
|
|
||||||
pass
|
|
||||||
def pre_exec(self, state, input):
|
|
||||||
pass
|
|
||||||
def post_exec(self, state, input, exit_kind):
|
|
||||||
pass
|
|
||||||
def pre_exec_child(self, state, input):
|
|
||||||
pass
|
|
||||||
def post_exec_child(self, state, input, exit_kind):
|
|
||||||
pass
|
|
||||||
def name(self):
|
|
||||||
return type(self).__name__
|
|
||||||
def as_observer(self):
|
|
||||||
return Observer.new_py(self)
|
|
||||||
|
|
||||||
class BaseFeedback:
|
|
||||||
def init_state(self, state):
|
|
||||||
pass
|
|
||||||
def is_interesting(self, state, mgr, input, observers, exit_kind) -> bool:
|
|
||||||
return False
|
|
||||||
def append_metadata(self, state, observers, testcase):
|
|
||||||
pass
|
|
||||||
def discard_metadata(self, state, input):
|
|
||||||
pass
|
|
||||||
def name(self):
|
|
||||||
return type(self).__name__
|
|
||||||
def as_feedback(self):
|
|
||||||
return Feedback.new_py(self)
|
|
||||||
|
|
||||||
class BaseExecutor:
|
|
||||||
def observers(self) -> ObserversTuple:
|
|
||||||
raise NotImplementedError('Implement this yourself')
|
|
||||||
def run_target(self, fuzzer, state, mgr, input) -> ExitKind:
|
|
||||||
raise NotImplementedError('Implement this yourself')
|
|
||||||
def as_executor(self):
|
|
||||||
return Executor.new_py(self)
|
|
||||||
|
|
||||||
class BaseStage:
|
|
||||||
def perform(self, fuzzer, executor, state, manager, corpus_idx):
|
|
||||||
pass
|
|
||||||
def as_stage(self):
|
|
||||||
return Stage.new_py(self)
|
|
||||||
|
|
||||||
class BaseMutator:
|
|
||||||
def mutate(self, state, input):
|
|
||||||
pass
|
|
||||||
def post_exec(self, state, corpus_idx):
|
|
||||||
pass
|
|
||||||
def as_mutator(self):
|
|
||||||
return Mutator.new_py(self)
|
|
||||||
|
|
||||||
class FnStage(BaseStage):
|
|
||||||
def __init__(self, fn):
|
|
||||||
self.fn = fn
|
|
||||||
def __call__(self, fuzzer, executor, state, manager, corpus_idx):
|
|
||||||
self.fn(fuzzer, executor, state, manager, corpus_idx)
|
|
||||||
def perform(self, fuzzer, executor, state, manager, corpus_idx):
|
|
||||||
self.fn(fuzzer, executor, state, manager, corpus_idx)
|
|
||||||
|
|
||||||
def feedback_not(a):
|
|
||||||
return NotFeedback(a).as_feedback()
|
|
||||||
|
|
||||||
def feedback_and(a, b):
|
|
||||||
return EagerAndFeedback(a, b).as_feedback()
|
|
||||||
|
|
||||||
def feedback_and_fast(a, b):
|
|
||||||
return FastAndFeedback(a, b).as_feedback()
|
|
||||||
|
|
||||||
def feedback_or(a, b):
|
|
||||||
return EagerOrFeedback(a, b).as_feedback()
|
|
||||||
|
|
||||||
def feedback_or_fast(a, b):
|
|
||||||
return FastOrFeedback(a, b).as_feedback()
|
|
||||||
"#;
|
|
||||||
|
|
||||||
#[pymodule]
|
|
||||||
#[pyo3(name = "pylibafl")]
|
|
||||||
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
pyo3_log::init();
|
|
||||||
|
|
||||||
let modules = py.import("sys")?.getattr("modules")?;
|
|
||||||
|
|
||||||
let sugar_module = PyModule::new(py, "sugar")?;
|
|
||||||
libafl_sugar::python_module(py, sugar_module)?;
|
|
||||||
m.add_submodule(sugar_module)?;
|
|
||||||
modules.set_item("pylibafl.sugar", sugar_module)?;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
let qemu_module = PyModule::new(py, "qemu")?;
|
|
||||||
libafl_qemu::python_module(py, qemu_module)?;
|
|
||||||
m.add_submodule(qemu_module)?;
|
|
||||||
modules.set_item("pylibafl.qemu", qemu_module)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bolts_module = PyModule::new(py, "libafl_bolts")?;
|
|
||||||
libafl_bolts::pybind::python_module(py, bolts_module)?;
|
|
||||||
m.add_submodule(bolts_module)?;
|
|
||||||
modules.set_item("pylibafl.libafl_bolts", bolts_module)?;
|
|
||||||
|
|
||||||
let libafl_module = PyModule::new(py, "libafl")?;
|
|
||||||
libafl::pybind::python_module(py, libafl_module)?;
|
|
||||||
|
|
||||||
libafl_module.add("__builtins__", py.import("builtins")?)?;
|
|
||||||
|
|
||||||
let locals = PyDict::new(py);
|
|
||||||
py.run(LIBAFL_CODE, Some(libafl_module.dict()), Some(locals))?;
|
|
||||||
for (key, val) in locals.iter() {
|
|
||||||
libafl_module.add(key.extract::<&str>()?, val)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
m.add_submodule(libafl_module)?;
|
|
||||||
modules.set_item("pylibafl.libafl", libafl_module)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
from pylibafl.libafl import *
|
|
||||||
import ctypes
|
|
||||||
import platform
|
|
||||||
|
|
||||||
MAP_SIZE = 4096
|
|
||||||
|
|
||||||
|
|
||||||
class FooObserver(BaseObserver):
|
|
||||||
def __init__(self):
|
|
||||||
self.n = 0
|
|
||||||
|
|
||||||
def name(self):
|
|
||||||
return "Foo"
|
|
||||||
|
|
||||||
def pre_exec(self, state, input):
|
|
||||||
if self.n % 10000 == 0:
|
|
||||||
print("FOO!", self.n, input)
|
|
||||||
self.n += 1
|
|
||||||
|
|
||||||
|
|
||||||
class FooFeedback(BaseFeedback):
|
|
||||||
def is_interesting(self, state, mgr, input, observers, exit_kind):
|
|
||||||
ob = observers.match_name("Foo").unwrap_py()
|
|
||||||
return ob.n % 10000 == 0
|
|
||||||
|
|
||||||
|
|
||||||
class FooExecutor(BaseExecutor):
|
|
||||||
def __init__(self, harness, observers: ObserversTuple):
|
|
||||||
self.h = harness
|
|
||||||
self.o = observers
|
|
||||||
|
|
||||||
def observers(self):
|
|
||||||
return self.o
|
|
||||||
|
|
||||||
def run_target(self, fuzzer, state, mgr, input) -> ExitKind:
|
|
||||||
return (self.h)(input)
|
|
||||||
|
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
libc = ctypes.cdll.LoadLibrary("libc.dylib")
|
|
||||||
else:
|
|
||||||
libc = ctypes.cdll.LoadLibrary("libc.so.6")
|
|
||||||
|
|
||||||
# Get a buffer to use for our map observer
|
|
||||||
libc.calloc.restype = ctypes.c_void_p
|
|
||||||
area_ptr = libc.calloc(1, MAP_SIZE)
|
|
||||||
|
|
||||||
observer = StdMapObserverI8("mymap", area_ptr, MAP_SIZE)
|
|
||||||
|
|
||||||
m = observer.as_map_observer()
|
|
||||||
|
|
||||||
observers = ObserversTuple(
|
|
||||||
[observer.as_map_observer().as_observer(), FooObserver().as_observer()]
|
|
||||||
)
|
|
||||||
|
|
||||||
feedback = feedback_or(MaxMapFeedbackI8(m).as_feedback(), FooFeedback().as_feedback())
|
|
||||||
|
|
||||||
objective = feedback_and_fast(
|
|
||||||
CrashFeedback().as_feedback(), MaxMapFeedbackI8(m).as_feedback()
|
|
||||||
)
|
|
||||||
|
|
||||||
fuzzer = StdFuzzer(feedback, objective)
|
|
||||||
|
|
||||||
rand = StdRand.with_current_nanos()
|
|
||||||
|
|
||||||
state = StdState(
|
|
||||||
rand.as_rand(),
|
|
||||||
InMemoryCorpus().as_corpus(),
|
|
||||||
InMemoryCorpus().as_corpus(),
|
|
||||||
feedback,
|
|
||||||
objective,
|
|
||||||
)
|
|
||||||
|
|
||||||
monitor = SimpleMonitor(lambda s: print(s))
|
|
||||||
|
|
||||||
mgr = SimpleEventManager(monitor.as_monitor())
|
|
||||||
|
|
||||||
|
|
||||||
def harness(buf) -> ExitKind:
|
|
||||||
"""
|
|
||||||
The harness fn that the fuzzer will execute in a loop
|
|
||||||
"""
|
|
||||||
# print(buf)
|
|
||||||
|
|
||||||
# set the observer map byte from python
|
|
||||||
m[0] = 1
|
|
||||||
if len(buf) > 0 and buf[0] == ord("a"):
|
|
||||||
m[1] = 1
|
|
||||||
if len(buf) > 1 and buf[1] == ord("b"):
|
|
||||||
m[2] = 1
|
|
||||||
if len(buf) > 2 and buf[2] == ord("c"):
|
|
||||||
m[3] = 1
|
|
||||||
return ExitKind.crash()
|
|
||||||
return ExitKind.ok()
|
|
||||||
|
|
||||||
|
|
||||||
# executor = InProcessExecutor(harness, observers, fuzzer, state, mgr.as_manager())
|
|
||||||
|
|
||||||
executor = FooExecutor(harness, observers)
|
|
||||||
|
|
||||||
stage = StdMutationalStage(StdHavocMutator().as_mutator())
|
|
||||||
|
|
||||||
stage_tuple_list = StagesTuple([stage.as_stage()])
|
|
||||||
|
|
||||||
fuzzer.add_input(state, executor.as_executor(), mgr.as_manager(), b"\0\0")
|
|
||||||
|
|
||||||
print("Starting to fuzz from python!")
|
|
||||||
|
|
||||||
fuzzer.fuzz_loop(executor.as_executor(), state, mgr.as_manager(), stage_tuple_list)
|
|
@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
timeout 10 python3 ./test.py
|
|
||||||
export exit_code=$?
|
|
||||||
if [ $exit_code -eq 124 ]; then
|
|
||||||
# 124 = timeout happened. All good.
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit $exit_code
|
|
||||||
fi
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|
||||||
// printf("Got %ld bytes.\n", Size);
|
|
||||||
if (Size >= 4 && *(uint32_t *)Data == 0xaabbccdd) { abort(); }
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
char buf[10] = {0};
|
|
||||||
LLVMFuzzerTestOneInput(buf, 10);
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
# from the maturin venv, after running 'maturin develop' in the pylibafl directory
|
|
||||||
|
|
||||||
from pylibafl import sugar, qemu
|
|
||||||
import lief
|
|
||||||
|
|
||||||
MAX_SIZE = 0x100
|
|
||||||
BINARY_PATH = './a.out'
|
|
||||||
|
|
||||||
emu = qemu.Emulator(['qemu-x86_64', BINARY_PATH], [])
|
|
||||||
|
|
||||||
elf = lief.parse(BINARY_PATH)
|
|
||||||
test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput")
|
|
||||||
if elf.is_pie:
|
|
||||||
test_one_input += emu.load_addr()
|
|
||||||
print('LLVMFuzzerTestOneInput @ 0x%x' % test_one_input)
|
|
||||||
|
|
||||||
emu.set_breakpoint(test_one_input)
|
|
||||||
emu.run()
|
|
||||||
|
|
||||||
sp = emu.read_reg(qemu.regs.Rsp)
|
|
||||||
print('SP = 0x%x' % sp)
|
|
||||||
|
|
||||||
retaddr = int.from_bytes(emu.read_mem(sp, 8), 'little')
|
|
||||||
print('RET = 0x%x' % retaddr)
|
|
||||||
|
|
||||||
inp = emu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite)
|
|
||||||
assert(inp > 0)
|
|
||||||
|
|
||||||
emu.remove_breakpoint(test_one_input)
|
|
||||||
emu.set_breakpoint(retaddr)
|
|
||||||
|
|
||||||
def harness(b):
|
|
||||||
if len(b) > MAX_SIZE:
|
|
||||||
b = b[:MAX_SIZE]
|
|
||||||
emu.write_mem(inp, b)
|
|
||||||
emu.write_reg(qemu.regs.Rsi, len(b))
|
|
||||||
emu.write_reg(qemu.regs.Rdi, inp)
|
|
||||||
emu.write_reg(qemu.regs.Rsp, sp)
|
|
||||||
emu.write_reg(qemu.regs.Rip, test_one_input)
|
|
||||||
emu.run()
|
|
||||||
|
|
||||||
fuzz = sugar.QemuBytesCoverageSugar(['./in'], './out', 3456, [0,1,2,3])
|
|
||||||
fuzz.run(emu, harness)
|
|
@ -32,9 +32,6 @@ introspection = []
|
|||||||
## Collects stats about scalability
|
## Collects stats about scalability
|
||||||
scalability_introspection = []
|
scalability_introspection = []
|
||||||
|
|
||||||
## Will build the `pyo3` bindings
|
|
||||||
python = ["pyo3", "concat-idents", "libafl_bolts/python"]
|
|
||||||
|
|
||||||
## Expose `libafl::prelude` for access without additional using directives
|
## Expose `libafl::prelude` for access without additional using directives
|
||||||
prelude = ["libafl_bolts/prelude"]
|
prelude = ["libafl_bolts/prelude"]
|
||||||
|
|
||||||
@ -181,7 +178,6 @@ wait-timeout = { version = "0.2", optional = true } # used by CommandExecutor to
|
|||||||
|
|
||||||
z3 = { version = "0.12.0", features = ["static-link-z3"], optional = true } # for concolic mutation
|
z3 = { version = "0.12.0", features = ["static-link-z3"], optional = true } # for concolic mutation
|
||||||
|
|
||||||
pyo3 = { version = "0.18", optional = true, features = ["serde", "macros"] }
|
|
||||||
concat-idents = { version = "1.1.3", optional = true }
|
concat-idents = { version = "1.1.3", optional = true }
|
||||||
|
|
||||||
libcasr = { version = "2.7", optional = true }
|
libcasr = { version = "2.7", optional = true }
|
||||||
|
@ -248,47 +248,3 @@ where
|
|||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ``CachedOnDiskCorpus`` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::string::String;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::{pybind::PythonCorpus, CachedOnDiskCorpus},
|
|
||||||
inputs::BytesInput,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "CachedOnDiskCorpus")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for CachedOnDiskCorpus
|
|
||||||
pub struct PythonCachedOnDiskCorpus {
|
|
||||||
/// Rust wrapped CachedOnDiskCorpus object
|
|
||||||
pub inner: CachedOnDiskCorpus<BytesInput>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonCachedOnDiskCorpus {
|
|
||||||
#[new]
|
|
||||||
fn new(path: String, cache_max_len: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: CachedOnDiskCorpus::new(PathBuf::from(path), cache_max_len).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_corpus(slf: Py<Self>) -> PythonCorpus {
|
|
||||||
PythonCorpus::new_cached_on_disk(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonCachedOnDiskCorpus>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -426,44 +426,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `InMemoryCorpus` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::{pybind::PythonCorpus, InMemoryCorpus},
|
|
||||||
inputs::BytesInput,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "InMemoryCorpus")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for InMemoryCorpus
|
|
||||||
pub struct PythonInMemoryCorpus {
|
|
||||||
/// Rust wrapped InMemoryCorpus object
|
|
||||||
pub inner: InMemoryCorpus<BytesInput>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonInMemoryCorpus {
|
|
||||||
#[new]
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: InMemoryCorpus::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_corpus(slf: Py<Self>) -> PythonCorpus {
|
|
||||||
PythonCorpus::new_in_memory(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonInMemoryCorpus>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -437,47 +437,3 @@ where
|
|||||||
&self.dir_path
|
&self.dir_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
/// `InMemoryOnDiskCorpus` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::string::String;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::{pybind::PythonCorpus, InMemoryOnDiskCorpus},
|
|
||||||
inputs::BytesInput,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "InMemoryOnDiskCorpus")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for InMemoryOnDiskCorpus
|
|
||||||
pub struct PythonInMemoryOnDiskCorpus {
|
|
||||||
/// Rust wrapped InMemoryOnDiskCorpus object
|
|
||||||
pub inner: InMemoryOnDiskCorpus<BytesInput>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonInMemoryOnDiskCorpus {
|
|
||||||
#[new]
|
|
||||||
fn new(path: String) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: InMemoryOnDiskCorpus::new(PathBuf::from(path)).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_corpus(slf: Py<Self>) -> PythonCorpus {
|
|
||||||
PythonCorpus::new_in_memory_on_disk(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonInMemoryOnDiskCorpus>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -200,243 +200,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Corpus` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::{
|
|
||||||
cached::pybind::PythonCachedOnDiskCorpus, inmemory::pybind::PythonInMemoryCorpus,
|
|
||||||
inmemory_ondisk::pybind::PythonInMemoryOnDiskCorpus,
|
|
||||||
ondisk::pybind::PythonOnDiskCorpus, testcase::pybind::PythonTestcaseWrapper, Corpus,
|
|
||||||
CorpusId, HasTestcase, Testcase,
|
|
||||||
},
|
|
||||||
inputs::{BytesInput, UsesInput},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
enum PythonCorpusWrapper {
|
|
||||||
InMemory(Py<PythonInMemoryCorpus>),
|
|
||||||
CachedOnDisk(Py<PythonCachedOnDiskCorpus>),
|
|
||||||
OnDisk(Py<PythonOnDiskCorpus>),
|
|
||||||
InMemoryOnDisk(Py<PythonInMemoryOnDiskCorpus>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Corpus Trait binding
|
|
||||||
#[pyclass(unsendable, name = "Corpus")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
pub struct PythonCorpus {
|
|
||||||
wrapper: PythonCorpusWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_body!(
|
|
||||||
$wrapper,
|
|
||||||
$name,
|
|
||||||
$body,
|
|
||||||
PythonCorpusWrapper,
|
|
||||||
{
|
|
||||||
InMemory,
|
|
||||||
InMemoryOnDisk,
|
|
||||||
CachedOnDisk,
|
|
||||||
OnDisk
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!(
|
|
||||||
$wrapper,
|
|
||||||
$name,
|
|
||||||
$body,
|
|
||||||
PythonCorpusWrapper,
|
|
||||||
{
|
|
||||||
InMemory,
|
|
||||||
InMemoryOnDisk,
|
|
||||||
CachedOnDisk,
|
|
||||||
OnDisk
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonCorpus {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_in_memory(py_in_memory_corpus: Py<PythonInMemoryCorpus>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonCorpusWrapper::InMemory(py_in_memory_corpus),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_cached_on_disk(py_cached_on_disk_corpus: Py<PythonCachedOnDiskCorpus>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_on_disk(py_on_disk_corpus: Py<PythonOnDiskCorpus>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonCorpusWrapper::OnDisk(py_on_disk_corpus),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_in_memory_on_disk(
|
|
||||||
py_in_memory_on_disk_corpus: Py<PythonInMemoryOnDiskCorpus>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonCorpusWrapper::InMemoryOnDisk(py_in_memory_on_disk_corpus),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "count")]
|
|
||||||
fn pycount(&self) -> usize {
|
|
||||||
self.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "current")]
|
|
||||||
fn pycurrent(&self) -> Option<usize> {
|
|
||||||
self.current().map(|x| x.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "get")]
|
|
||||||
fn pyget(&self, idx: usize) -> PythonTestcaseWrapper {
|
|
||||||
let t: &mut Testcase<BytesInput> = unwrap_me!(self.wrapper, c, {
|
|
||||||
c.get(CorpusId::from(idx))
|
|
||||||
.map(|v| unsafe { v.as_ptr().as_mut().unwrap() })
|
|
||||||
.expect("PythonCorpus::get failed")
|
|
||||||
});
|
|
||||||
PythonTestcaseWrapper::wrap(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesInput for PythonCorpus {
|
|
||||||
type Input = BytesInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Corpus for PythonCorpus {
|
|
||||||
#[inline]
|
|
||||||
fn count(&self) -> usize {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.count() })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add(&mut self, testcase: Testcase<BytesInput>) -> Result<CorpusId, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, c, { c.add(testcase) })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn replace(
|
|
||||||
&mut self,
|
|
||||||
idx: CorpusId,
|
|
||||||
testcase: Testcase<BytesInput>,
|
|
||||||
) -> Result<Testcase<BytesInput>, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, c, { c.replace(idx, testcase) })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn remove(&mut self, idx: CorpusId) -> Result<Testcase<BytesInput>, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, c, { c.remove(idx) })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get(&self, idx: CorpusId) -> Result<&RefCell<Testcase<BytesInput>>, Error> {
|
|
||||||
let ptr = unwrap_me!(self.wrapper, c, {
|
|
||||||
c.get(idx)
|
|
||||||
.map(core::ptr::from_ref::<RefCell<Testcase<BytesInput>>>)
|
|
||||||
})?;
|
|
||||||
Ok(unsafe { ptr.as_ref().unwrap() })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn current(&self) -> &Option<CorpusId> {
|
|
||||||
let ptr = unwrap_me!(self.wrapper, c, { core::ptr::from_ref(c.current()) });
|
|
||||||
unsafe { ptr.as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn current_mut(&mut self) -> &mut Option<CorpusId> {
|
|
||||||
let ptr = unwrap_me_mut!(self.wrapper, c, { core::ptr::from_mut(c.current_mut()) });
|
|
||||||
unsafe { ptr.as_mut().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&self, idx: CorpusId) -> Option<CorpusId> {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.next(idx) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prev(&self, idx: CorpusId) -> Option<CorpusId> {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.prev(idx) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn first(&self) -> Option<CorpusId> {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.first() })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last(&self) -> Option<CorpusId> {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.last() })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_input_into(&self, testcase: &mut Testcase<BytesInput>) -> Result<(), Error> {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.load_input_into(testcase) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_input_from(&self, testcase: &Testcase<BytesInput>) -> Result<(), Error> {
|
|
||||||
unwrap_me!(self.wrapper, c, { c.store_input_from(testcase) })
|
|
||||||
}
|
|
||||||
|
|
||||||
/*fn ids<'a>(&'a self) -> CorpusIdIterator<'a, Self> {
|
|
||||||
CorpusIdIterator {
|
|
||||||
corpus: self,
|
|
||||||
cur: self.first(),
|
|
||||||
cur_back: self.last(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_id(&self, next_random: u64) -> CorpusId {
|
|
||||||
let nth = (next_random as usize) % self.count();
|
|
||||||
self.ids()
|
|
||||||
.nth(nth)
|
|
||||||
.expect("Failed to get a random CorpusId")
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasTestcase for PythonCorpus {
|
|
||||||
fn testcase(
|
|
||||||
&self,
|
|
||||||
id: CorpusId,
|
|
||||||
) -> Result<core::cell::Ref<Testcase<<Self as UsesInput>::Input>>, Error> {
|
|
||||||
Ok(self.get(id)?.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn testcase_mut(
|
|
||||||
&self,
|
|
||||||
id: CorpusId,
|
|
||||||
) -> Result<core::cell::RefMut<Testcase<<Self as UsesInput>::Input>>, Error> {
|
|
||||||
Ok(self.get(id)?.borrow_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonCorpus>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -260,47 +260,3 @@ where
|
|||||||
&self.dir_path
|
&self.dir_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
/// `OnDiskCorpus` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::string::String;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::{pybind::PythonCorpus, OnDiskCorpus},
|
|
||||||
inputs::BytesInput,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "OnDiskCorpus")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for OnDiskCorpus
|
|
||||||
pub struct PythonOnDiskCorpus {
|
|
||||||
/// Rust wrapped OnDiskCorpus object
|
|
||||||
pub inner: OnDiskCorpus<BytesInput>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonOnDiskCorpus {
|
|
||||||
#[new]
|
|
||||||
fn new(path: String) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OnDiskCorpus::new(PathBuf::from(path)).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_corpus(slf: Py<Self>) -> PythonCorpus {
|
|
||||||
PythonCorpus::new_on_disk(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonOnDiskCorpus>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -485,91 +485,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
/// `Testcase` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
|
||||||
|
|
||||||
use libafl_bolts::ownedref::OwnedMutPtr;
|
|
||||||
use pyo3::{prelude::*, types::PyDict};
|
|
||||||
|
|
||||||
use super::{HasMetadata, Testcase};
|
|
||||||
use crate::{inputs::BytesInput, pybind::PythonMetadata};
|
|
||||||
|
|
||||||
/// `PythonTestcase` with fixed generics
|
|
||||||
pub type PythonTestcase = Testcase<BytesInput>;
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "Testcase")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Python class for Testcase
|
|
||||||
pub struct PythonTestcaseWrapper {
|
|
||||||
/// Rust wrapped Testcase object
|
|
||||||
pub inner: OwnedMutPtr<PythonTestcase>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PythonTestcaseWrapper {
|
|
||||||
pub fn wrap(r: &mut PythonTestcase) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedMutPtr::Ptr(r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap(&self) -> &PythonTestcase {
|
|
||||||
self.inner.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_mut(&mut self) -> &mut PythonTestcase {
|
|
||||||
self.inner.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonTestcaseWrapper {
|
|
||||||
#[new]
|
|
||||||
fn new(input: Vec<u8>) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedMutPtr::Owned(Box::new(PythonTestcase::new(BytesInput::new(input)))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[getter]
|
|
||||||
fn exec_time_ms(&self) -> Option<u128> {
|
|
||||||
self.inner.as_ref().exec_time().map(|t| t.as_millis())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[getter]
|
|
||||||
fn executions(&self) -> u64 {
|
|
||||||
*self.inner.as_ref().executions()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[getter]
|
|
||||||
fn parent_id(&self) -> Option<usize> {
|
|
||||||
self.inner.as_ref().parent_id().map(|x| x.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[getter]
|
|
||||||
fn scheduled_count(&self) -> usize {
|
|
||||||
self.inner.as_ref().scheduled_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metadata(&mut self) -> PyObject {
|
|
||||||
let meta = self.inner.as_mut().metadata_map_mut();
|
|
||||||
if !meta.contains::<PythonMetadata>() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let dict: Py<PyDict> = PyDict::new(py).into();
|
|
||||||
meta.insert(PythonMetadata::new(dict.to_object(py)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
meta.get::<PythonMetadata>().unwrap().map.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonTestcaseWrapper>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -911,100 +911,3 @@ mod tests {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `EventManager` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
events::{
|
|
||||||
simple::pybind::PythonSimpleEventManager, Event, EventFirer, EventManager,
|
|
||||||
EventManagerId, EventProcessor, EventRestarter, HasEventManagerId, ProgressReporter,
|
|
||||||
},
|
|
||||||
executors::pybind::PythonExecutor,
|
|
||||||
fuzzer::pybind::PythonStdFuzzer,
|
|
||||||
inputs::BytesInput,
|
|
||||||
state::{pybind::PythonStdState, UsesState},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PythonEventManagerWrapper {
|
|
||||||
Simple(Py<PythonSimpleEventManager>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// EventManager Trait binding
|
|
||||||
#[pyclass(unsendable, name = "EventManager")]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PythonEventManager {
|
|
||||||
pub wrapper: PythonEventManagerWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_body!($wrapper, $name, $body, PythonEventManagerWrapper, {
|
|
||||||
Simple
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonEventManagerWrapper, {
|
|
||||||
Simple
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonEventManager {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_simple(mgr: Py<PythonSimpleEventManager>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonEventManagerWrapper::Simple(mgr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesState for PythonEventManager {
|
|
||||||
type State = PythonStdState;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventFirer for PythonEventManager {
|
|
||||||
fn fire(&mut self, state: &mut Self::State, event: Event<BytesInput>) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, e, { e.fire(state, event) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventRestarter for PythonEventManager {}
|
|
||||||
|
|
||||||
impl EventProcessor<PythonExecutor, PythonStdFuzzer> for PythonEventManager {
|
|
||||||
fn process(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut PythonStdFuzzer,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
executor: &mut PythonExecutor,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, e, { e.process(fuzzer, state, executor) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProgressReporter for PythonEventManager {}
|
|
||||||
|
|
||||||
impl HasEventManagerId for PythonEventManager {
|
|
||||||
fn mgr_id(&self) -> EventManagerId {
|
|
||||||
unwrap_me!(self.wrapper, e, { e.mgr_id() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventManager<PythonExecutor, PythonStdFuzzer> for PythonEventManager {}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonEventManager>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -601,45 +601,3 @@ where
|
|||||||
Ok((state, mgr))
|
Ok((state, mgr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `SimpleEventManager` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
events::{pybind::PythonEventManager, SimpleEventManager},
|
|
||||||
monitors::pybind::PythonMonitor,
|
|
||||||
state::pybind::PythonStdState,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "SimpleEventManager")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Python class for SimpleEventManager
|
|
||||||
pub struct PythonSimpleEventManager {
|
|
||||||
/// Rust wrapped SimpleEventManager object
|
|
||||||
pub inner: SimpleEventManager<PythonMonitor, PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonSimpleEventManager {
|
|
||||||
#[new]
|
|
||||||
fn new(py_monitor: PythonMonitor) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: SimpleEventManager::new(py_monitor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_manager(slf: Py<Self>) -> PythonEventManager {
|
|
||||||
PythonEventManager::new_simple(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonSimpleEventManager>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -565,74 +565,3 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
/// `InProcess` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
|
|
||||||
use libafl_bolts::tuples::tuple_list;
|
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
events::pybind::PythonEventManager,
|
|
||||||
executors::{inprocess::OwnedInProcessExecutor, pybind::PythonExecutor, ExitKind},
|
|
||||||
fuzzer::pybind::PythonStdFuzzerWrapper,
|
|
||||||
inputs::{BytesInput, HasBytesVec},
|
|
||||||
observers::pybind::PythonObserversTuple,
|
|
||||||
state::pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "InProcessExecutor")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Python class for OwnedInProcessExecutor (i.e. InProcessExecutor with owned harness)
|
|
||||||
pub struct PythonOwnedInProcessExecutor {
|
|
||||||
/// Rust wrapped OwnedInProcessExecutor object
|
|
||||||
pub inner: OwnedInProcessExecutor<PythonObserversTuple, PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonOwnedInProcessExecutor {
|
|
||||||
#[new]
|
|
||||||
fn new(
|
|
||||||
harness: PyObject,
|
|
||||||
py_observers: PythonObserversTuple,
|
|
||||||
py_fuzzer: &mut PythonStdFuzzerWrapper,
|
|
||||||
py_state: &mut PythonStdStateWrapper,
|
|
||||||
py_event_manager: &mut PythonEventManager,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedInProcessExecutor::generic(
|
|
||||||
tuple_list!(),
|
|
||||||
Box::new(move |input: &BytesInput| {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
let args = (PyBytes::new(py, input.bytes()),);
|
|
||||||
harness.call1(py, args)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
ExitKind::Ok
|
|
||||||
}),
|
|
||||||
py_observers,
|
|
||||||
py_fuzzer.unwrap_mut(),
|
|
||||||
py_state.unwrap_mut(),
|
|
||||||
py_event_manager,
|
|
||||||
)
|
|
||||||
.expect("Failed to create the Executor"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_executor(slf: Py<Self>) -> PythonExecutor {
|
|
||||||
PythonExecutor::new_inprocess(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonOwnedInProcessExecutor>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -257,274 +257,3 @@ pub mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
/// `Executor` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
events::pybind::PythonEventManager,
|
|
||||||
executors::{
|
|
||||||
inprocess::pybind::PythonOwnedInProcessExecutor, Executor, ExitKind, HasObservers,
|
|
||||||
},
|
|
||||||
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
|
|
||||||
inputs::HasBytesVec,
|
|
||||||
observers::{pybind::PythonObserversTuple, UsesObservers},
|
|
||||||
state::{
|
|
||||||
pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
UsesState,
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "ExitKind")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct PythonExitKind {
|
|
||||||
pub inner: ExitKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ExitKind> for PythonExitKind {
|
|
||||||
fn from(inner: ExitKind) -> Self {
|
|
||||||
Self { inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonExitKind {
|
|
||||||
fn __eq__(&self, other: &PythonExitKind) -> bool {
|
|
||||||
self.inner == other.inner
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
fn is_ok(&self) -> bool {
|
|
||||||
self.inner == ExitKind::Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
fn is_crash(&self) -> bool {
|
|
||||||
self.inner == ExitKind::Crash
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
fn is_oom(&self) -> bool {
|
|
||||||
self.inner == ExitKind::Oom
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
fn is_timeout(&self) -> bool {
|
|
||||||
self.inner == ExitKind::Timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
fn ok() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: ExitKind::Ok,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
fn crash() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: ExitKind::Crash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
fn oom() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: ExitKind::Oom,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
fn timeout() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: ExitKind::Timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PyObjectExecutor {
|
|
||||||
inner: PyObject,
|
|
||||||
tuple: PythonObserversTuple,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PyObjectExecutor {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(obj: PyObject) -> Self {
|
|
||||||
let tuple = Python::with_gil(|py| -> PyResult<PythonObserversTuple> {
|
|
||||||
obj.call_method1(py, "observers", ())?.extract(py)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
PyObjectExecutor { inner: obj, tuple }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesState for PyObjectExecutor {
|
|
||||||
type State = PythonStdState;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesObservers for PyObjectExecutor {
|
|
||||||
type Observers = PythonObserversTuple;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasObservers for PyObjectExecutor {
|
|
||||||
#[inline]
|
|
||||||
fn observers(&self) -> &PythonObserversTuple {
|
|
||||||
&self.tuple
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observers_mut(&mut self) -> &mut PythonObserversTuple {
|
|
||||||
&mut self.tuple
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Executor<PythonEventManager, PythonStdFuzzer> for PyObjectExecutor {
|
|
||||||
#[inline]
|
|
||||||
fn run_target(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut PythonStdFuzzer,
|
|
||||||
state: &mut Self::State,
|
|
||||||
mgr: &mut PythonEventManager,
|
|
||||||
input: &Self::Input,
|
|
||||||
) -> Result<ExitKind, Error> {
|
|
||||||
let ek = Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let ek: PythonExitKind = self
|
|
||||||
.inner
|
|
||||||
.call_method1(
|
|
||||||
py,
|
|
||||||
"run_target",
|
|
||||||
(
|
|
||||||
PythonStdFuzzerWrapper::wrap(fuzzer),
|
|
||||||
PythonStdStateWrapper::wrap(state),
|
|
||||||
mgr.clone(),
|
|
||||||
input.bytes(),
|
|
||||||
),
|
|
||||||
)?
|
|
||||||
.extract(py)?;
|
|
||||||
Ok(ek)
|
|
||||||
})?;
|
|
||||||
Ok(ek.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum PythonExecutorWrapper {
|
|
||||||
InProcess(Py<PythonOwnedInProcessExecutor>),
|
|
||||||
Python(PyObjectExecutor),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "Executor")]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
/// Executor<Input = I> + HasObservers Trait binding
|
|
||||||
pub struct PythonExecutor {
|
|
||||||
wrapper: PythonExecutorWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_body!($wrapper, $name, $body, PythonExecutorWrapper,
|
|
||||||
{ InProcess },
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonExecutorWrapper,
|
|
||||||
{ InProcess },
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonExecutor {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_inprocess(owned_inprocess_executor: Py<PythonOwnedInProcessExecutor>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonExecutorWrapper::InProcess(owned_inprocess_executor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_py(obj: PyObject) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonExecutorWrapper::Python(PyObjectExecutor::new(obj)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap_py(&self) -> Option<PyObject> {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonExecutorWrapper::Python(pyo) => Some(pyo.inner.clone()),
|
|
||||||
PythonExecutorWrapper::InProcess(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesState for PythonExecutor {
|
|
||||||
type State = PythonStdState;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesObservers for PythonExecutor {
|
|
||||||
type Observers = PythonObserversTuple;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasObservers for PythonExecutor {
|
|
||||||
#[inline]
|
|
||||||
fn observers(&self) -> &PythonObserversTuple {
|
|
||||||
let ptr = unwrap_me!(self.wrapper, e, { core::ptr::from_ref(e.observers()) });
|
|
||||||
unsafe { ptr.as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observers_mut(&mut self) -> &mut PythonObserversTuple {
|
|
||||||
let ptr = unwrap_me_mut!(self.wrapper, e, { core::ptr::from_mut(e.observers_mut()) });
|
|
||||||
unsafe { ptr.as_mut().unwrap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Executor<PythonEventManager, PythonStdFuzzer> for PythonExecutor {
|
|
||||||
#[inline]
|
|
||||||
fn run_target(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut PythonStdFuzzer,
|
|
||||||
state: &mut Self::State,
|
|
||||||
mgr: &mut PythonEventManager,
|
|
||||||
input: &Self::Input,
|
|
||||||
) -> Result<ExitKind, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, e, { e.run_target(fuzzer, state, mgr, input) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonExitKind>()?;
|
|
||||||
m.add_class::<PythonExecutor>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -854,127 +854,3 @@ mod tests {
|
|||||||
assert!(!NextPow2IsNovel::is_novel(255_u8, 255));
|
assert!(!NextPow2IsNovel::is_novel(255_u8, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MapFeedback` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use concat_idents::concat_idents;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use super::{Debug, HasObserverName, MaxMapFeedback};
|
|
||||||
use crate::{feedbacks::pybind::PythonFeedback, state::pybind::PythonStdState};
|
|
||||||
|
|
||||||
macro_rules! define_python_map_feedback {
|
|
||||||
($struct_name:ident, $py_name:tt, $datatype:ty, $map_observer_type_name: ident, $my_std_state_type_name: ident) => {
|
|
||||||
use crate::observers::map::pybind::$map_observer_type_name;
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = $py_name)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
/// Python class for MaxMapFeedback
|
|
||||||
pub struct $struct_name {
|
|
||||||
/// Rust wrapped MaxMapFeedback object
|
|
||||||
pub inner: MaxMapFeedback<
|
|
||||||
$map_observer_type_name, /* PythonMapObserverI8 */
|
|
||||||
$my_std_state_type_name,
|
|
||||||
$datatype,
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl $struct_name {
|
|
||||||
#[new]
|
|
||||||
fn new(observer: &$map_observer_type_name) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: MaxMapFeedback::new(observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_feedback(slf: Py<Self>) -> PythonFeedback {
|
|
||||||
concat_idents!(func = new_max_map_,$datatype {
|
|
||||||
PythonFeedback::func(slf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasObserverName for $struct_name {
|
|
||||||
fn observer_name(&self) -> &str {
|
|
||||||
self.inner.observer_name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackI8,
|
|
||||||
"MaxMapFeedbackI8",
|
|
||||||
i8,
|
|
||||||
PythonMapObserverI8,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackI16,
|
|
||||||
"MaxMapFeedbackI16",
|
|
||||||
i16,
|
|
||||||
PythonMapObserverI16,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackI32,
|
|
||||||
"MaxMapFeedbackI32",
|
|
||||||
i32,
|
|
||||||
PythonMapObserverI32,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackI64,
|
|
||||||
"MaxMapFeedbackI64",
|
|
||||||
i64,
|
|
||||||
PythonMapObserverI64,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackU8,
|
|
||||||
"MaxMapFeedbackU8",
|
|
||||||
u8,
|
|
||||||
PythonMapObserverU8,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackU16,
|
|
||||||
"MaxMapFeedbackU16",
|
|
||||||
u16,
|
|
||||||
PythonMapObserverU16,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackU32,
|
|
||||||
"MaxMapFeedbackU32",
|
|
||||||
u32,
|
|
||||||
PythonMapObserverU32,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
define_python_map_feedback!(
|
|
||||||
PythonMaxMapFeedbackU64,
|
|
||||||
"MaxMapFeedbackU64",
|
|
||||||
u64,
|
|
||||||
PythonMapObserverU64,
|
|
||||||
PythonStdState
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonMaxMapFeedbackI8>()?;
|
|
||||||
m.add_class::<PythonMaxMapFeedbackI16>()?;
|
|
||||||
m.add_class::<PythonMaxMapFeedbackI32>()?;
|
|
||||||
m.add_class::<PythonMaxMapFeedbackI64>()?;
|
|
||||||
|
|
||||||
m.add_class::<PythonMaxMapFeedbackU8>()?;
|
|
||||||
m.add_class::<PythonMaxMapFeedbackU16>()?;
|
|
||||||
m.add_class::<PythonMaxMapFeedbackU32>()?;
|
|
||||||
m.add_class::<PythonMaxMapFeedbackU64>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1034,604 +1034,3 @@ impl From<bool> for ConstFeedback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Feedback` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use core::ptr;
|
|
||||||
use std::cell::UnsafeCell;
|
|
||||||
|
|
||||||
use libafl_bolts::Named;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
ConstFeedback, CrashFeedback, Debug, EagerAndFeedback, EagerOrFeedback, FastAndFeedback,
|
|
||||||
FastOrFeedback, Feedback, NotFeedback, String, ToString,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
corpus::{testcase::pybind::PythonTestcaseWrapper, Testcase},
|
|
||||||
events::{pybind::PythonEventManager, EventFirer},
|
|
||||||
executors::{pybind::PythonExitKind, ExitKind},
|
|
||||||
feedbacks::map::pybind::{
|
|
||||||
PythonMaxMapFeedbackI16, PythonMaxMapFeedbackI32, PythonMaxMapFeedbackI64,
|
|
||||||
PythonMaxMapFeedbackI8, PythonMaxMapFeedbackU16, PythonMaxMapFeedbackU32,
|
|
||||||
PythonMaxMapFeedbackU64, PythonMaxMapFeedbackU8,
|
|
||||||
},
|
|
||||||
inputs::{BytesInput, HasBytesVec},
|
|
||||||
observers::{pybind::PythonObserversTuple, ObserversTuple},
|
|
||||||
state::pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PyObjectFeedback {
|
|
||||||
inner: PyObject,
|
|
||||||
name: UnsafeCell<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for PyObjectFeedback {
|
|
||||||
fn clone(&self) -> PyObjectFeedback {
|
|
||||||
PyObjectFeedback {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PyObjectFeedback {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(obj: PyObject) -> Self {
|
|
||||||
PyObjectFeedback {
|
|
||||||
inner: obj,
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// crate::impl_serde_pyobjectwrapper!(PyObjectObserver, inner);
|
|
||||||
|
|
||||||
impl Named for PyObjectFeedback {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
let s = Python::with_gil(|py| -> PyResult<String> {
|
|
||||||
let s: String = self.inner.call_method0(py, "name")?.extract(py)?;
|
|
||||||
Ok(s)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
unsafe {
|
|
||||||
*self.name.get() = s;
|
|
||||||
&*self.name.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Feedback<PythonStdState> for PyObjectFeedback {
|
|
||||||
fn init_state(&mut self, state: &mut PythonStdState) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner
|
|
||||||
.call_method1(py, "init_state", (PythonStdStateWrapper::wrap(state),))?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_interesting<EM, OT>(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
manager: &mut EM,
|
|
||||||
input: &BytesInput,
|
|
||||||
observers: &OT,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<bool, Error>
|
|
||||||
where
|
|
||||||
EM: EventFirer<State = PythonStdState>,
|
|
||||||
OT: ObserversTuple<PythonStdState>,
|
|
||||||
{
|
|
||||||
// # Safety
|
|
||||||
// We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
|
|
||||||
let dont_look_at_this: &PythonObserversTuple =
|
|
||||||
unsafe { &*(ptr::from_ref(observers) as *const PythonObserversTuple) };
|
|
||||||
let dont_look_at_this2: &PythonEventManager =
|
|
||||||
unsafe { &*(ptr::from_mut(manager) as *const PythonEventManager) };
|
|
||||||
Ok(Python::with_gil(|py| -> PyResult<bool> {
|
|
||||||
let r: bool = self
|
|
||||||
.inner
|
|
||||||
.call_method1(
|
|
||||||
py,
|
|
||||||
"is_interesting",
|
|
||||||
(
|
|
||||||
PythonStdStateWrapper::wrap(state),
|
|
||||||
dont_look_at_this2.clone(),
|
|
||||||
input.bytes(),
|
|
||||||
dont_look_at_this.clone(),
|
|
||||||
PythonExitKind::from(*exit_kind),
|
|
||||||
),
|
|
||||||
)?
|
|
||||||
.extract(py)?;
|
|
||||||
Ok(r)
|
|
||||||
})?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_metadata<EM, OT>(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
_manager: &mut EM,
|
|
||||||
observers: &OT,
|
|
||||||
testcase: &mut Testcase<BytesInput>,
|
|
||||||
) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<PythonStdState>,
|
|
||||||
{
|
|
||||||
// # Safety
|
|
||||||
// We use this observer in Python ony when the ObserverTuple is PythonObserversTuple
|
|
||||||
let dont_look_at_this: &PythonObserversTuple =
|
|
||||||
unsafe { &*(ptr::from_ref(observers) as *const PythonObserversTuple) };
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"append_metadata",
|
|
||||||
(
|
|
||||||
PythonStdStateWrapper::wrap(state),
|
|
||||||
dont_look_at_this.clone(),
|
|
||||||
PythonTestcaseWrapper::wrap(testcase),
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discard_metadata(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"discard_metadata",
|
|
||||||
(PythonStdStateWrapper::wrap(state), input.bytes()),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[pyclass(unsendable, name = "CrashFeedback")]
|
|
||||||
pub struct PythonCrashFeedback {
|
|
||||||
pub inner: CrashFeedback,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonCrashFeedback {
|
|
||||||
#[new]
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: CrashFeedback::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_feedback(slf: Py<Self>) -> PythonFeedback {
|
|
||||||
PythonFeedback::new_crash(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[pyclass(unsendable, name = "ConstFeedback")]
|
|
||||||
pub struct PythonConstFeedback {
|
|
||||||
pub inner: ConstFeedback,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonConstFeedback {
|
|
||||||
#[new]
|
|
||||||
fn new(v: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: ConstFeedback::new(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_feedback(slf: Py<Self>) -> PythonFeedback {
|
|
||||||
PythonFeedback::new_const(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[pyclass(unsendable, name = "NotFeedback")]
|
|
||||||
pub struct PythonNotFeedback {
|
|
||||||
pub inner: NotFeedback<PythonFeedback, PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonNotFeedback {
|
|
||||||
#[new]
|
|
||||||
fn new(feedback: PythonFeedback) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: NotFeedback::new(feedback),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_feedback(slf: Py<Self>) -> PythonFeedback {
|
|
||||||
PythonFeedback::new_not(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! define_combined {
|
|
||||||
($feed:ident, $pyname:ident, $pystring:tt, $method:ident) => {
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[pyclass(unsendable, name = $pystring)]
|
|
||||||
pub struct $pyname {
|
|
||||||
pub inner: $feed<PythonFeedback, PythonFeedback, PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl $pyname {
|
|
||||||
#[new]
|
|
||||||
fn new(a: PythonFeedback, b: PythonFeedback) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: $feed::new(a, b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_feedback(slf: Py<Self>) -> PythonFeedback {
|
|
||||||
PythonFeedback::$method(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
define_combined!(
|
|
||||||
EagerAndFeedback,
|
|
||||||
PythonEagerAndFeedback,
|
|
||||||
"EagerAndFeedback",
|
|
||||||
new_and
|
|
||||||
);
|
|
||||||
define_combined!(
|
|
||||||
FastAndFeedback,
|
|
||||||
PythonFastAndFeedback,
|
|
||||||
"FastAndFeedback",
|
|
||||||
new_fast_and
|
|
||||||
);
|
|
||||||
define_combined!(
|
|
||||||
EagerOrFeedback,
|
|
||||||
PythonEagerOrFeedback,
|
|
||||||
"EagerOrFeedback",
|
|
||||||
new_or
|
|
||||||
);
|
|
||||||
define_combined!(
|
|
||||||
FastOrFeedback,
|
|
||||||
PythonFastOrFeedback,
|
|
||||||
"FastOrFeedback",
|
|
||||||
new_fast_or
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum PythonFeedbackWrapper {
|
|
||||||
MaxMapI8(Py<PythonMaxMapFeedbackI8>),
|
|
||||||
MaxMapI16(Py<PythonMaxMapFeedbackI16>),
|
|
||||||
MaxMapI32(Py<PythonMaxMapFeedbackI32>),
|
|
||||||
MaxMapI64(Py<PythonMaxMapFeedbackI64>),
|
|
||||||
MaxMapU8(Py<PythonMaxMapFeedbackU8>),
|
|
||||||
MaxMapU16(Py<PythonMaxMapFeedbackU16>),
|
|
||||||
MaxMapU32(Py<PythonMaxMapFeedbackU32>),
|
|
||||||
MaxMapU64(Py<PythonMaxMapFeedbackU64>),
|
|
||||||
Crash(Py<PythonCrashFeedback>),
|
|
||||||
Const(Py<PythonConstFeedback>),
|
|
||||||
Not(Py<PythonNotFeedback>),
|
|
||||||
And(Py<PythonEagerAndFeedback>),
|
|
||||||
FastAnd(Py<PythonFastAndFeedback>),
|
|
||||||
Or(Py<PythonEagerOrFeedback>),
|
|
||||||
FastOr(Py<PythonFastOrFeedback>),
|
|
||||||
Python(PyObjectFeedback),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "Feedback")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Observer Trait binding
|
|
||||||
pub struct PythonFeedback {
|
|
||||||
pub wrapper: PythonFeedbackWrapper,
|
|
||||||
name: UnsafeCell<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_body!($wrapper, $name, $body, PythonFeedbackWrapper,
|
|
||||||
{
|
|
||||||
MaxMapI8,
|
|
||||||
MaxMapI16,
|
|
||||||
MaxMapI32,
|
|
||||||
MaxMapI64,
|
|
||||||
MaxMapU8,
|
|
||||||
MaxMapU16,
|
|
||||||
MaxMapU32,
|
|
||||||
MaxMapU64,
|
|
||||||
Crash,
|
|
||||||
Const,
|
|
||||||
Not,
|
|
||||||
And,
|
|
||||||
FastAnd,
|
|
||||||
Or,
|
|
||||||
FastOr
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonFeedbackWrapper,
|
|
||||||
{
|
|
||||||
MaxMapI8,
|
|
||||||
MaxMapI16,
|
|
||||||
MaxMapI32,
|
|
||||||
MaxMapI64,
|
|
||||||
MaxMapU8,
|
|
||||||
MaxMapU16,
|
|
||||||
MaxMapU32,
|
|
||||||
MaxMapU64,
|
|
||||||
Crash,
|
|
||||||
Const,
|
|
||||||
Not,
|
|
||||||
And,
|
|
||||||
FastAnd,
|
|
||||||
Or,
|
|
||||||
FastOr
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for PythonFeedback {
|
|
||||||
fn clone(&self) -> PythonFeedback {
|
|
||||||
PythonFeedback {
|
|
||||||
wrapper: self.wrapper.clone(),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonFeedback {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_i8(map_feedback: Py<PythonMaxMapFeedbackI8>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapI8(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_i16(map_feedback: Py<PythonMaxMapFeedbackI16>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapI16(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_i32(map_feedback: Py<PythonMaxMapFeedbackI32>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapI32(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_i64(map_feedback: Py<PythonMaxMapFeedbackI64>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapI64(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_u8(map_feedback: Py<PythonMaxMapFeedbackU8>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapU8(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_u16(map_feedback: Py<PythonMaxMapFeedbackU16>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapU16(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_u32(map_feedback: Py<PythonMaxMapFeedbackU32>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapU32(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_max_map_u64(map_feedback: Py<PythonMaxMapFeedbackU64>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::MaxMapU64(map_feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_crash(feedback: Py<PythonCrashFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::Crash(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_const(feedback: Py<PythonConstFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::Const(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_not(feedback: Py<PythonNotFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::Not(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_and(feedback: Py<PythonEagerAndFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::And(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_fast_and(feedback: Py<PythonFastAndFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::FastAnd(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_or(feedback: Py<PythonEagerOrFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::Or(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_fast_or(feedback: Py<PythonFastOrFeedback>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::FastOr(feedback),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_py(obj: PyObject) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonFeedbackWrapper::Python(PyObjectFeedback::new(obj)),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_py(&self) -> Option<PyObject> {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonFeedbackWrapper::Python(pyo) => Some(pyo.inner.clone()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for PythonFeedback {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
let s = unwrap_me!(self.wrapper, f, { f.name().to_string() });
|
|
||||||
unsafe {
|
|
||||||
*self.name.get() = s;
|
|
||||||
&*self.name.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Feedback<PythonStdState> for PythonFeedback {
|
|
||||||
fn init_state(&mut self, state: &mut PythonStdState) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, f, {
|
|
||||||
Feedback::<PythonStdState>::init_state(f, state)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_interesting<EM, OT>(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
manager: &mut EM,
|
|
||||||
input: &BytesInput,
|
|
||||||
observers: &OT,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<bool, Error>
|
|
||||||
where
|
|
||||||
EM: EventFirer<State = PythonStdState>,
|
|
||||||
OT: ObserversTuple<PythonStdState>,
|
|
||||||
{
|
|
||||||
unwrap_me_mut!(self.wrapper, f, {
|
|
||||||
f.is_interesting(state, manager, input, observers, exit_kind)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_metadata<EM, OT>(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
manager: &mut EM,
|
|
||||||
observers: &OT,
|
|
||||||
testcase: &mut Testcase<BytesInput>,
|
|
||||||
) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<PythonStdState>,
|
|
||||||
EM: EventFirer<State = PythonStdState>,
|
|
||||||
{
|
|
||||||
unwrap_me_mut!(self.wrapper, f, {
|
|
||||||
f.append_metadata(state, manager, observers, testcase)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discard_metadata(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, f, { f.discard_metadata(state, input) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonCrashFeedback>()?;
|
|
||||||
m.add_class::<PythonConstFeedback>()?;
|
|
||||||
m.add_class::<PythonNotFeedback>()?;
|
|
||||||
m.add_class::<PythonEagerAndFeedback>()?;
|
|
||||||
m.add_class::<PythonFastAndFeedback>()?;
|
|
||||||
m.add_class::<PythonEagerOrFeedback>()?;
|
|
||||||
m.add_class::<PythonFastOrFeedback>()?;
|
|
||||||
m.add_class::<PythonFeedback>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -811,110 +811,3 @@ pub mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
/// `Fuzzer` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
|
||||||
|
|
||||||
use libafl_bolts::ownedref::OwnedMutPtr;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
events::pybind::PythonEventManager,
|
|
||||||
executors::pybind::PythonExecutor,
|
|
||||||
feedbacks::pybind::PythonFeedback,
|
|
||||||
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
|
|
||||||
inputs::BytesInput,
|
|
||||||
observers::pybind::PythonObserversTuple,
|
|
||||||
schedulers::QueueScheduler,
|
|
||||||
stages::pybind::PythonStagesTuple,
|
|
||||||
state::pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// `StdFuzzer` with fixed generics
|
|
||||||
pub type PythonStdFuzzer = StdFuzzer<
|
|
||||||
QueueScheduler<PythonStdState>,
|
|
||||||
PythonFeedback,
|
|
||||||
PythonFeedback,
|
|
||||||
PythonObserversTuple,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Python class for StdFuzzer
|
|
||||||
#[pyclass(unsendable, name = "StdFuzzer")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PythonStdFuzzerWrapper {
|
|
||||||
/// Rust wrapped StdFuzzer object
|
|
||||||
pub inner: OwnedMutPtr<PythonStdFuzzer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PythonStdFuzzerWrapper {
|
|
||||||
pub fn wrap(r: &mut PythonStdFuzzer) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedMutPtr::Ptr(r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap(&self) -> &PythonStdFuzzer {
|
|
||||||
self.inner.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_mut(&mut self) -> &mut PythonStdFuzzer {
|
|
||||||
self.inner.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStdFuzzerWrapper {
|
|
||||||
#[new]
|
|
||||||
fn new(py_feedback: PythonFeedback, py_objective: PythonFeedback) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedMutPtr::Owned(Box::new(StdFuzzer::new(
|
|
||||||
QueueScheduler::new(),
|
|
||||||
py_feedback,
|
|
||||||
py_objective,
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_input(
|
|
||||||
&mut self,
|
|
||||||
py_state: &mut PythonStdStateWrapper,
|
|
||||||
py_executor: &mut PythonExecutor,
|
|
||||||
py_mgr: &mut PythonEventManager,
|
|
||||||
input: Vec<u8>,
|
|
||||||
) -> usize {
|
|
||||||
self.inner
|
|
||||||
.as_mut()
|
|
||||||
.add_input(
|
|
||||||
py_state.unwrap_mut(),
|
|
||||||
py_executor,
|
|
||||||
py_mgr,
|
|
||||||
BytesInput::new(input),
|
|
||||||
)
|
|
||||||
.expect("Failed to add input")
|
|
||||||
.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fuzz_loop(
|
|
||||||
&mut self,
|
|
||||||
py_executor: &mut PythonExecutor,
|
|
||||||
py_state: &mut PythonStdStateWrapper,
|
|
||||||
py_mgr: &mut PythonEventManager,
|
|
||||||
stages_tuple: &mut PythonStagesTuple,
|
|
||||||
) {
|
|
||||||
self.inner
|
|
||||||
.as_mut()
|
|
||||||
.fuzz_loop(stages_tuple, py_executor, py_state.unwrap_mut(), py_mgr)
|
|
||||||
.expect("Failed to generate the initial corpus");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStdFuzzerWrapper>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -166,179 +166,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Generator` Python bindings
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
generators::{Generator, RandBytesGenerator, RandPrintablesGenerator},
|
|
||||||
inputs::{BytesInput, HasBytesVec},
|
|
||||||
state::pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PyObjectGenerator {
|
|
||||||
inner: PyObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PyObjectGenerator {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(obj: PyObject) -> Self {
|
|
||||||
PyObjectGenerator { inner: obj }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Generator<BytesInput, PythonStdState> for PyObjectGenerator {
|
|
||||||
fn generate(&mut self, state: &mut PythonStdState) -> Result<BytesInput, Error> {
|
|
||||||
let bytes = Python::with_gil(|py| -> PyResult<Vec<u8>> {
|
|
||||||
self.inner
|
|
||||||
.call_method1(py, "generate", (PythonStdStateWrapper::wrap(state),))?
|
|
||||||
.extract(py)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
Ok(BytesInput::new(bytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "RandBytesGenerator")]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
/// Python class for RandBytesGenerator
|
|
||||||
pub struct PythonRandBytesGenerator {
|
|
||||||
/// Rust wrapped RandBytesGenerator object
|
|
||||||
pub inner: RandBytesGenerator<PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonRandBytesGenerator {
|
|
||||||
#[new]
|
|
||||||
fn new(max_size: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: RandBytesGenerator::new(max_size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate(&mut self, state: &mut PythonStdStateWrapper) -> Vec<u8> {
|
|
||||||
self.inner
|
|
||||||
.generate(state.unwrap_mut())
|
|
||||||
.expect("PythonRandBytesGenerator::generate failed")
|
|
||||||
.bytes()
|
|
||||||
.to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_generator(slf: Py<Self>) -> PythonGenerator {
|
|
||||||
PythonGenerator::new_rand_bytes(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "RandPrintablesGenerator")]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
/// Python class for RandPrintablesGenerator
|
|
||||||
pub struct PythonRandPrintablesGenerator {
|
|
||||||
/// Rust wrapped RandPrintablesGenerator object
|
|
||||||
pub inner: RandPrintablesGenerator<PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonRandPrintablesGenerator {
|
|
||||||
#[new]
|
|
||||||
fn new(max_size: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: RandPrintablesGenerator::new(max_size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate(&mut self, state: &mut PythonStdStateWrapper) -> Vec<u8> {
|
|
||||||
self.inner
|
|
||||||
.generate(state.unwrap_mut())
|
|
||||||
.expect("PythonRandPrintablesGenerator::generate failed")
|
|
||||||
.bytes()
|
|
||||||
.to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_generator(slf: Py<Self>) -> PythonGenerator {
|
|
||||||
PythonGenerator::new_rand_printables(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
enum PythonGeneratorWrapper {
|
|
||||||
RandBytes(Py<PythonRandBytesGenerator>),
|
|
||||||
RandPrintables(Py<PythonRandPrintablesGenerator>),
|
|
||||||
Python(PyObjectGenerator),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rand Trait binding
|
|
||||||
#[pyclass(unsendable, name = "Generator")]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PythonGenerator {
|
|
||||||
wrapper: PythonGeneratorWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonGeneratorWrapper,
|
|
||||||
{ RandBytes, RandPrintables },
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonGenerator {
|
|
||||||
#[staticmethod]
|
|
||||||
fn new_rand_bytes(py_gen: Py<PythonRandBytesGenerator>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonGeneratorWrapper::RandBytes(py_gen),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
fn new_rand_printables(py_gen: Py<PythonRandPrintablesGenerator>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonGeneratorWrapper::RandPrintables(py_gen),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_py(obj: PyObject) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonGeneratorWrapper::Python(PyObjectGenerator::new(obj)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap_py(&self) -> Option<PyObject> {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonGeneratorWrapper::Python(pyo) => Some(pyo.inner.clone()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Generator<BytesInput, PythonStdState> for PythonGenerator {
|
|
||||||
fn generate(&mut self, state: &mut PythonStdState) -> Result<BytesInput, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, g, { g.generate(state) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonRandBytesGenerator>()?;
|
|
||||||
m.add_class::<PythonRandPrintablesGenerator>()?;
|
|
||||||
m.add_class::<PythonGenerator>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -233,58 +233,3 @@ mod tests {
|
|||||||
assert_eq!(state.corpus().count(), corpus_deserialized.count());
|
assert_eq!(state.corpus().count(), corpus_deserialized.count());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
corpus, events, executors, feedbacks, fuzzer, generators, monitors, mutators, observers,
|
|
||||||
stages, state,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PythonMetadata {
|
|
||||||
pub map: PyObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
libafl_bolts::impl_serde_pyobjectwrapper!(PythonMetadata, map);
|
|
||||||
libafl_bolts::impl_serdeany!(PythonMetadata);
|
|
||||||
|
|
||||||
impl PythonMetadata {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(map: PyObject) -> Self {
|
|
||||||
Self { map }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymodule]
|
|
||||||
#[pyo3(name = "libafl")]
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
libafl_bolts::rands::pybind::register(py, m)?;
|
|
||||||
observers::map::pybind::register(py, m)?;
|
|
||||||
observers::pybind::register(py, m)?;
|
|
||||||
feedbacks::map::pybind::register(py, m)?;
|
|
||||||
feedbacks::pybind::register(py, m)?;
|
|
||||||
state::pybind::register(py, m)?;
|
|
||||||
monitors::pybind::register(py, m)?;
|
|
||||||
events::pybind::register(py, m)?;
|
|
||||||
events::simple::pybind::register(py, m)?;
|
|
||||||
fuzzer::pybind::register(py, m)?;
|
|
||||||
executors::pybind::register(py, m)?;
|
|
||||||
executors::inprocess::pybind::register(py, m)?;
|
|
||||||
generators::pybind::register(py, m)?;
|
|
||||||
mutators::pybind::register(py, m)?;
|
|
||||||
mutators::scheduled::pybind::register(py, m)?;
|
|
||||||
corpus::pybind::register(py, m)?;
|
|
||||||
corpus::testcase::pybind::register(py, m)?;
|
|
||||||
corpus::ondisk::pybind::register(py, m)?;
|
|
||||||
corpus::inmemory::pybind::register(py, m)?;
|
|
||||||
corpus::cached::pybind::register(py, m)?;
|
|
||||||
stages::pybind::register(py, m)?;
|
|
||||||
stages::mutational::pybind::register(py, m)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1306,175 +1306,3 @@ impl Default for ClientPerfMonitor {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// `Monitor` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
|
||||||
use core::time::Duration;
|
|
||||||
|
|
||||||
use libafl_bolts::ClientId;
|
|
||||||
use pyo3::{prelude::*, types::PyUnicode};
|
|
||||||
|
|
||||||
use super::ClientStats;
|
|
||||||
use crate::monitors::{Monitor, SimpleMonitor};
|
|
||||||
|
|
||||||
/// A [`SimpleMonitor`] type with a boxed `FnMut` for printing
|
|
||||||
pub type SimpleBoxedFnMonitor = SimpleMonitor<Box<dyn FnMut(&str)>>;
|
|
||||||
|
|
||||||
// TODO create a PyObjectFnMut to pass, track stabilization of https://github.com/rust-lang/rust/issues/29625
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "SimpleMonitor")]
|
|
||||||
/// Python class for SimpleMonitor
|
|
||||||
pub struct PythonSimpleMonitor {
|
|
||||||
/// Rust wrapped SimpleMonitor object
|
|
||||||
pub inner: SimpleBoxedFnMonitor,
|
|
||||||
print_fn: PyObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for PythonSimpleMonitor {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("PythonSimpleMonitor")
|
|
||||||
.field("print_fn", &self.print_fn)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for PythonSimpleMonitor {
|
|
||||||
fn clone(&self) -> PythonSimpleMonitor {
|
|
||||||
let py_print_fn = self.print_fn.clone();
|
|
||||||
let closure = move |s: &str| {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
py_print_fn.call1(py, (PyUnicode::new(py, s),))?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
};
|
|
||||||
|
|
||||||
PythonSimpleMonitor {
|
|
||||||
inner: SimpleMonitor {
|
|
||||||
print_fn: Box::new(closure),
|
|
||||||
start_time: self.inner.start_time,
|
|
||||||
print_user_monitor: false,
|
|
||||||
client_stats: self.inner.client_stats.clone(),
|
|
||||||
},
|
|
||||||
print_fn: self.print_fn.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonSimpleMonitor {
|
|
||||||
#[new]
|
|
||||||
fn new(py_print_fn: PyObject) -> Self {
|
|
||||||
let py_print_fn1 = py_print_fn.clone();
|
|
||||||
let closure = move |s: &str| {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
py_print_fn1.call1(py, (PyUnicode::new(py, s),))?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
inner: SimpleMonitor::new(Box::new(closure)),
|
|
||||||
print_fn: py_print_fn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_monitor(slf: Py<Self>) -> PythonMonitor {
|
|
||||||
PythonMonitor::new_simple(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum PythonMonitorWrapper {
|
|
||||||
Simple(Py<PythonSimpleMonitor>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "Monitor")]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
/// EventManager Trait binding
|
|
||||||
pub struct PythonMonitor {
|
|
||||||
wrapper: PythonMonitorWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_body!($wrapper, $name, $body, PythonMonitorWrapper, { Simple })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonMonitorWrapper, {
|
|
||||||
Simple
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonMonitor {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_simple(simple_monitor: Py<PythonSimpleMonitor>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonMonitorWrapper::Simple(simple_monitor),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Monitor for PythonMonitor {
|
|
||||||
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats> {
|
|
||||||
let ptr = unwrap_me_mut!(self.wrapper, m, {
|
|
||||||
core::ptr::from_mut(m.client_stats_mut())
|
|
||||||
});
|
|
||||||
unsafe { ptr.as_mut().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn client_stats(&self) -> &[ClientStats] {
|
|
||||||
let ptr = unwrap_me!(self.wrapper, m, { core::ptr::from_ref(m.client_stats()) });
|
|
||||||
unsafe { ptr.as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Time this fuzzing run stated
|
|
||||||
fn start_time(&self) -> Duration {
|
|
||||||
unwrap_me!(self.wrapper, m, { m.start_time() })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// set start time
|
|
||||||
fn set_start_time(&mut self, time: Duration) {
|
|
||||||
unwrap_me_mut!(self.wrapper, m, { m.set_start_time(time) });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display(&mut self, event_msg: &str, sender_id: ClientId) {
|
|
||||||
unwrap_me_mut!(self.wrapper, m, { m.display(event_msg, sender_id) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonSimpleMonitor>()?;
|
|
||||||
m.add_class::<PythonMonitor>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::monitors::prettify_float;
|
|
||||||
#[test]
|
|
||||||
fn test_prettify_float() {
|
|
||||||
assert_eq!(prettify_float(123423123.0), "123.4M");
|
|
||||||
assert_eq!(prettify_float(12342312.3), "12.34M");
|
|
||||||
assert_eq!(prettify_float(1234231.23), "1.234M");
|
|
||||||
assert_eq!(prettify_float(123423.123), "123.4k");
|
|
||||||
assert_eq!(prettify_float(12342.3123), "12.34k");
|
|
||||||
assert_eq!(prettify_float(1234.23123), "1.234k");
|
|
||||||
assert_eq!(prettify_float(123.423123), "123.4");
|
|
||||||
assert_eq!(prettify_float(12.3423123), "12.34");
|
|
||||||
assert_eq!(prettify_float(1.23423123), "1.234");
|
|
||||||
assert_eq!(prettify_float(0.123423123), "0.123");
|
|
||||||
assert_eq!(prettify_float(0.0123423123), "0.012");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -407,197 +407,3 @@ impl<I, S> IntoVec<Box<dyn Mutator<I, S>>> for Vec<Box<dyn Mutator<I, S>>> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Mutator` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use core::ffi::CStr;
|
|
||||||
|
|
||||||
use libafl_bolts::Named;
|
|
||||||
use pyo3::{prelude::*, AsPyPointer};
|
|
||||||
|
|
||||||
use super::{MutationResult, Mutator};
|
|
||||||
use crate::{
|
|
||||||
corpus::CorpusId,
|
|
||||||
inputs::{BytesInput, HasBytesVec},
|
|
||||||
mutators::scheduled::pybind::PythonStdHavocMutator,
|
|
||||||
state::pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PyObjectMutator {
|
|
||||||
inner: PyObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PyObjectMutator {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(obj: PyObject) -> Self {
|
|
||||||
PyObjectMutator { inner: obj }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for PyObjectMutator {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
unsafe { CStr::from_ptr((*(*self.inner.as_ptr()).ob_type).tp_name) }
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mutator<BytesInput, PythonStdState> for PyObjectMutator {
|
|
||||||
fn mutate(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &mut BytesInput,
|
|
||||||
) -> Result<MutationResult, Error> {
|
|
||||||
let mutated = Python::with_gil(|py| -> PyResult<bool> {
|
|
||||||
self.inner
|
|
||||||
.call_method1(
|
|
||||||
py,
|
|
||||||
"mutate",
|
|
||||||
(PythonStdStateWrapper::wrap(state), input.bytes()),
|
|
||||||
)?
|
|
||||||
.extract(py)
|
|
||||||
})?;
|
|
||||||
Ok(if mutated {
|
|
||||||
MutationResult::Mutated
|
|
||||||
} else {
|
|
||||||
MutationResult::Skipped
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
|
|
||||||
corpus_idx: Option<CorpusId>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"post_exec",
|
|
||||||
(PythonStdStateWrapper::wrap(state), corpus_idx.map(|x| x.0)),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PythonMutatorWrapper {
|
|
||||||
StdHavoc(Py<PythonStdHavocMutator>),
|
|
||||||
Python(PyObjectMutator),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutator Trait binding
|
|
||||||
#[pyclass(unsendable, name = "Mutator")]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PythonMutator {
|
|
||||||
pub wrapper: PythonMutatorWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonMutatorWrapper, {
|
|
||||||
StdHavoc
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonMutator {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_std_havoc(mgr: Py<PythonStdHavocMutator>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonMutatorWrapper::StdHavoc(mgr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_py(obj: PyObject) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonMutatorWrapper::Python(PyObjectMutator::new(obj)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap_py(&self) -> Option<PyObject> {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonMutatorWrapper::Python(pyo) => Some(pyo.inner.clone()),
|
|
||||||
PythonMutatorWrapper::StdHavoc(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for PythonMutator {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonMutatorWrapper::Python(pyo) => pyo.name(),
|
|
||||||
PythonMutatorWrapper::StdHavoc(_) => "StdHavocPythonMutator",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mutator<BytesInput, PythonStdState> for PythonMutator {
|
|
||||||
fn mutate(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &mut BytesInput,
|
|
||||||
) -> Result<MutationResult, Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, m, { m.mutate(state, input) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
|
|
||||||
corpus_idx: Option<CorpusId>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, m, { m.post_exec(state, corpus_idx) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonMutator>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
|
||||||
|
|
||||||
use libafl_bolts::{rands::StdRand, tuples::IntoVec};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::InMemoryCorpus,
|
|
||||||
inputs::BytesInput,
|
|
||||||
mutators::{havoc_mutations, Mutator, MutatorsTuple},
|
|
||||||
state::StdState,
|
|
||||||
};
|
|
||||||
|
|
||||||
type TestStdStateType =
|
|
||||||
StdState<BytesInput, InMemoryCorpus<BytesInput>, StdRand, InMemoryCorpus<BytesInput>>;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_tuple_into_vec() {
|
|
||||||
let mutators = havoc_mutations::<BytesInput>();
|
|
||||||
let names_before = MutatorsTuple::<BytesInput, TestStdStateType>::names(&mutators);
|
|
||||||
|
|
||||||
let mutators = havoc_mutations::<BytesInput>();
|
|
||||||
let mutators_vec: Vec<Box<dyn Mutator<BytesInput, TestStdStateType>>> = mutators.into_vec();
|
|
||||||
assert_eq!(names_before, mutators_vec.names());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -568,44 +568,3 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `SchedulerMutator` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use super::{havoc_mutations, Debug, HavocMutationsType, StdScheduledMutator};
|
|
||||||
use crate::{
|
|
||||||
inputs::BytesInput, mutators::pybind::PythonMutator, state::pybind::PythonStdState,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "StdHavocMutator")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Python class for StdHavocMutator
|
|
||||||
pub struct PythonStdHavocMutator {
|
|
||||||
/// Rust wrapped StdHavocMutator object
|
|
||||||
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType<BytesInput>, PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStdHavocMutator {
|
|
||||||
#[new]
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: StdScheduledMutator::new(havoc_mutations()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mutator(slf: Py<Self>) -> PythonMutator {
|
|
||||||
PythonMutator::new_std_havoc(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStdHavocMutator>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2138,7 +2138,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Exact copy of `StdMapObserver` that owns its map
|
/// Exact copy of `StdMapObserver` that owns its map
|
||||||
/// Used for python bindings
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
@ -2366,468 +2365,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MapObserver` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use concat_idents::concat_idents;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
AsIter, AsIterMut, AsMutSlice, AsSlice, Debug, Error, HasLen, Iter, IterMut, MapObserver,
|
|
||||||
Named, Observer, OwnedMapObserver, StdMapObserver, String, Vec,
|
|
||||||
};
|
|
||||||
use crate::{inputs::UsesInput, observers::pybind::PythonObserver};
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! mapob_unwrap_me {
|
|
||||||
($wrapper_name:ident, $wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
match &$wrapper {
|
|
||||||
$wrapper_name::Std(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
let $name = &borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
$wrapper_name::Owned(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
let $name = &borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
$wrapper_name::None => panic!("Serde is not supported ATM"),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! mapob_unwrap_me_mut {
|
|
||||||
($wrapper_name:ident, $wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
match &mut $wrapper {
|
|
||||||
$wrapper_name::Std(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
let $name = &mut borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
$wrapper_name::Owned(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
let $name = &mut borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
$wrapper_name::None => panic!("Serde is not supported ATM"),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! define_python_map_observer {
|
|
||||||
($struct_name1:ident, $py_name1:tt, $struct_name2:ident, $py_name2:tt, $struct_name_trait:ident, $py_name_trait:tt, $datatype:ty, $wrapper_name: ident) => {
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = $py_name1)]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for StdMapObserver
|
|
||||||
pub struct $struct_name1 {
|
|
||||||
/// Rust wrapped StdMapObserver object
|
|
||||||
pub inner: StdMapObserver<'static, $datatype, false>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl $struct_name1 {
|
|
||||||
#[new]
|
|
||||||
fn new(name: String, ptr: usize, size: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: unsafe { StdMapObserver::from_mut_ptr(name, ptr as *mut $datatype, size) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_map_observer(slf: Py<Self>) -> $struct_name_trait {
|
|
||||||
$struct_name_trait::new_std(slf)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_observer(slf: Py<Self>) -> PythonObserver {
|
|
||||||
let m = Self::as_map_observer(slf);
|
|
||||||
Python::with_gil(|py| -> PyResult<PythonObserver> {
|
|
||||||
let p: Py<_> = Py::new(py, m)?;
|
|
||||||
Ok($struct_name_trait::as_observer(p))
|
|
||||||
}).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __getitem__(&self, idx: usize) -> $datatype {
|
|
||||||
*self.inner.get(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __setitem__(&mut self, idx: usize, val: $datatype) {
|
|
||||||
*self.inner.get_mut(idx) = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "usable_count")]
|
|
||||||
fn pyusable_count(&self) -> usize {
|
|
||||||
self.inner.usable_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "len")]
|
|
||||||
fn pylen(&self) -> usize {
|
|
||||||
self.inner.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "name")]
|
|
||||||
fn pyname(&self) -> &str {
|
|
||||||
self.inner.name()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = $py_name2)]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for OwnedMapObserver (i.e. StdMapObserver with owned map)
|
|
||||||
pub struct $struct_name2 {
|
|
||||||
/// Rust wrapped OwnedMapObserver object
|
|
||||||
pub inner: OwnedMapObserver<$datatype>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl $struct_name2 {
|
|
||||||
#[new]
|
|
||||||
fn new(name: String, map: Vec<$datatype>) -> Self {
|
|
||||||
Self {
|
|
||||||
//TODO: Not leak memory
|
|
||||||
inner: OwnedMapObserver::new(alloc::boxed::Box::leak(name.into_boxed_str()), map),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_map_observer(slf: Py<Self>) -> $struct_name_trait {
|
|
||||||
$struct_name_trait::new_owned(slf)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_observer(slf: Py<Self>) -> PythonObserver {
|
|
||||||
let m = Self::as_map_observer(slf);
|
|
||||||
Python::with_gil(|py| -> PyResult<PythonObserver> {
|
|
||||||
let p: Py<_> = Py::new(py, m)?;
|
|
||||||
Ok($struct_name_trait::as_observer(p))
|
|
||||||
}).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __getitem__(&self, idx: usize) -> $datatype {
|
|
||||||
*self.inner.get(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __setitem__(&mut self, idx: usize, val: $datatype) {
|
|
||||||
*self.inner.get_mut(idx) = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "usable_count")]
|
|
||||||
fn pyusable_count(&self) -> usize {
|
|
||||||
self.inner.usable_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "len")]
|
|
||||||
fn pylen(&self) -> usize {
|
|
||||||
self.inner.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "name")]
|
|
||||||
fn pyname(&self) -> &str {
|
|
||||||
self.inner.name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum $wrapper_name {
|
|
||||||
Std(Py<$struct_name1>),
|
|
||||||
Owned(Py<$struct_name2>),
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for $wrapper_name {
|
|
||||||
fn default() -> Self {
|
|
||||||
$wrapper_name::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should not be exposed to user
|
|
||||||
#[pyclass(unsendable, name = $py_name_trait)]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// MapObserver + Observer Trait binding
|
|
||||||
pub struct $struct_name_trait {
|
|
||||||
#[serde(skip)]
|
|
||||||
pub wrapper: $wrapper_name,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl $struct_name_trait {
|
|
||||||
#[staticmethod]
|
|
||||||
fn new_std(std_map: Py<$struct_name1>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: $wrapper_name::Std(std_map),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
fn new_owned(owned_map: Py<$struct_name2>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: $wrapper_name::Owned(owned_map),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn as_observer(slf: Py<Self>) -> PythonObserver {
|
|
||||||
concat_idents!(func = new_map_,$datatype {
|
|
||||||
PythonObserver::func(slf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __getitem__(&self, idx: usize) -> $datatype {
|
|
||||||
*self.get(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __setitem__(&mut self, idx: usize, val: $datatype) {
|
|
||||||
*self.get_mut(idx) = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "usable_count")]
|
|
||||||
fn pyusable_count(&self) -> usize {
|
|
||||||
self.usable_count()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "len")]
|
|
||||||
fn pylen(&self) -> usize {
|
|
||||||
self.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "name")]
|
|
||||||
fn pyname(&self) -> &str {
|
|
||||||
self.name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'it> AsIter<'it> for $struct_name_trait {
|
|
||||||
type Item = $datatype;
|
|
||||||
type IntoIter = Iter<'it, $datatype>;
|
|
||||||
|
|
||||||
fn as_iter(&'it self) -> Self::IntoIter {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute::<_, Self::IntoIter>(m.as_iter()) } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'it> AsIterMut<'it> for $struct_name_trait {
|
|
||||||
type Item = $datatype;
|
|
||||||
type IntoIter = IterMut<'it, $datatype>;
|
|
||||||
|
|
||||||
fn as_iter_mut(&'it mut self) -> Self::IntoIter {
|
|
||||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute::<_, Self::IntoIter>(m.as_iter_mut()) } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsSlice for $struct_name_trait {
|
|
||||||
type Entry = $datatype;
|
|
||||||
fn as_slice(&self) -> &[$datatype] {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute(m.as_slice()) }} )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMutSlice for $struct_name_trait {
|
|
||||||
type Entry = $datatype;
|
|
||||||
fn as_mut_slice(&mut self) -> &mut [$datatype] {
|
|
||||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute(m.as_mut_slice()) }} )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MapObserver for $struct_name_trait {
|
|
||||||
type Entry = $datatype;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get(&self, idx: usize) -> &$datatype {
|
|
||||||
let ptr = mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.get(idx) as *const $datatype });
|
|
||||||
unsafe { ptr.as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_mut(&mut self, idx: usize) -> &mut $datatype {
|
|
||||||
let ptr = mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.get_mut(idx) as *mut $datatype });
|
|
||||||
unsafe { ptr.as_mut().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn count_bytes(&self) -> u64 {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.count_bytes() })
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn usable_count(&self) -> usize {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.usable_count() })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash(&self) -> u64 {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.hash() })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial(&self) -> $datatype {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.initial() })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn reset_map(&mut self) -> Result<(), Error> {
|
|
||||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.reset_map() })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_vec(&self) -> Vec<$datatype> {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.to_vec() })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.how_many_set(indexes) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for $struct_name_trait {
|
|
||||||
#[inline]
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
let ptr = mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.name() as *const str });
|
|
||||||
unsafe { ptr.as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasLen for $struct_name_trait {
|
|
||||||
#[inline]
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.len() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Observer<S> for $struct_name_trait
|
|
||||||
where
|
|
||||||
Self: MapObserver,
|
|
||||||
S: UsesInput,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn pre_exec(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
|
|
||||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.pre_exec(state, input) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverI8,
|
|
||||||
"StdMapObserverI8",
|
|
||||||
PythonOwnedMapObserverI8,
|
|
||||||
"OwnedMapObserverI8",
|
|
||||||
PythonMapObserverI8,
|
|
||||||
"MapObserverI8",
|
|
||||||
i8,
|
|
||||||
PythonMapObserverWrapperI8
|
|
||||||
);
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverI16,
|
|
||||||
"StdMapObserverI16",
|
|
||||||
PythonOwnedMapObserverI16,
|
|
||||||
"OwnedMapObserverI16",
|
|
||||||
PythonMapObserverI16,
|
|
||||||
"MapObserverI16",
|
|
||||||
i16,
|
|
||||||
PythonMapObserverWrapperI16
|
|
||||||
);
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverI32,
|
|
||||||
"StdMapObserverI32",
|
|
||||||
PythonOwnedMapObserverI32,
|
|
||||||
"OwnedMapObserverI32",
|
|
||||||
PythonMapObserverI32,
|
|
||||||
"MapObserverI32",
|
|
||||||
i32,
|
|
||||||
PythonMapObserverWrapperI32
|
|
||||||
);
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverI64,
|
|
||||||
"StdMapObserverI64",
|
|
||||||
PythonOwnedMapObserverI64,
|
|
||||||
"OwnedMapObserverI64",
|
|
||||||
PythonMapObserverI64,
|
|
||||||
"MapObserverI64",
|
|
||||||
i64,
|
|
||||||
PythonMapObserverWrapperI64
|
|
||||||
);
|
|
||||||
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverU8,
|
|
||||||
"StdMapObserverU8",
|
|
||||||
PythonOwnedMapObserverU8,
|
|
||||||
"OwnedMapObserverU8",
|
|
||||||
PythonMapObserverU8,
|
|
||||||
"MapObserverU8",
|
|
||||||
u8,
|
|
||||||
PythonMapObserverWrapperU8
|
|
||||||
);
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverU16,
|
|
||||||
"StdMapObserverU16",
|
|
||||||
PythonOwnedMapObserverU16,
|
|
||||||
"OwnedMapObserverU16",
|
|
||||||
PythonMapObserverU16,
|
|
||||||
"MapObserverU16",
|
|
||||||
u16,
|
|
||||||
PythonMapObserverWrapperU16
|
|
||||||
);
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverU32,
|
|
||||||
"StdMapObserverU32",
|
|
||||||
PythonOwnedMapObserverU32,
|
|
||||||
"OwnedMapObserverU32",
|
|
||||||
PythonMapObserverU32,
|
|
||||||
"MapObserverU32",
|
|
||||||
u32,
|
|
||||||
PythonMapObserverWrapperU32
|
|
||||||
);
|
|
||||||
define_python_map_observer!(
|
|
||||||
PythonStdMapObserverU64,
|
|
||||||
"StdMapObserverU64",
|
|
||||||
PythonOwnedMapObserverU64,
|
|
||||||
"OwnedMapObserverU64",
|
|
||||||
PythonMapObserverU64,
|
|
||||||
"MapObserverU64",
|
|
||||||
u64,
|
|
||||||
PythonMapObserverWrapperU64
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStdMapObserverI8>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverI8>()?;
|
|
||||||
m.add_class::<PythonMapObserverI8>()?;
|
|
||||||
m.add_class::<PythonStdMapObserverI16>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverI16>()?;
|
|
||||||
m.add_class::<PythonMapObserverI16>()?;
|
|
||||||
m.add_class::<PythonStdMapObserverI32>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverI32>()?;
|
|
||||||
m.add_class::<PythonMapObserverI32>()?;
|
|
||||||
m.add_class::<PythonStdMapObserverI64>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverI64>()?;
|
|
||||||
m.add_class::<PythonMapObserverI64>()?;
|
|
||||||
|
|
||||||
m.add_class::<PythonStdMapObserverU8>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverU8>()?;
|
|
||||||
m.add_class::<PythonMapObserverU8>()?;
|
|
||||||
m.add_class::<PythonStdMapObserverU16>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverU16>()?;
|
|
||||||
m.add_class::<PythonMapObserverU16>()?;
|
|
||||||
m.add_class::<PythonStdMapObserverU32>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverU32>()?;
|
|
||||||
m.add_class::<PythonMapObserverU32>()?;
|
|
||||||
m.add_class::<PythonStdMapObserverU64>()?;
|
|
||||||
m.add_class::<PythonOwnedMapObserverU64>()?;
|
|
||||||
m.add_class::<PythonMapObserverU64>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -526,798 +526,6 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Observer` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::ptr;
|
|
||||||
use std::cell::UnsafeCell;
|
|
||||||
|
|
||||||
use libafl_bolts::{
|
|
||||||
tuples::{type_eq, MatchName},
|
|
||||||
Named,
|
|
||||||
};
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use super::{Debug, Observer, ObserversTuple, String};
|
|
||||||
use crate::{
|
|
||||||
executors::{pybind::PythonExitKind, ExitKind},
|
|
||||||
inputs::{BytesInput, HasBytesVec},
|
|
||||||
observers::map::pybind::{
|
|
||||||
PythonMapObserverI16, PythonMapObserverI32, PythonMapObserverI64, PythonMapObserverI8,
|
|
||||||
PythonMapObserverU16, PythonMapObserverU32, PythonMapObserverU64, PythonMapObserverU8,
|
|
||||||
PythonMapObserverWrapperI16, PythonMapObserverWrapperI32, PythonMapObserverWrapperI64,
|
|
||||||
PythonMapObserverWrapperI8, PythonMapObserverWrapperU16, PythonMapObserverWrapperU32,
|
|
||||||
PythonMapObserverWrapperU64, PythonMapObserverWrapperU8,
|
|
||||||
},
|
|
||||||
state::pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PyObjectObserver {
|
|
||||||
inner: PyObject,
|
|
||||||
name: UnsafeCell<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for PyObjectObserver {
|
|
||||||
fn clone(&self) -> PyObjectObserver {
|
|
||||||
PyObjectObserver {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PyObjectObserver {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(obj: PyObject) -> Self {
|
|
||||||
PyObjectObserver {
|
|
||||||
inner: obj,
|
|
||||||
name: UnsafeCell::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libafl_bolts::impl_serde_pyobjectwrapper!(PyObjectObserver, inner);
|
|
||||||
|
|
||||||
impl Named for PyObjectObserver {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
let s = Python::with_gil(|py| -> PyResult<String> {
|
|
||||||
let s: String = self.inner.call_method0(py, "name")?.extract(py)?;
|
|
||||||
Ok(s)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
unsafe {
|
|
||||||
*self.name.get() = s;
|
|
||||||
&*self.name.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Observer<PythonStdState> for PyObjectObserver {
|
|
||||||
fn flush(&mut self) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method0(py, "flush")?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"pre_exec",
|
|
||||||
(PythonStdStateWrapper::wrap(state), input.bytes()),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"post_exec",
|
|
||||||
(
|
|
||||||
PythonStdStateWrapper::wrap(state),
|
|
||||||
input.bytes(),
|
|
||||||
PythonExitKind::from(*exit_kind),
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_exec_child(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"pre_exec_child",
|
|
||||||
(PythonStdStateWrapper::wrap(state), input.bytes()),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec_child(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"post_exec_child",
|
|
||||||
(
|
|
||||||
PythonStdStateWrapper::wrap(state),
|
|
||||||
input.bytes(),
|
|
||||||
PythonExitKind::from(*exit_kind),
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub enum PythonObserverWrapper {
|
|
||||||
MapI8(Py<PythonMapObserverI8>),
|
|
||||||
MapI16(Py<PythonMapObserverI16>),
|
|
||||||
MapI32(Py<PythonMapObserverI32>),
|
|
||||||
MapI64(Py<PythonMapObserverI64>),
|
|
||||||
MapU8(Py<PythonMapObserverU8>),
|
|
||||||
MapU16(Py<PythonMapObserverU16>),
|
|
||||||
MapU32(Py<PythonMapObserverU32>),
|
|
||||||
MapU64(Py<PythonMapObserverU64>),
|
|
||||||
Python(PyObjectObserver),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "Observer")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
/// Observer Trait binding
|
|
||||||
pub struct PythonObserver {
|
|
||||||
pub wrapper: PythonObserverWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
match &$wrapper {
|
|
||||||
PythonObserverWrapper::MapI8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperI8,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
PythonObserverWrapper::MapI16(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperI16,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI32(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperI32,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI64(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperI64,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperU8,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
PythonObserverWrapper::MapU16(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperU16,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU32(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperU32,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU64(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
Ok(crate::mapob_unwrap_me!(
|
|
||||||
PythonMapObserverWrapperU64,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
match &mut $wrapper {
|
|
||||||
PythonObserverWrapper::MapI8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperI8,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
PythonObserverWrapper::MapI16(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperI16,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI32(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperI32,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI64(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperI64,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperU8,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
PythonObserverWrapper::MapU16(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperU16,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU32(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperU32,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU64(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
Ok(crate::mapob_unwrap_me_mut!(
|
|
||||||
PythonMapObserverWrapperU64,
|
|
||||||
borrowed.wrapper,
|
|
||||||
$name,
|
|
||||||
$body
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonObserver {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_i8(map_observer: Py<PythonMapObserverI8>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapI8(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_i16(map_observer: Py<PythonMapObserverI16>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapI16(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_i32(map_observer: Py<PythonMapObserverI32>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapI32(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_i64(map_observer: Py<PythonMapObserverI64>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapI64(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_u8(map_observer: Py<PythonMapObserverU8>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapU8(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_u16(map_observer: Py<PythonMapObserverU16>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapU16(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_u32(map_observer: Py<PythonMapObserverU32>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapU32(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_map_u64(map_observer: Py<PythonMapObserverU64>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::MapU64(map_observer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_py(py_observer: PyObject) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonObserverWrapper::Python(PyObjectObserver::new(py_observer)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_py(&self) -> Option<PyObject> {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonObserverWrapper::Python(pyo) => Some(pyo.inner.clone()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for PythonObserver {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
let ptr = unwrap_me!(self.wrapper, o, { ptr::from_ref::<str>(o.name()) });
|
|
||||||
unsafe { ptr.as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Observer<PythonStdState> for PythonObserver {
|
|
||||||
fn flush(&mut self) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, o, { Observer::<PythonStdState>::flush(o) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, o, { o.pre_exec(state, input) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, o, { o.post_exec(state, input, exit_kind) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_exec_child(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, o, { o.pre_exec_child(state, input) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec_child(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, o, {
|
|
||||||
o.post_exec_child(state, input, exit_kind)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[pyclass(unsendable, name = "ObserversTuple")]
|
|
||||||
pub struct PythonObserversTuple {
|
|
||||||
list: Vec<PythonObserver>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonObserversTuple {
|
|
||||||
#[new]
|
|
||||||
fn new(list: Vec<PythonObserver>) -> Self {
|
|
||||||
Self { list }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.list.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __getitem__(&self, idx: usize) -> PythonObserver {
|
|
||||||
self.list[idx].clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyo3(name = "match_name")]
|
|
||||||
fn pymatch_name(&self, name: &str) -> Option<PythonObserver> {
|
|
||||||
for ob in &self.list {
|
|
||||||
if *ob.name() == *name {
|
|
||||||
return Some(ob.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObserversTuple<PythonStdState> for PythonObserversTuple {
|
|
||||||
fn pre_exec_all(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for ob in &mut self.list {
|
|
||||||
ob.pre_exec(state, input)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec_all(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for ob in &mut self.list {
|
|
||||||
ob.post_exec(state, input, exit_kind)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_exec_child_all(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for ob in &mut self.list {
|
|
||||||
ob.pre_exec_child(state, input)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec_child_all(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
input: &BytesInput,
|
|
||||||
exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for ob in &mut self.list {
|
|
||||||
ob.post_exec_child(state, input, exit_kind)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: expose stdout/stderr to python
|
|
||||||
#[inline]
|
|
||||||
fn observes_stdout(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observes_stderr(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observe_stderr(&mut self, _: &[u8]) {}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observe_stdout(&mut self, _: &[u8]) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MatchName for PythonObserversTuple {
|
|
||||||
fn match_name<T>(&self, name: &str) -> Option<&T> {
|
|
||||||
unsafe {
|
|
||||||
let mut r = None;
|
|
||||||
for ob in &self.list {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
match &ob.wrapper {
|
|
||||||
PythonObserverWrapper::MapI8(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI8, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI16(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI16, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI32(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI32, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI64(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI64, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PythonObserverWrapper::MapU8(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU8, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU16(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU16, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU32(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU32, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU64(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU64, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
|
|
||||||
.as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::Python(py_wrapper) => {
|
|
||||||
if type_eq::<PyObjectObserver, T>() && py_wrapper.name() == name {
|
|
||||||
r = (ptr::from_ref(py_wrapper) as *const T).as_ref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
|
|
||||||
unsafe {
|
|
||||||
let mut r = None;
|
|
||||||
for ob in &mut self.list {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
match &mut ob.wrapper {
|
|
||||||
PythonObserverWrapper::MapI8(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI8, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI16(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI16, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI32(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI32, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapI64(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverI64, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PythonObserverWrapper::MapU8(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU8, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU16(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU16, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU32(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU32, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
|
|
||||||
as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::MapU64(py_wrapper) => {
|
|
||||||
if type_eq::<PythonMapObserverU64, T>()
|
|
||||||
&& py_wrapper.borrow(py).name() == name
|
|
||||||
{
|
|
||||||
r = (ptr::addr_of!(*(*py_wrapper).borrow_mut(py)) as *mut T)
|
|
||||||
.as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PythonObserverWrapper::Python(py_wrapper) => {
|
|
||||||
if type_eq::<PyObjectObserver, T>() && py_wrapper.name() == name {
|
|
||||||
r = (ptr::from_mut(py_wrapper) as *mut T).as_mut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonObserver>()?;
|
|
||||||
m.add_class::<PythonObserversTuple>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -644,248 +644,6 @@ impl ExecutionCountRestartHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Stage` Python bindings
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
use libafl_bolts::Named;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::HasCurrentCorpusIdx,
|
|
||||||
events::pybind::PythonEventManager,
|
|
||||||
executors::pybind::PythonExecutor,
|
|
||||||
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
|
|
||||||
stages::{
|
|
||||||
mutational::pybind::PythonStdMutationalStage, HasCurrentStage, RetryRestartHelper,
|
|
||||||
Stage, StagesTuple,
|
|
||||||
},
|
|
||||||
state::{
|
|
||||||
pybind::{PythonStdState, PythonStdStateWrapper},
|
|
||||||
UsesState,
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PyObjectStage {
|
|
||||||
inner: PyObject,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PyObjectStage {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(obj: PyObject) -> Self {
|
|
||||||
PyObjectStage { inner: obj }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesState for PyObjectStage {
|
|
||||||
type State = PythonStdState;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for PyObjectStage {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"PyObjectStage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage {
|
|
||||||
#[inline]
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut PythonStdFuzzer,
|
|
||||||
executor: &mut PythonExecutor,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
manager: &mut PythonEventManager,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
|
||||||
return Err(Error::illegal_state(
|
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
self.inner.call_method1(
|
|
||||||
py,
|
|
||||||
"perform",
|
|
||||||
(
|
|
||||||
PythonStdFuzzerWrapper::wrap(fuzzer),
|
|
||||||
executor.clone(),
|
|
||||||
PythonStdStateWrapper::wrap(state),
|
|
||||||
manager.clone(),
|
|
||||||
corpus_idx.0,
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
|
||||||
// we don't support resumption in python, and maybe can't?
|
|
||||||
RetryRestartHelper::restart_progress_should_run(state, self, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
|
||||||
RetryRestartHelper::clear_restart_progress(state, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum PythonStageWrapper {
|
|
||||||
StdMutational(Py<PythonStdMutationalStage>),
|
|
||||||
Python(PyObjectStage),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stage Trait binding
|
|
||||||
#[pyclass(unsendable, name = "Stage")]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PythonStage {
|
|
||||||
wrapper: PythonStageWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
libafl_bolts::unwrap_me_mut_body!($wrapper, $name, $body, PythonStageWrapper,
|
|
||||||
{ StdMutational },
|
|
||||||
{
|
|
||||||
Python(py_wrapper) => {
|
|
||||||
let $name = py_wrapper;
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStage {
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_std_mutational(
|
|
||||||
py_std_havoc_mutations_stage: Py<PythonStdMutationalStage>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonStageWrapper::StdMutational(py_std_havoc_mutations_stage),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_py(obj: PyObject) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonStageWrapper::Python(PyObjectStage::new(obj)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap_py(&self) -> Option<PyObject> {
|
|
||||||
match &self.wrapper {
|
|
||||||
PythonStageWrapper::Python(pyo) => Some(pyo.inner.clone()),
|
|
||||||
PythonStageWrapper::StdMutational(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UsesState for PythonStage {
|
|
||||||
type State = PythonStdState;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for PythonStage {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"PythonStage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage {
|
|
||||||
#[inline]
|
|
||||||
#[allow(clippy::let_and_return)]
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut PythonStdFuzzer,
|
|
||||||
executor: &mut PythonExecutor,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
manager: &mut PythonEventManager,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unwrap_me_mut!(self.wrapper, s, {
|
|
||||||
s.perform_restartable(fuzzer, executor, state, manager)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn restart_progress_should_run(
|
|
||||||
&mut self,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
) -> Result<bool, Error> {
|
|
||||||
// TODO we need to apply MutationalStage-like resumption here.
|
|
||||||
// For now, make sure we don't get stuck crashing on a single test
|
|
||||||
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
|
||||||
RetryRestartHelper::clear_restart_progress(state, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[pyclass(unsendable, name = "StagesTuple")]
|
|
||||||
pub struct PythonStagesTuple {
|
|
||||||
list: Vec<PythonStage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStagesTuple {
|
|
||||||
#[new]
|
|
||||||
fn new(list: Vec<PythonStage>) -> Self {
|
|
||||||
Self { list }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.list.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __getitem__(&self, idx: usize) -> PythonStage {
|
|
||||||
self.list[idx].clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StagesTuple<PythonExecutor, PythonEventManager, PythonStdState, PythonStdFuzzer>
|
|
||||||
for PythonStagesTuple
|
|
||||||
{
|
|
||||||
fn perform_all(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut PythonStdFuzzer,
|
|
||||||
executor: &mut PythonExecutor,
|
|
||||||
state: &mut PythonStdState,
|
|
||||||
manager: &mut PythonEventManager,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for (i, s) in self.list.iter_mut().enumerate() {
|
|
||||||
if let Some(continued) = state.current_stage()? {
|
|
||||||
assert!(continued >= i);
|
|
||||||
if continued > i {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.set_stage(i)?;
|
|
||||||
}
|
|
||||||
s.perform_restartable(fuzzer, executor, state, manager)?;
|
|
||||||
state.clear_stage()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStage>()?;
|
|
||||||
m.add_class::<PythonStagesTuple>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
@ -403,54 +403,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
/// `StdMutationalStage` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
events::pybind::PythonEventManager,
|
|
||||||
executors::pybind::PythonExecutor,
|
|
||||||
fuzzer::pybind::PythonStdFuzzer,
|
|
||||||
inputs::BytesInput,
|
|
||||||
mutators::pybind::PythonMutator,
|
|
||||||
stages::{pybind::PythonStage, StdMutationalStage},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "StdMutationalStage")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Python class for StdMutationalStage
|
|
||||||
pub struct PythonStdMutationalStage {
|
|
||||||
/// Rust wrapped StdMutationalStage object
|
|
||||||
pub inner: StdMutationalStage<
|
|
||||||
PythonExecutor,
|
|
||||||
PythonEventManager,
|
|
||||||
BytesInput,
|
|
||||||
PythonMutator,
|
|
||||||
PythonStdFuzzer,
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStdMutationalStage {
|
|
||||||
#[new]
|
|
||||||
fn new(mutator: PythonMutator) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: StdMutationalStage::new(mutator),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_stage(slf: Py<Self>) -> PythonStage {
|
|
||||||
PythonStage::new_std_mutational(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStdMutationalStage>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1361,146 +1361,3 @@ pub mod test {
|
|||||||
.expect("couldn't instantiate the test state")
|
.expect("couldn't instantiate the test state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
/// `State` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use libafl_bolts::{ownedref::OwnedMutPtr, rands::pybind::PythonRand};
|
|
||||||
use pyo3::{prelude::*, types::PyDict};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
corpus::pybind::PythonCorpus,
|
|
||||||
events::pybind::PythonEventManager,
|
|
||||||
executors::pybind::PythonExecutor,
|
|
||||||
feedbacks::pybind::PythonFeedback,
|
|
||||||
fuzzer::pybind::PythonStdFuzzerWrapper,
|
|
||||||
generators::pybind::PythonGenerator,
|
|
||||||
inputs::BytesInput,
|
|
||||||
pybind::PythonMetadata,
|
|
||||||
state::{
|
|
||||||
HasCorpus, HasExecutions, HasMaxSize, HasMetadata, HasRand, HasSolutions, StdState,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// `StdState` with fixed generics
|
|
||||||
pub type PythonStdState = StdState<BytesInput, PythonCorpus, PythonRand, PythonCorpus>;
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "StdState")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// Python class for StdState
|
|
||||||
pub struct PythonStdStateWrapper {
|
|
||||||
/// Rust wrapped StdState object
|
|
||||||
pub inner: OwnedMutPtr<PythonStdState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PythonStdStateWrapper {
|
|
||||||
pub fn wrap(r: &mut PythonStdState) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedMutPtr::Ptr(r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn unwrap(&self) -> &PythonStdState {
|
|
||||||
self.inner.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwrap_mut(&mut self) -> &mut PythonStdState {
|
|
||||||
self.inner.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStdStateWrapper {
|
|
||||||
#[new]
|
|
||||||
fn new(
|
|
||||||
py_rand: PythonRand,
|
|
||||||
corpus: PythonCorpus,
|
|
||||||
solutions: PythonCorpus,
|
|
||||||
feedback: &mut PythonFeedback,
|
|
||||||
objective: &mut PythonFeedback,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: OwnedMutPtr::Owned(Box::new(
|
|
||||||
StdState::new(py_rand, corpus, solutions, feedback, objective)
|
|
||||||
.expect("Failed to create a new StdState"),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metadata(&mut self) -> PyObject {
|
|
||||||
let meta = self.inner.as_mut().metadata_map_mut();
|
|
||||||
if !meta.contains::<PythonMetadata>() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let dict: Py<PyDict> = PyDict::new(py).into();
|
|
||||||
meta.insert(PythonMetadata::new(dict.to_object(py)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
meta.get::<PythonMetadata>().unwrap().map.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rand(&self) -> PythonRand {
|
|
||||||
self.inner.as_ref().rand().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn corpus(&self) -> PythonCorpus {
|
|
||||||
self.inner.as_ref().corpus().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn solutions(&self) -> PythonCorpus {
|
|
||||||
self.inner.as_ref().solutions().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn executions(&self) -> u64 {
|
|
||||||
*self.inner.as_ref().executions()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn max_size(&self) -> usize {
|
|
||||||
self.inner.as_ref().max_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_initial_inputs(
|
|
||||||
&mut self,
|
|
||||||
py_fuzzer: &mut PythonStdFuzzerWrapper,
|
|
||||||
py_executor: &mut PythonExecutor,
|
|
||||||
py_generator: &mut PythonGenerator,
|
|
||||||
py_mgr: &mut PythonEventManager,
|
|
||||||
num: usize,
|
|
||||||
) {
|
|
||||||
self.inner
|
|
||||||
.as_mut()
|
|
||||||
.generate_initial_inputs(
|
|
||||||
py_fuzzer.unwrap_mut(),
|
|
||||||
py_executor,
|
|
||||||
py_generator,
|
|
||||||
py_mgr,
|
|
||||||
num,
|
|
||||||
)
|
|
||||||
.expect("Failed to generate the initial corpus");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
|
||||||
fn load_initial_inputs(
|
|
||||||
&mut self,
|
|
||||||
py_fuzzer: &mut PythonStdFuzzerWrapper,
|
|
||||||
py_executor: &mut PythonExecutor,
|
|
||||||
py_mgr: &mut PythonEventManager,
|
|
||||||
in_dirs: Vec<PathBuf>,
|
|
||||||
) {
|
|
||||||
self.inner
|
|
||||||
.as_mut()
|
|
||||||
.load_initial_inputs(py_fuzzer.unwrap_mut(), py_executor, py_mgr, &in_dirs)
|
|
||||||
.expect("Failed to load the initial corpus");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStdStateWrapper>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -35,9 +35,6 @@ derive = ["libafl_derive"]
|
|||||||
## If set, libafl_bolt's `rand` implementations will implement `rand::Rng`
|
## If set, libafl_bolt's `rand` implementations will implement `rand::Rng`
|
||||||
rand_trait = ["rand_core"]
|
rand_trait = ["rand_core"]
|
||||||
|
|
||||||
## Will build the `pyo3` bindings
|
|
||||||
python = ["pyo3", "std"]
|
|
||||||
|
|
||||||
## Expose `libafl::prelude` for direct access to all types without additional `use` directives
|
## Expose `libafl::prelude` for direct access to all types without additional `use` directives
|
||||||
prelude = []
|
prelude = []
|
||||||
|
|
||||||
@ -116,8 +113,6 @@ uuid = { version = "1.4", optional = true, features = ["serde", "v4"] }
|
|||||||
clap = {version = "4.5", features = ["derive", "wrap_help"], optional = true} # CLI parsing, for libafl_bolts::cli / the `cli` feature
|
clap = {version = "4.5", features = ["derive", "wrap_help"], optional = true} # CLI parsing, for libafl_bolts::cli / the `cli` feature
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
|
||||||
pyo3 = { version = "0.18", optional = true, features = ["serde", "macros"] }
|
|
||||||
|
|
||||||
# optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable)
|
# optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable)
|
||||||
serial_test = { version = "2", optional = true, default-features = false, features = ["logging"] }
|
serial_test = { version = "2", optional = true, default-features = false, features = ["logging"] }
|
||||||
|
|
||||||
|
@ -611,22 +611,6 @@ impl From<windows::core::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl From<pyo3::PyErr> for Error {
|
|
||||||
fn from(err: pyo3::PyErr) -> Self {
|
|
||||||
pyo3::Python::with_gil(|py| {
|
|
||||||
if err.matches(
|
|
||||||
py,
|
|
||||||
pyo3::types::PyType::new::<pyo3::exceptions::PyKeyboardInterrupt>(py),
|
|
||||||
) {
|
|
||||||
Self::shutting_down()
|
|
||||||
} else {
|
|
||||||
Self::illegal_state(format!("Python exception: {err:?}"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(not(nightly), feature = "std"))]
|
#[cfg(all(not(nightly), feature = "std"))]
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
@ -1038,148 +1022,6 @@ pub unsafe fn set_error_print_panic_hook(new_stderr: RawFd) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub mod pybind {
|
|
||||||
|
|
||||||
use pyo3::{pymodule, types::PyModule, PyResult, Python};
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! unwrap_me_body {
|
|
||||||
($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),* }) => {
|
|
||||||
match &$wrapper {
|
|
||||||
$(
|
|
||||||
$wrapper_type::$wrapper_option(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
let $name = &borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),* }, { $($wrapper_optional:tt($pw:ident) => $code_block:block)* }) => {
|
|
||||||
match &$wrapper {
|
|
||||||
$(
|
|
||||||
$wrapper_type::$wrapper_option(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let borrowed = py_wrapper.borrow(py);
|
|
||||||
let $name = &borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
$($wrapper_type::$wrapper_optional($pw) => { $code_block })*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! unwrap_me_mut_body {
|
|
||||||
($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),*}) => {
|
|
||||||
match &mut $wrapper {
|
|
||||||
$(
|
|
||||||
$wrapper_type::$wrapper_option(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
let $name = &mut borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),*}, { $($wrapper_optional:tt($pw:ident) => $code_block:block)* }) => {
|
|
||||||
match &mut $wrapper {
|
|
||||||
$(
|
|
||||||
$wrapper_type::$wrapper_option(py_wrapper) => {
|
|
||||||
Python::with_gil(|py| -> PyResult<_> {
|
|
||||||
let mut borrowed = py_wrapper.borrow_mut(py);
|
|
||||||
let $name = &mut borrowed.inner;
|
|
||||||
Ok($body)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
$($wrapper_type::$wrapper_optional($pw) => { $code_block })*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! impl_serde_pyobjectwrapper {
|
|
||||||
($struct_name:ident, $inner:tt) => {
|
|
||||||
const _: () = {
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
||||||
|
|
||||||
impl Serialize for $struct_name {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let buf = Python::with_gil(|py| -> PyResult<Vec<u8>> {
|
|
||||||
let pickle = PyModule::import(py, "pickle")?;
|
|
||||||
let buf: Vec<u8> =
|
|
||||||
pickle.getattr("dumps")?.call1((&self.$inner,))?.extract()?;
|
|
||||||
Ok(buf)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
serializer.serialize_bytes(&buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PyObjectVisitor;
|
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for PyObjectVisitor {
|
|
||||||
type Value = $struct_name;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter
|
|
||||||
.write_str("Expecting some bytes to deserialize from the Python side")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
let obj = Python::with_gil(|py| -> PyResult<PyObject> {
|
|
||||||
let pickle = PyModule::import(py, "pickle")?;
|
|
||||||
let obj = pickle.getattr("loads")?.call1((v,))?.to_object(py);
|
|
||||||
Ok(obj)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
Ok($struct_name::new(obj))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for $struct_name {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
deserializer.deserialize_byte_buf(PyObjectVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymodule]
|
|
||||||
#[pyo3(name = "libafl_bolts")]
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
crate::rands::pybind::register(py, m)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -433,92 +433,3 @@ mod tests {
|
|||||||
log::info!("random value: {}", mutator.rng.next_u32());
|
log::info!("random value: {}", mutator.rng.next_u32());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[allow(clippy::unnecessary_fallible_conversions, unused_qualifications)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
/// `Rand` Python bindings
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use super::Rand;
|
|
||||||
use crate::{current_nanos, rands::StdRand};
|
|
||||||
|
|
||||||
#[pyclass(unsendable, name = "StdRand")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
/// Python class for StdRand
|
|
||||||
pub struct PythonStdRand {
|
|
||||||
/// Rust wrapped StdRand object
|
|
||||||
pub inner: StdRand,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonStdRand {
|
|
||||||
#[staticmethod]
|
|
||||||
fn with_current_nanos() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: StdRand::with_seed(current_nanos()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[staticmethod]
|
|
||||||
fn with_seed(seed: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: StdRand::with_seed(seed),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_rand(slf: Py<Self>) -> PythonRand {
|
|
||||||
PythonRand::new_std(slf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
enum PythonRandWrapper {
|
|
||||||
Std(Py<PythonStdRand>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rand Trait binding
|
|
||||||
#[pyclass(unsendable, name = "Rand")]
|
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
pub struct PythonRand {
|
|
||||||
wrapper: PythonRandWrapper,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap_me_mut {
|
|
||||||
($wrapper:expr, $name:ident, $body:block) => {
|
|
||||||
crate::unwrap_me_mut_body!($wrapper, $name, $body, PythonRandWrapper, { Std })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl PythonRand {
|
|
||||||
#[staticmethod]
|
|
||||||
fn new_std(py_std_rand: Py<PythonStdRand>) -> Self {
|
|
||||||
Self {
|
|
||||||
wrapper: PythonRandWrapper::Std(py_std_rand),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rand for PythonRand {
|
|
||||||
fn set_seed(&mut self, seed: u64) {
|
|
||||||
unwrap_me_mut!(self.wrapper, r, { r.set_seed(seed) });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> u64 {
|
|
||||||
unwrap_me_mut!(self.wrapper, r, { r.next() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the classes to the python module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<PythonStdRand>()?;
|
|
||||||
m.add_class::<PythonRand>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,7 +12,7 @@ edition = "2021"
|
|||||||
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["document-features", "default", "python", "x86_64", "usermode"]
|
features = ["document-features", "default", "x86_64", "usermode"]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -24,8 +24,6 @@ document-features = ["dep:document-features"]
|
|||||||
#! ### General Features
|
#! ### General Features
|
||||||
## Find injections during fuzzing
|
## Find injections during fuzzing
|
||||||
injections = ["serde_yaml", "toml"]
|
injections = ["serde_yaml", "toml"]
|
||||||
## Python bindings support
|
|
||||||
python = ["pyo3", "pyo3-build-config", "libafl_qemu_sys/python"]
|
|
||||||
## Fork support
|
## Fork support
|
||||||
fork = ["libafl/fork"]
|
fork = ["libafl/fork"]
|
||||||
## Build libqasan for address sanitization
|
## Build libqasan for address sanitization
|
||||||
@ -87,12 +85,10 @@ paste = "1"
|
|||||||
enum-map = "2.7"
|
enum-map = "2.7"
|
||||||
serde_yaml = { version = "0.8", optional = true } # For parsing the injections yaml file
|
serde_yaml = { version = "0.8", optional = true } # For parsing the injections yaml file
|
||||||
toml = { version = "0.4.2", optional = true } # For parsing the injections toml file
|
toml = { version = "0.4.2", optional = true } # For parsing the injections toml file
|
||||||
pyo3 = { version = "0.18", optional = true }
|
|
||||||
# Document all features of this crate (for `cargo doc`)
|
# Document all features of this crate (for `cargo doc`)
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { version = "0.18", optional = true }
|
|
||||||
rustversion = "1.0"
|
rustversion = "1.0"
|
||||||
bindgen = "0.69"
|
bindgen = "0.69"
|
||||||
|
|
||||||
|
@ -30,8 +30,6 @@ be = []
|
|||||||
usermode = []
|
usermode = []
|
||||||
systemmode = []
|
systemmode = []
|
||||||
|
|
||||||
python = ["pyo3", "pyo3-build-config"]
|
|
||||||
|
|
||||||
slirp = [ "systemmode", "libafl_qemu_build/slirp" ] # build qemu with host libslirp (for user networking)
|
slirp = [ "systemmode", "libafl_qemu_build/slirp" ] # build qemu with host libslirp (for user networking)
|
||||||
shared = [ "libafl_qemu_build/shared" ]
|
shared = [ "libafl_qemu_build/shared" ]
|
||||||
|
|
||||||
@ -43,8 +41,6 @@ num_enum = "0.7"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
strum = "0.25"
|
strum = "0.25"
|
||||||
strum_macros = "0.25"
|
strum_macros = "0.25"
|
||||||
pyo3 = { version = "0.18", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
libafl_qemu_build = { path = "../libafl_qemu_build", version = "0.11.2" }
|
libafl_qemu_build = { path = "../libafl_qemu_build", version = "0.11.2" }
|
||||||
pyo3-build-config = { version = "0.18", optional = true }
|
|
||||||
|
@ -98,8 +98,6 @@ macro_rules! extern_c_checked {
|
|||||||
use core::ops::BitAnd;
|
use core::ops::BitAnd;
|
||||||
use std::{ffi::c_void, slice::from_raw_parts, str::from_utf8_unchecked};
|
use std::{ffi::c_void, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python};
|
|
||||||
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
||||||
pub use x86_64_stub_bindings::*;
|
pub use x86_64_stub_bindings::*;
|
||||||
|
|
||||||
@ -117,7 +115,6 @@ pub type GuestVirtAddr = crate::vaddr;
|
|||||||
pub type GuestHwAddrInfo = crate::qemu_plugin_hwaddr;
|
pub type GuestHwAddrInfo = crate::qemu_plugin_hwaddr;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
|
||||||
pub struct MapInfo {
|
pub struct MapInfo {
|
||||||
start: GuestAddr,
|
start: GuestAddr,
|
||||||
end: GuestAddr,
|
end: GuestAddr,
|
||||||
@ -204,7 +201,6 @@ extern_c_checked! {
|
|||||||
pub fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
pub fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "python", pymethods)]
|
|
||||||
impl MapInfo {
|
impl MapInfo {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn start(&self) -> GuestAddr {
|
pub fn start(&self) -> GuestAddr {
|
||||||
@ -280,11 +276,3 @@ impl MmapPerms {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for MmapPerms {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,8 +3,6 @@ use std::sync::OnceLock;
|
|||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::aarch64::*;
|
pub use syscall_numbers::aarch64::*;
|
||||||
|
|
||||||
@ -73,14 +71,6 @@ impl Regs {
|
|||||||
pub const Lr: Regs = Regs::X30;
|
pub const Lr: Regs = Regs::X30;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for Regs {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an ARM64 ArchCapstoneBuilder
|
/// Return an ARM64 ArchCapstoneBuilder
|
||||||
pub fn capstone() -> capstone::arch::arm64::ArchCapstoneBuilder {
|
pub fn capstone() -> capstone::arch::arm64::ArchCapstoneBuilder {
|
||||||
capstone::Capstone::new()
|
capstone::Capstone::new()
|
||||||
|
@ -3,8 +3,6 @@ use std::sync::OnceLock;
|
|||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::arm::*;
|
pub use syscall_numbers::arm::*;
|
||||||
|
|
||||||
@ -63,14 +61,6 @@ impl Regs {
|
|||||||
pub const Cpsr: Regs = Regs::R25;
|
pub const Cpsr: Regs = Regs::R25;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for Regs {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an ARM ArchCapstoneBuilder
|
/// Return an ARM ArchCapstoneBuilder
|
||||||
pub fn capstone() -> capstone::arch::arm::ArchCapstoneBuilder {
|
pub fn capstone() -> capstone::arch::arm::ArchCapstoneBuilder {
|
||||||
capstone::Capstone::new()
|
capstone::Capstone::new()
|
||||||
|
@ -352,9 +352,6 @@ impl From<libafl_qemu_sys::MemOpIdx> for MemAccessInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
||||||
|
|
||||||
pub use libafl_qemu_sys::{CPUArchState, CPUState};
|
pub use libafl_qemu_sys::{CPUArchState, CPUState};
|
||||||
@ -363,33 +360,11 @@ use crate::sync_backdoor::{SyncBackdoor, SyncBackdoorError};
|
|||||||
|
|
||||||
// syshook_ret
|
// syshook_ret
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[cfg_attr(feature = "python", pyclass)]
|
|
||||||
#[cfg_attr(feature = "python", derive(FromPyObject))]
|
|
||||||
pub struct SyscallHookResult {
|
pub struct SyscallHookResult {
|
||||||
pub retval: GuestAddr,
|
pub retval: GuestAddr,
|
||||||
pub skip_syscall: bool,
|
pub skip_syscall: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[pymethods]
|
|
||||||
impl SyscallHookResult {
|
|
||||||
#[new]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(value: Option<GuestAddr>) -> Self {
|
|
||||||
value.map_or(
|
|
||||||
Self {
|
|
||||||
retval: 0,
|
|
||||||
skip_syscall: false,
|
|
||||||
},
|
|
||||||
|v| Self {
|
|
||||||
retval: v,
|
|
||||||
skip_syscall: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "python"))]
|
|
||||||
impl SyscallHookResult {
|
impl SyscallHookResult {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(value: Option<GuestAddr>) -> Self {
|
pub fn new(value: Option<GuestAddr>) -> Self {
|
||||||
@ -1738,190 +1713,3 @@ where
|
|||||||
// self.qemu.write_function_argument(conv, idx, val)
|
// self.qemu.write_function_argument(conv, idx, val)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
pub mod pybind {
|
|
||||||
use pyo3::{exceptions::PyValueError, prelude::*, types::PyInt};
|
|
||||||
|
|
||||||
use super::{GuestAddr, GuestUsize, MmapPerms, SyscallHookResult};
|
|
||||||
|
|
||||||
static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
|
|
||||||
static mut PY_GENERIC_HOOKS: Vec<(GuestAddr, PyObject)> = vec![];
|
|
||||||
|
|
||||||
extern "C" fn py_syscall_hook_wrapper(
|
|
||||||
_data: u64,
|
|
||||||
sys_num: i32,
|
|
||||||
a0: u64,
|
|
||||||
a1: u64,
|
|
||||||
a2: u64,
|
|
||||||
a3: u64,
|
|
||||||
a4: u64,
|
|
||||||
a5: u64,
|
|
||||||
a6: u64,
|
|
||||||
a7: u64,
|
|
||||||
) -> SyscallHookResult {
|
|
||||||
unsafe { PY_SYSCALL_HOOK.as_ref() }.map_or_else(
|
|
||||||
|| SyscallHookResult::new(None),
|
|
||||||
|obj| {
|
|
||||||
let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7);
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let ret = obj.call1(py, args).expect("Error in the syscall hook");
|
|
||||||
let any = ret.as_ref(py);
|
|
||||||
if any.is_none() {
|
|
||||||
SyscallHookResult::new(None)
|
|
||||||
} else {
|
|
||||||
let a: Result<&PyInt, _> = any.downcast();
|
|
||||||
if let Ok(i) = a {
|
|
||||||
SyscallHookResult::new(Some(
|
|
||||||
i.extract().expect("Invalid syscall hook return value"),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
SyscallHookResult::extract(any)
|
|
||||||
.expect("The syscall hook must return a SyscallHookResult")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn py_generic_hook_wrapper(idx: u64, _pc: GuestAddr) {
|
|
||||||
let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 };
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
obj.call0(py).expect("Error in the hook");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass(unsendable)]
|
|
||||||
pub struct Qemu {
|
|
||||||
pub qemu: super::Qemu,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl Qemu {
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
|
||||||
#[new]
|
|
||||||
fn new(args: Vec<String>, env: Vec<(String, String)>) -> PyResult<Qemu> {
|
|
||||||
let qemu = super::Qemu::init(&args, &env)
|
|
||||||
.map_err(|e| PyValueError::new_err(format!("{e}")))?;
|
|
||||||
|
|
||||||
Ok(Qemu { qemu })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
|
||||||
unsafe {
|
|
||||||
self.qemu.write_mem(addr, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_mem(&self, addr: GuestAddr, size: usize) -> Vec<u8> {
|
|
||||||
let mut buf = vec![0; size];
|
|
||||||
unsafe {
|
|
||||||
self.qemu.read_mem(addr, &mut buf);
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn num_regs(&self) -> i32 {
|
|
||||||
self.qemu.num_regs()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_reg(&self, reg: i32, val: GuestUsize) -> PyResult<()> {
|
|
||||||
self.qemu.write_reg(reg, val).map_err(PyValueError::new_err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_reg(&self, reg: i32) -> PyResult<GuestUsize> {
|
|
||||||
self.qemu.read_reg(reg).map_err(PyValueError::new_err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_breakpoint(&self, addr: GuestAddr) {
|
|
||||||
self.qemu.set_breakpoint(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn entry_break(&self, addr: GuestAddr) {
|
|
||||||
self.qemu.entry_break(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_breakpoint(&self, addr: GuestAddr) {
|
|
||||||
self.qemu.remove_breakpoint(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn g2h(&self, addr: GuestAddr) -> u64 {
|
|
||||||
self.qemu.g2h::<*const u8>(addr) as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn h2g(&self, addr: u64) -> GuestAddr {
|
|
||||||
self.qemu.h2g(addr as *const u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn binary_path(&self) -> String {
|
|
||||||
self.qemu.binary_path().to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_addr(&self) -> GuestAddr {
|
|
||||||
self.qemu.load_addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_jit(&self) {
|
|
||||||
self.qemu.flush_jit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_private(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<GuestAddr> {
|
|
||||||
if let Ok(p) = MmapPerms::try_from(perms) {
|
|
||||||
self.qemu
|
|
||||||
.map_private(addr, size, p)
|
|
||||||
.map_err(PyValueError::new_err)
|
|
||||||
} else {
|
|
||||||
Err(PyValueError::new_err("Invalid perms"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_fixed(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<GuestAddr> {
|
|
||||||
if let Ok(p) = MmapPerms::try_from(perms) {
|
|
||||||
self.qemu
|
|
||||||
.map_fixed(addr, size, p)
|
|
||||||
.map_err(PyValueError::new_err)
|
|
||||||
} else {
|
|
||||||
Err(PyValueError::new_err("Invalid perms"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mprotect(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<()> {
|
|
||||||
if let Ok(p) = MmapPerms::try_from(perms) {
|
|
||||||
self.qemu
|
|
||||||
.mprotect(addr, size, p)
|
|
||||||
.map_err(PyValueError::new_err)
|
|
||||||
} else {
|
|
||||||
Err(PyValueError::new_err("Invalid perms"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unmap(&self, addr: GuestAddr, size: usize) -> PyResult<()> {
|
|
||||||
self.qemu.unmap(addr, size).map_err(PyValueError::new_err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_syscall_hook(&self, hook: PyObject) {
|
|
||||||
unsafe {
|
|
||||||
PY_SYSCALL_HOOK = Some(hook);
|
|
||||||
}
|
|
||||||
self.qemu
|
|
||||||
.add_pre_syscall_hook(0u64, py_syscall_hook_wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_hook(&self, addr: GuestAddr, hook: PyObject) {
|
|
||||||
unsafe {
|
|
||||||
let idx = PY_GENERIC_HOOKS.len();
|
|
||||||
PY_GENERIC_HOOKS.push((addr, hook));
|
|
||||||
self.qemu
|
|
||||||
.set_hook(idx as u64, addr, py_generic_hook_wrapper, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_hooks_at(&self, addr: GuestAddr) -> usize {
|
|
||||||
unsafe {
|
|
||||||
PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
|
|
||||||
}
|
|
||||||
self.qemu.remove_hooks_at(addr, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,8 +7,6 @@ use libafl_qemu_sys::{
|
|||||||
read_self_maps, strlen, GuestAddr, GuestUsize, MapInfo, MmapPerms, VerifyAccess,
|
read_self_maps, strlen, GuestAddr, GuestUsize, MapInfo, MmapPerms, VerifyAccess,
|
||||||
};
|
};
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{HasExecutions, State},
|
emu::{HasExecutions, State},
|
||||||
@ -24,7 +22,6 @@ pub enum HandlerError {
|
|||||||
MultipleInputDefinition,
|
MultipleInputDefinition,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
|
||||||
pub struct GuestMaps {
|
pub struct GuestMaps {
|
||||||
orig_c_iter: *const c_void,
|
orig_c_iter: *const c_void,
|
||||||
c_iter: *const c_void,
|
c_iter: *const c_void,
|
||||||
@ -64,17 +61,6 @@ impl Iterator for GuestMaps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[pymethods]
|
|
||||||
impl GuestMaps {
|
|
||||||
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
|
||||||
slf
|
|
||||||
}
|
|
||||||
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
|
|
||||||
Python::with_gil(|py| slf.next().map(|x| x.into_py(py)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for GuestMaps {
|
impl Drop for GuestMaps {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -2,8 +2,6 @@ use std::sync::OnceLock;
|
|||||||
|
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
|
|
||||||
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
@ -3,8 +3,6 @@ use std::{mem::size_of, sync::OnceLock};
|
|||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86::*;
|
pub use syscall_numbers::x86::*;
|
||||||
|
|
||||||
@ -49,14 +47,6 @@ impl Regs {
|
|||||||
pub const Pc: Regs = Regs::Eip;
|
pub const Pc: Regs = Regs::Eip;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for Regs {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an X86 ArchCapstoneBuilder
|
/// Return an X86 ArchCapstoneBuilder
|
||||||
pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
|
pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
|
||||||
capstone::Capstone::new()
|
capstone::Capstone::new()
|
||||||
|
@ -131,33 +131,3 @@ pub fn filter_qemu_args() -> Vec<String> {
|
|||||||
}
|
}
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[pymodule]
|
|
||||||
#[pyo3(name = "libafl_qemu")]
|
|
||||||
#[allow(clippy::items_after_statements, clippy::too_many_lines)]
|
|
||||||
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
let regsm = PyModule::new(py, "regs")?;
|
|
||||||
for r in Regs::iter() {
|
|
||||||
let v: i32 = r.into();
|
|
||||||
regsm.add(&format!("{r:?}"), v)?;
|
|
||||||
}
|
|
||||||
m.add_submodule(regsm)?;
|
|
||||||
|
|
||||||
let mmapm = PyModule::new(py, "mmap")?;
|
|
||||||
for r in emu::MmapPerms::iter() {
|
|
||||||
let v: i32 = r.into();
|
|
||||||
mmapm.add(&format!("{r:?}"), v)?;
|
|
||||||
}
|
|
||||||
m.add_submodule(mmapm)?;
|
|
||||||
|
|
||||||
m.add_class::<emu::MapInfo>()?;
|
|
||||||
m.add_class::<emu::GuestMaps>()?;
|
|
||||||
m.add_class::<emu::SyscallHookResult>()?;
|
|
||||||
m.add_class::<emu::pybind::Qemu>()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
@ -2,8 +2,6 @@ use std::sync::OnceLock;
|
|||||||
|
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::mips::*;
|
pub use syscall_numbers::mips::*;
|
||||||
|
|
||||||
@ -72,14 +70,6 @@ impl Regs {
|
|||||||
pub const Zero: Regs = Regs::R0;
|
pub const Zero: Regs = Regs::R0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for Regs {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an MIPS ArchCapstoneBuilder
|
/// Return an MIPS ArchCapstoneBuilder
|
||||||
pub fn capstone() -> capstone::arch::mips::ArchCapstoneBuilder {
|
pub fn capstone() -> capstone::arch::mips::ArchCapstoneBuilder {
|
||||||
capstone::Capstone::new().mips()
|
capstone::Capstone::new().mips()
|
||||||
|
@ -2,8 +2,6 @@ use std::sync::OnceLock;
|
|||||||
|
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::powerpc::*;
|
pub use syscall_numbers::powerpc::*;
|
||||||
|
|
||||||
@ -112,14 +110,6 @@ impl Regs {
|
|||||||
pub const Sp: Regs = Regs::R1;
|
pub const Sp: Regs = Regs::R1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for Regs {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an MIPS ArchCapstoneBuilder
|
/// Return an MIPS ArchCapstoneBuilder
|
||||||
pub fn capstone() -> capstone::arch::ppc::ArchCapstoneBuilder {
|
pub fn capstone() -> capstone::arch::ppc::ArchCapstoneBuilder {
|
||||||
capstone::Capstone::new().ppc()
|
capstone::Capstone::new().ppc()
|
||||||
|
@ -3,8 +3,6 @@ use std::{mem::size_of, sync::OnceLock};
|
|||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86_64::*;
|
pub use syscall_numbers::x86_64::*;
|
||||||
|
|
||||||
@ -57,14 +55,6 @@ impl Regs {
|
|||||||
pub const Pc: Regs = Regs::Rip;
|
pub const Pc: Regs = Regs::Rip;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
impl IntoPy<PyObject> for Regs {
|
|
||||||
fn into_py(self, py: Python) -> PyObject {
|
|
||||||
let n: i32 = self.into();
|
|
||||||
n.into_py(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an X86 `ArchCapstoneBuilder`
|
/// Return an X86 `ArchCapstoneBuilder`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
|
pub fn capstone() -> capstone::arch::x86::ArchCapstoneBuilder {
|
||||||
|
@ -9,14 +9,12 @@ readme = "../README.md"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["fuzzing"]
|
keywords = ["fuzzing"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
|
||||||
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
python = ["pyo3", "libafl_qemu/python", "pyo3-build-config"]
|
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
# for libafl_qemu
|
# for libafl_qemu
|
||||||
@ -29,16 +27,12 @@ mips = ["libafl_qemu/mips"] # build qemu for mips (el, use with the 'be' feature
|
|||||||
ppc = ["libafl_qemu/ppc"] # build qemu for powerpc
|
ppc = ["libafl_qemu/ppc"] # build qemu for powerpc
|
||||||
hexagon = ["libafl_qemu/hexagon"] # build qemu for hexagon
|
hexagon = ["libafl_qemu/hexagon"] # build qemu for hexagon
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
pyo3-build-config = { version = "0.18", optional = true }
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../libafl", version = "0.11.2" }
|
libafl = { path = "../libafl", version = "0.11.2" }
|
||||||
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2" }
|
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2" }
|
||||||
libafl_targets = { path = "../libafl_targets", version = "0.11.2" }
|
libafl_targets = { path = "../libafl_targets", version = "0.11.2" }
|
||||||
|
|
||||||
typed-builder = "0.16" # Implement the builder pattern at compiletime
|
typed-builder = "0.16" # Implement the builder pattern at compiletime
|
||||||
pyo3 = { version = "0.18", optional = true }
|
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
pyo3_build_config::add_extension_module_link_args();
|
|
||||||
}
|
|
@ -299,80 +299,3 @@ impl<'a> ForkserverBytesCoverageSugar<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The python bindings for this sugar
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
pub mod pybind {
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::Cores;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use crate::forkserver;
|
|
||||||
|
|
||||||
/// Python bindings for the `LibAFL` forkserver sugar
|
|
||||||
#[pyclass(unsendable)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct ForkserverBytesCoverageSugar {
|
|
||||||
input_dirs: Vec<PathBuf>,
|
|
||||||
output_dir: PathBuf,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: Cores,
|
|
||||||
use_cmplog: Option<bool>,
|
|
||||||
iterations: Option<u64>,
|
|
||||||
tokens_file: Option<PathBuf>,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl ForkserverBytesCoverageSugar {
|
|
||||||
/// Create a new [`ForkserverBytesCoverageSugar`]
|
|
||||||
#[new]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn new(
|
|
||||||
input_dirs: Vec<PathBuf>,
|
|
||||||
output_dir: PathBuf,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: Vec<usize>,
|
|
||||||
use_cmplog: Option<bool>,
|
|
||||||
iterations: Option<u64>,
|
|
||||||
tokens_file: Option<PathBuf>,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
input_dirs,
|
|
||||||
output_dir,
|
|
||||||
broker_port,
|
|
||||||
cores: cores.into(),
|
|
||||||
use_cmplog,
|
|
||||||
iterations,
|
|
||||||
tokens_file,
|
|
||||||
timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the fuzzer
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
|
||||||
pub fn run(&self, program: String, arguments: Vec<String>) {
|
|
||||||
forkserver::ForkserverBytesCoverageSugar::builder()
|
|
||||||
.input_dirs(&self.input_dirs)
|
|
||||||
.output_dir(self.output_dir.clone())
|
|
||||||
.broker_port(self.broker_port)
|
|
||||||
.cores(&self.cores)
|
|
||||||
.program(program)
|
|
||||||
.arguments(&arguments)
|
|
||||||
.use_cmplog(self.use_cmplog)
|
|
||||||
.timeout(self.timeout)
|
|
||||||
.tokens_file(self.tokens_file.clone())
|
|
||||||
.iterations(self.iterations)
|
|
||||||
.build()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<ForkserverBytesCoverageSugar>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -348,87 +348,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Python bindings for this sugar
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
pub mod pybind {
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::Cores;
|
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
|
||||||
|
|
||||||
use crate::inmemory;
|
|
||||||
|
|
||||||
/// In-Memory fuzzing made easy.
|
|
||||||
/// Use this sugar for scaling `libfuzzer`-style fuzzers.
|
|
||||||
#[pyclass(unsendable)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct InMemoryBytesCoverageSugar {
|
|
||||||
input_dirs: Vec<PathBuf>,
|
|
||||||
output_dir: PathBuf,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: Cores,
|
|
||||||
use_cmplog: Option<bool>,
|
|
||||||
iterations: Option<u64>,
|
|
||||||
tokens_file: Option<PathBuf>,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl InMemoryBytesCoverageSugar {
|
|
||||||
/// Create a new [`InMemoryBytesCoverageSugar`]
|
|
||||||
#[new]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn new(
|
|
||||||
input_dirs: Vec<PathBuf>,
|
|
||||||
output_dir: PathBuf,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: Vec<usize>,
|
|
||||||
use_cmplog: Option<bool>,
|
|
||||||
iterations: Option<u64>,
|
|
||||||
tokens_file: Option<PathBuf>,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
input_dirs,
|
|
||||||
output_dir,
|
|
||||||
broker_port,
|
|
||||||
cores: cores.into(),
|
|
||||||
use_cmplog,
|
|
||||||
iterations,
|
|
||||||
tokens_file,
|
|
||||||
timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the fuzzer
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
|
||||||
pub fn run(&self, harness: PyObject) {
|
|
||||||
inmemory::InMemoryBytesCoverageSugar::builder()
|
|
||||||
.input_dirs(&self.input_dirs)
|
|
||||||
.output_dir(self.output_dir.clone())
|
|
||||||
.broker_port(self.broker_port)
|
|
||||||
.cores(&self.cores)
|
|
||||||
.harness(|buf| {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
let args = (PyBytes::new(py, buf),); // TODO avoid copy
|
|
||||||
harness.call1(py, args)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
.use_cmplog(self.use_cmplog)
|
|
||||||
.timeout(self.timeout)
|
|
||||||
.tokens_file(self.tokens_file.clone())
|
|
||||||
.iterations(self.iterations)
|
|
||||||
.build()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the module
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<InMemoryBytesCoverageSugar>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -78,23 +78,3 @@ pub const DEFAULT_TIMEOUT_SECS: u64 = 1200;
|
|||||||
/// Default cache size for the corpus in memory.
|
/// Default cache size for the corpus in memory.
|
||||||
/// Anything else will be on disk.
|
/// Anything else will be on disk.
|
||||||
pub const CORPUS_CACHE_SIZE: usize = 4096;
|
pub const CORPUS_CACHE_SIZE: usize = 4096;
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
/// The sugar python module
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
#[pymodule]
|
|
||||||
#[pyo3(name = "libafl_sugar")]
|
|
||||||
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
inmemory::pybind::register(py, m)?;
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
qemu::pybind::register(py, m)?;
|
|
||||||
}
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
forkserver::pybind::register(py, m)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
@ -436,86 +436,3 @@ where
|
|||||||
launcher.build().launch().expect("Launcher failed");
|
launcher.build().launch().expect("Launcher failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// python bindings for this sugar
|
|
||||||
#[cfg(feature = "python")]
|
|
||||||
pub mod pybind {
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::Cores;
|
|
||||||
use libafl_qemu::emu::pybind::Qemu;
|
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
|
||||||
|
|
||||||
use crate::qemu;
|
|
||||||
|
|
||||||
#[pyclass(unsendable)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct QemuBytesCoverageSugar {
|
|
||||||
input_dirs: Vec<PathBuf>,
|
|
||||||
output_dir: PathBuf,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: Cores,
|
|
||||||
use_cmplog: Option<bool>,
|
|
||||||
iterations: Option<u64>,
|
|
||||||
tokens_file: Option<PathBuf>,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl QemuBytesCoverageSugar {
|
|
||||||
/// Create a new [`QemuBytesCoverageSugar`]
|
|
||||||
#[new]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn new(
|
|
||||||
input_dirs: Vec<PathBuf>,
|
|
||||||
output_dir: PathBuf,
|
|
||||||
broker_port: u16,
|
|
||||||
cores: Vec<usize>,
|
|
||||||
use_cmplog: Option<bool>,
|
|
||||||
iterations: Option<u64>,
|
|
||||||
tokens_file: Option<PathBuf>,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
input_dirs,
|
|
||||||
output_dir,
|
|
||||||
broker_port,
|
|
||||||
cores: cores.into(),
|
|
||||||
use_cmplog,
|
|
||||||
iterations,
|
|
||||||
tokens_file,
|
|
||||||
timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the fuzzer
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
|
||||||
pub fn run(&self, qemu: &Qemu, harness: PyObject) {
|
|
||||||
qemu::QemuBytesCoverageSugar::builder()
|
|
||||||
.input_dirs(&self.input_dirs)
|
|
||||||
.output_dir(self.output_dir.clone())
|
|
||||||
.broker_port(self.broker_port)
|
|
||||||
.cores(&self.cores)
|
|
||||||
.harness(|buf| {
|
|
||||||
Python::with_gil(|py| -> PyResult<()> {
|
|
||||||
let args = (PyBytes::new(py, buf),); // TODO avoid copy
|
|
||||||
harness.call1(py, args)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
.use_cmplog(self.use_cmplog)
|
|
||||||
.timeout(self.timeout)
|
|
||||||
.tokens_file(self.tokens_file.clone())
|
|
||||||
.iterations(self.iterations)
|
|
||||||
.build()
|
|
||||||
.run(&qemu.qemu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register this class
|
|
||||||
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
m.add_class::<QemuBytesCoverageSugar>()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user