create libafl_targets and start new structure for libfuzzer_libpng
This commit is contained in:
parent
414a66382b
commit
1c8cdc76a8
@ -9,11 +9,15 @@ members = [
|
|||||||
"libafl",
|
"libafl",
|
||||||
"libafl_derive",
|
"libafl_derive",
|
||||||
"libafl_cc",
|
"libafl_cc",
|
||||||
|
"libafl_targets",
|
||||||
|
|
||||||
#example fuzzers
|
#example fuzzers
|
||||||
"fuzzers/libfuzzer_libpng",
|
#"fuzzers/libfuzzer_libpng",
|
||||||
"fuzzers/frida_libpng",
|
"fuzzers/frida_libpng",
|
||||||
"fuzzers/libfuzzer_libmozjpeg",
|
"fuzzers/libfuzzer_libmozjpeg",
|
||||||
"fuzzers/libfuzzer_libpng_cmpalloc",
|
"fuzzers/libfuzzer_libpng_cmpalloc",
|
||||||
"fuzzers/libfuzzer_windows",
|
"fuzzers/libfuzzer_windows",
|
||||||
]
|
]
|
||||||
|
exclude = [
|
||||||
|
"fuzzers/libfuzzer_libpng",
|
||||||
|
]
|
||||||
|
@ -3,7 +3,6 @@ name = "libfuzzer_libpng"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -11,21 +10,21 @@ build = "build.rs"
|
|||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = []
|
std = []
|
||||||
|
|
||||||
#[profile.release]
|
[profile.release]
|
||||||
#lto = true
|
lto = true
|
||||||
#codegen-units = 1
|
codegen-units = 1
|
||||||
#opt-level = 3
|
opt-level = 3
|
||||||
#debug = true
|
debug = true
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
|
||||||
num_cpus = "1.0"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
|
# TODO Include it only when building cc
|
||||||
|
libafl_cc = { path = "../../libafl_cc/" }
|
||||||
|
|
||||||
[[example]]
|
[lib]
|
||||||
name = "libfuzzer_libpng"
|
name = "libfuzzer_libpng"
|
||||||
path = "./src/fuzzer.rs"
|
path = "./src/fuzzer.rs"
|
||||||
test = false
|
test = false
|
||||||
bench = false
|
bench = false
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
13
fuzzers/libfuzzer_libpng/src/bin/cc.rs
Normal file
13
fuzzers/libfuzzer_libpng/src/bin/cc.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
ClangWrapper::new("clang", "clang++")
|
||||||
|
.from_args(&args)
|
||||||
|
.unwrap()
|
||||||
|
.add_arg("-fsanitize=trace-pc-guard".into())
|
||||||
|
.unwrap()
|
||||||
|
.run()
|
||||||
|
.unwrap();
|
||||||
|
}
|
1
fuzzers/libfuzzer_libpng_old/.gitignore
vendored
Normal file
1
fuzzers/libfuzzer_libpng_old/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
libpng-*
|
31
fuzzers/libfuzzer_libpng_old/Cargo.toml
Normal file
31
fuzzers/libfuzzer_libpng_old/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "libfuzzer_libpng"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
|
|
||||||
|
#[profile.release]
|
||||||
|
#lto = true
|
||||||
|
#codegen-units = 1
|
||||||
|
#opt-level = 3
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
num_cpus = "1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "libfuzzer_libpng"
|
||||||
|
path = "./src/fuzzer.rs"
|
||||||
|
test = false
|
||||||
|
bench = false
|
25
fuzzers/libfuzzer_libpng_old/README.md
Normal file
25
fuzzers/libfuzzer_libpng_old/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Libfuzzer for libpng
|
||||||
|
|
||||||
|
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
|
||||||
|
To show off crash detection, we added a ud2 instruction to the harness, edit harness.cc if you want a non-crashing example.
|
||||||
|
It has been tested on Linux.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build this example, run `cargo build --example libfuzzer_libpng --release`.
|
||||||
|
This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web.
|
||||||
|
Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`.
|
||||||
|
Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel.
|
||||||
|
|
||||||
|
Each following execution will run a fuzzer client.
|
||||||
|
As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`).
|
||||||
|
This means each client will start itself again to listen for crashes and timeouts.
|
||||||
|
By restarting the actual fuzzer, it can recover from these exit conditions.
|
||||||
|
|
||||||
|
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
|
||||||
|
|
||||||
|
For convenience, you may just run `./test.sh` in this folder to test it.
|
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png
Normal file
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
179
fuzzers/libfuzzer_libpng_old/src/fuzzer.rs
Normal file
179
fuzzers/libfuzzer_libpng_old/src/fuzzer.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||||
|
//! The example harness is built for libpng.
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::{env, path::PathBuf};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use libafl::{
|
||||||
|
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||||
|
corpus::{
|
||||||
|
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||||
|
QueueCorpusScheduler,
|
||||||
|
},
|
||||||
|
events::setup_restarting_mgr,
|
||||||
|
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
|
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||||
|
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||||
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
|
stages::mutational::StdMutationalStage,
|
||||||
|
state::{HasCorpus, HasMetadata, State},
|
||||||
|
stats::SimpleStats,
|
||||||
|
utils::{current_nanos, StdRand},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// We will interact with a C++ target, so use external c functionality
|
||||||
|
#[cfg(unix)]
|
||||||
|
extern "C" {
|
||||||
|
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
|
||||||
|
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
|
||||||
|
|
||||||
|
// afl_libfuzzer_init calls LLVMFUzzerInitialize()
|
||||||
|
fn afl_libfuzzer_init() -> i32;
|
||||||
|
|
||||||
|
static __lafl_edges_map: *mut u8;
|
||||||
|
static __lafl_cmp_map: *mut u8;
|
||||||
|
static __lafl_max_edges_size: u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main fn, usually parsing parameters, and starting the fuzzer
|
||||||
|
pub fn main() {
|
||||||
|
// Registry the metadata types used in this fuzzer
|
||||||
|
// Needed only on no_std
|
||||||
|
//RegistryBuilder::register::<Tokens>();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Workdir: {:?}",
|
||||||
|
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||||
|
);
|
||||||
|
fuzz(
|
||||||
|
vec![PathBuf::from("./corpus")],
|
||||||
|
PathBuf::from("./crashes"),
|
||||||
|
1337,
|
||||||
|
)
|
||||||
|
.expect("An error occurred while fuzzing");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Not supported on windows right now
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn fuzz(_corpus_dirs: Vec<PathBuf>, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
|
||||||
|
todo!("Example not supported on Windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The actual fuzzer
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
|
||||||
|
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||||
|
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||||
|
|
||||||
|
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||||
|
let (state, mut restarting_mgr) =
|
||||||
|
match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => match err {
|
||||||
|
Error::ShuttingDown => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Failed to setup the restarter: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an observation channel using the coverage map
|
||||||
|
let edges_observer = HitcountsMapObserver::new(unsafe {
|
||||||
|
StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
|
||||||
|
});
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
State::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
// Feedbacks to rate the interestingness of an input
|
||||||
|
tuple_list!(
|
||||||
|
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
|
||||||
|
TimeFeedback::new()
|
||||||
|
),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
|
// Feedbacks to recognize an input as solution
|
||||||
|
tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("We're a client, let's fuzz :)");
|
||||||
|
|
||||||
|
// Create a PNG dictionary if not existing
|
||||||
|
if state.metadata().get::<Tokens>().is_none() {
|
||||||
|
state.add_metadata(Tokens::new(vec![
|
||||||
|
vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
|
||||||
|
"IHDR".as_bytes().to_vec(),
|
||||||
|
"IDAT".as_bytes().to_vec(),
|
||||||
|
"PLTE".as_bytes().to_vec(),
|
||||||
|
"IEND".as_bytes().to_vec(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a basic mutator with a mutational stage
|
||||||
|
let mutator = HavocBytesMutator::default();
|
||||||
|
let stage = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
|
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||||
|
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||||
|
|
||||||
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
|
let mut harness = |buf: &[u8]| {
|
||||||
|
unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) };
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
|
let mut executor = TimeoutExecutor::new(
|
||||||
|
InProcessExecutor::new(
|
||||||
|
"in-process(edges)",
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(edges_observer, TimeObserver::new("time")),
|
||||||
|
&mut state,
|
||||||
|
&mut restarting_mgr,
|
||||||
|
)?,
|
||||||
|
// 10 seconds timeout
|
||||||
|
Duration::new(10, 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// The actual target run starts here.
|
||||||
|
// Call LLVMFUzzerInitialize() if present.
|
||||||
|
unsafe {
|
||||||
|
if afl_libfuzzer_init() == -1 {
|
||||||
|
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case the corpus is empty (on first run), reset
|
||||||
|
if state.corpus().count() < 1 {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(
|
||||||
|
&mut executor,
|
||||||
|
&mut restarting_mgr,
|
||||||
|
fuzzer.scheduler(),
|
||||||
|
&corpus_dirs,
|
||||||
|
)
|
||||||
|
.expect(&format!(
|
||||||
|
"Failed to load initial corpus at {:?}",
|
||||||
|
&corpus_dirs
|
||||||
|
));
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||||
|
|
||||||
|
// Never reached
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
todo!("libafl-cc");
|
|
||||||
}
|
|
@ -75,7 +75,7 @@ impl CompilerWrapper for ClangWrapper {
|
|||||||
|
|
||||||
let mut linking = true;
|
let mut linking = true;
|
||||||
// Detect stray -v calls from ./configure scripts.
|
// Detect stray -v calls from ./configure scripts.
|
||||||
if args.len() == 1 && args[1] == "-v" {
|
if args.len() > 1 && args[1] == "-v" {
|
||||||
linking = false;
|
linking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
libafl_targets/Cargo.toml
Normal file
15
libafl_targets/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "libafl_targets"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["sancov", "libfuzzer_compatibility"]
|
||||||
|
sancov = []
|
||||||
|
libfuzzer_compatibility = []
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
|
||||||
|
[dependencies]
|
22
libafl_targets/build.rs
Normal file
22
libafl_targets/build.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// build.rs
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
|
//let out_dir_path = Path::new(&out_dir);
|
||||||
|
|
||||||
|
#[cfg(feature = "libfuzzer_compatibility")]
|
||||||
|
{
|
||||||
|
println!("cargo:rerun-if-changed=libfuzzer_compatibility.c");
|
||||||
|
|
||||||
|
cc::Build::new()
|
||||||
|
.file("libfuzzer_compatibility.c")
|
||||||
|
.compile("libfuzzer-compatibility");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-search=native={}", &out_dir);
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
}
|
25
libafl_targets/libfuzzer_compatibility.c
Normal file
25
libafl_targets/libfuzzer_compatibility.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
static int orig_argc;
|
||||||
|
static char **orig_argv;
|
||||||
|
static char **orig_envp;
|
||||||
|
|
||||||
|
static void save_main_args(int argc, char** argv, char** envp) {
|
||||||
|
orig_argc = argc;
|
||||||
|
orig_argv = argv;
|
||||||
|
orig_envp = envp;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((section(".init_array")))
|
||||||
|
void (*p_libafl_targets_save_main_args)(int, char*[], char*[]) = &save_main_args;
|
||||||
|
|
||||||
|
__attribute__((weak))
|
||||||
|
int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||||
|
|
||||||
|
int libafl_targets_libfuzzer_init() {
|
||||||
|
|
||||||
|
if (LLVMFuzzerInitialize) {
|
||||||
|
return LLVMFuzzerInitialize(&orig_argc, &orig_argv);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
libafl_targets/src/lib.rs
Normal file
10
libafl_targets/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#[cfg(feature = "sancov")]
|
||||||
|
pub mod sancov;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
assert_eq!(2 + 2, 4);
|
||||||
|
}
|
||||||
|
}
|
26
libafl_targets/src/sancov.rs
Normal file
26
libafl_targets/src/sancov.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// TODO compile time flag
|
||||||
|
pub const MAP_SIZE: usize = 65536;
|
||||||
|
|
||||||
|
pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE];
|
||||||
|
pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE];
|
||||||
|
pub static mut MAX_EDGES_NUM: usize = 0;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
||||||
|
let pos = *guard as usize;
|
||||||
|
let val = (EDGES_MAP[pos] as u8).wrapping_add(1);
|
||||||
|
EDGES_MAP[pos] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) {
|
||||||
|
if start == stop || *start != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while start < stop {
|
||||||
|
MAX_EDGES_NUM += 1;
|
||||||
|
*start = (MAX_EDGES_NUM & (MAP_SIZE - 1)) as u32;
|
||||||
|
start = start.offset(1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user