Merge branch 'dev' into main

This commit is contained in:
Dominik Maier 2021-03-09 21:38:25 +01:00
commit 8a60d806eb
51 changed files with 1966 additions and 989 deletions

View File

@ -1,52 +1,46 @@
name: Build and Test name: Build and Test
on: [push] on:
push:
branches: [ main ]
pull_requests:
branches: [ main, dev ]
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
jobs: jobs:
default: ubuntu:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Build - name: Default Build
run: cargo build --verbose run: cargo build --verbose
- name: Test - name: Default Test
run: cargo test --verbose run: cargo test --verbose
all-features:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build all features - name: Build all features
run: cd libafl && cargo build --all-features --verbose run: cd libafl && cargo build --all-features --verbose
- name: Test all features - name: Test all features
run: cd libafl && cargo test --all-features --verbose run: cd libafl && cargo test --all-features --verbose
no-std:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build no_std - name: Build no_std
run: cd libafl && cargo build --no-default-features --verbose run: cd libafl && cargo build --no-default-features --verbose
- name: Test no_std - name: Test no_std
run: cd libafl && cargo test --no-default-features --verbose run: cd libafl && cargo test --no-default-features --verbose
examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build examples - name: Build examples
run: cargo build --examples --verbose run: cargo build --examples --verbose
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Format - name: Format
run: cargo fmt -- --check run: cargo fmt -- --check
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Build Docs - name: Build Docs
run: cargo doc run: cargo doc
- name: Test Docs - name: Test Docs
run: cargo test --doc run: cargo test --doc
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Windows Build
run: cargo build --verbose
- name: Windows Test
run: cargo test --verbose

166
LICENSE
View File

@ -1,166 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

201
LICENSE-APACHE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

23
LICENSE-MIT Normal file
View File

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -4,9 +4,7 @@ Advanced Fuzzing Library - Slot your own fuzzers together and extend their featu
LibAFL is written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <mail@dmnk.co>. LibAFL is written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <mail@dmnk.co>.
It is released as Free and Open Source Software under the GNU Lesser General Public License V3. It is released as Open Source Software under the [Apache v2](LICENSE-APACHE) or [MIT](LICENSE-MIT) licenses.
## Example usages ## Example usages

View File

@ -15,6 +15,7 @@
- [ ] Restart Count in Fuzzing Loop - [ ] Restart Count in Fuzzing Loop
- [ ] LAIN / structured fuzzing example - [ ] LAIN / structured fuzzing example
- [ ] Errors in the Fuzzer should exit the fuzz run - [ ] Errors in the Fuzzer should exit the fuzz run
- [ ] More informative outpus, deeper introspection (stats, what mutation did x, etc.)
- [ ] Timeouts for executors - [ ] Timeouts for executors
- [ ] Timeout handling for llmp clients (no ping for n seconds -> treat as disconnected) - [ ] Timeout handling for llmp clients (no ping for n seconds -> treat as disconnected)
- [ ] LLMP Cross Machine Link (2 brokers connected via TCP) - [ ] LLMP Cross Machine Link (2 brokers connected via TCP)

View File

@ -1,12 +1,19 @@
// build.rs // build.rs
use std::env; use std::{
use std::path::Path; env,
use std::process::Command; path::Path,
process::{exit, Command},
};
const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz"; const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz";
fn main() { fn main() {
if cfg!(windows) {
println!("cargo:warning=Skipping libmozjpeg example on Windows");
exit(0);
}
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string();

View File

@ -3,6 +3,7 @@
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
#[cfg(unix)]
use libafl::{ use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list}, bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
@ -22,6 +23,7 @@ use libafl::{
}; };
/// We will interact with a C++ target, so use external c functionality /// We will interact with a C++ target, so use external c functionality
#[cfg(unix)]
extern "C" { extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
@ -35,6 +37,7 @@ extern "C" {
} }
/// The wrapped harness function, calling out to the LLVM-style harness /// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where where
E: Executor<I>, E: Executor<I>,
@ -65,7 +68,14 @@ pub fn main() {
.expect("An error occurred while fuzzing"); .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 /// The actual fuzzer
#[cfg(unix)]
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { 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 // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = SimpleStats::new(|s| println!("{}", s));
@ -76,10 +86,9 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
.expect("Failed to setup the restarter".into()); .expect("Failed to setup the restarter".into());
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = let edges_observer = unsafe {
StdMapObserver::new_from_ptr("edges", unsafe { __lafl_edges_map }, unsafe { StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
__lafl_max_edges_size as usize };
});
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
@ -119,7 +128,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
tuple_list!(edges_observer), tuple_list!(edges_observer),
&mut state, &mut state,
&mut restarting_mgr, &mut restarting_mgr,
); )?;
// The actual target run starts here. // The actual target run starts here.
// Call LLVMFUzzerInitialize() if present. // Call LLVMFUzzerInitialize() if present.

View File

@ -1,13 +1,20 @@
// build.rs // build.rs
use std::env; use std::{
use std::path::Path; env,
use std::process::Command; path::Path,
process::{exit, Command},
};
const LIBPNG_URL: &str = const LIBPNG_URL: &str =
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz"; "https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
fn main() { fn main() {
if cfg!(windows) {
println!("cargo:warning=Skipping libpng example on Windows");
exit(0);
}
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string(); let out_dir = out_dir.to_string_lossy().to_string();

View File

@ -158,7 +158,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (width && height > 100000000 / width) { if (width && height > 100000000 / width) {
PNG_CLEANUP PNG_CLEANUP
#ifdef HAS_DUMMY_CRASH #ifdef HAS_DUMMY_CRASH
asm("ud2");
#ifdef __aarch64__ #ifdef __aarch64__
asm volatile (".word 0xf7f0a000\n"); asm volatile (".word 0xf7f0a000\n");
#else #else

View File

@ -3,6 +3,7 @@
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
#[cfg(unix)]
use libafl::{ use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list}, bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{ corpus::{
@ -11,11 +12,11 @@ use libafl::{
}, },
events::setup_restarting_mgr, events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind}, executors::{inprocess::InProcessExecutor, Executor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::Input, inputs::Input,
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
observers::{HitcountsMapObserver, StdMapObserver}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage, stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State}, state::{HasCorpus, HasMetadata, State},
stats::SimpleStats, stats::SimpleStats,
@ -24,6 +25,7 @@ use libafl::{
}; };
/// We will interact with a C++ target, so use external c functionality /// We will interact with a C++ target, so use external c functionality
#[cfg(unix)]
extern "C" { extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
@ -37,6 +39,7 @@ extern "C" {
} }
/// The wrapped harness function, calling out to the LLVM-style harness /// The wrapped harness function, calling out to the LLVM-style harness
#[cfg(unix)]
fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind fn harness<E, I>(_executor: &E, buf: &[u8]) -> ExitKind
where where
E: Executor<I>, E: Executor<I>,
@ -67,22 +70,36 @@ pub fn main() {
.expect("An error occurred while fuzzing"); .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 /// The actual fuzzer
#[cfg(unix)]
fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { 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 // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); 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. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) = let (state, mut restarting_mgr) =
setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
.expect("Failed to setup the restarter".into()); 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 // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr( let edges_observer = HitcountsMapObserver::new(unsafe {
"edges", StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
unsafe { __lafl_edges_map }, });
unsafe { __lafl_max_edges_size as usize },
));
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
@ -92,11 +109,10 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(), InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input // Feedbacks to rate the interestingness of an input
tuple_list!(MaxMapFeedback::new_with_observer_track( tuple_list!(
&edges_observer, MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
true, TimeFeedback::new()
false ),
)),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(), OnDiskCorpus::new(objective_dir).unwrap(),
@ -130,10 +146,10 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let mut executor = InProcessExecutor::new( let mut executor = InProcessExecutor::new(
"in-process(edges)", "in-process(edges)",
harness, harness,
tuple_list!(edges_observer), tuple_list!(edges_observer, TimeObserver::new("time")),
&mut state, &mut state,
&mut restarting_mgr, &mut restarting_mgr,
); )?;
// The actual target run starts here. // The actual target run starts here.
// Call LLVMFUzzerInitialize() if present. // Call LLVMFUzzerInitialize() if present.

View File

@ -1,5 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define MAP_SIZE 65536 #define MAP_SIZE 65536
@ -8,9 +10,11 @@ char **orig_argv;
char **orig_envp; char **orig_envp;
uint8_t __lafl_dummy_map[MAP_SIZE]; uint8_t __lafl_dummy_map[MAP_SIZE];
size_t __lafl_dummy_map_usize[MAP_SIZE];
uint8_t *__lafl_edges_map = __lafl_dummy_map; uint8_t *__lafl_edges_map = __lafl_dummy_map;
uint8_t *__lafl_cmp_map = __lafl_dummy_map; uint8_t *__lafl_cmp_map = __lafl_dummy_map;
size_t *__lafl_alloc_map = __lafl_dummy_map_usize;
uint32_t __lafl_max_edges_size = 0; uint32_t __lafl_max_edges_size = 0;
@ -127,6 +131,32 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
} }
void *malloc(size_t size) {
uintptr_t k = (uintptr_t)__builtin_return_address(0);
k = (k >> 4) ^ (k << 8);
k &= MAP_SIZE - 1;
__lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size);
return realloc(NULL, size);
}
void *calloc(size_t nmemb, size_t size) {
size *= nmemb;
uintptr_t k = (uintptr_t)__builtin_return_address(0);
k = (k >> 4) ^ (k << 8);
k &= MAP_SIZE - 1;
__lafl_alloc_map[k] = MAX(__lafl_alloc_map[k], size);
void *result = realloc(NULL, size);
memset(result, 0, size);
return result;
}
static void afl_libfuzzer_copy_args(int argc, char** argv, char** envp) { static void afl_libfuzzer_copy_args(int argc, char** argv, char** envp) {
orig_argc = argc; orig_argc = argc;
orig_argv = argv; orig_argv = argv;

View File

@ -72,7 +72,7 @@ pub extern "C" fn fuzz_main_loop() {
}); });
let edges_feedback = MaxMapFeedback::new_with_observer(&NAME_COV_MAP, &edges_observer); let edges_feedback = MaxMapFeedback::new_with_observer(&NAME_COV_MAP, &edges_observer);
let executor = InProcessExecutor::new("QEMUFuzzer", harness, tuple_list!(edges_observer)); let executor = InProcessExecutor::new("QEMUFuzzer", harness, tuple_list!(edges_observer))?;
let mut state = State::new(tuple_list!(edges_feedback)); let mut state = State::new(tuple_list!(edges_feedback));
let mut engine = Engine::new(executor); let mut engine = Engine::new(executor);

View File

@ -54,9 +54,11 @@ ctor = "*"
libafl_derive = { version = "*", optional = true, path = "../libafl_derive" } libafl_derive = { version = "*", optional = true, path = "../libafl_derive" }
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap
#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression #TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression
num_enum = "0.5.1"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
libc = "0.2" # For (*nix) libc libc = "0.2" # For (*nix) libc
nix = "0.20.0"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows = "0.3.1" windows = "0.3.1"

View File

@ -3,17 +3,21 @@ This shows how llmp can be used directly, without libafl abstractions
*/ */
extern crate alloc; extern crate alloc;
#[cfg(all(unix, feature = "std"))]
use core::{convert::TryInto, time::Duration}; use core::{convert::TryInto, time::Duration};
#[cfg(all(unix, feature = "std"))]
use std::{thread, time}; use std::{thread, time};
#[cfg(all(unix, feature = "std"))]
use libafl::{ use libafl::{
bolts::{llmp, shmem::UnixShMem}, bolts::{llmp, shmem::UnixShMem},
Error, Error,
}; };
const TAG_SIMPLE_U32_V1: u32 = 0x51300321; const _TAG_SIMPLE_U32_V1: u32 = 0x51300321;
const TAG_MATH_RESULT_V1: u32 = 0x77474331; const _TAG_MATH_RESULT_V1: u32 = 0x77474331;
#[cfg(all(unix, feature = "std"))]
fn adder_loop(port: u16) -> ! { fn adder_loop(port: u16) -> ! {
let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap(); let mut client = llmp::LlmpClient::<UnixShMem>::create_attach_to_tcp(port).unwrap();
let mut last_result: u32 = 0; let mut last_result: u32 = 0;
@ -27,7 +31,7 @@ fn adder_loop(port: u16) -> ! {
}; };
msg_counter += 1; msg_counter += 1;
match tag { match tag {
TAG_SIMPLE_U32_V1 => { _TAG_SIMPLE_U32_V1 => {
current_result = current_result =
current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap())); current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap()));
} }
@ -42,7 +46,7 @@ fn adder_loop(port: u16) -> ! {
); );
client client
.send_buf(TAG_MATH_RESULT_V1, &current_result.to_le_bytes()) .send_buf(_TAG_MATH_RESULT_V1, &current_result.to_le_bytes())
.unwrap(); .unwrap();
last_result = current_result; last_result = current_result;
} }
@ -51,13 +55,14 @@ fn adder_loop(port: u16) -> ! {
} }
} }
#[cfg(all(unix, feature = "std"))]
fn broker_message_hook( fn broker_message_hook(
client_id: u32, client_id: u32,
tag: llmp::Tag, tag: llmp::Tag,
message: &[u8], message: &[u8],
) -> Result<llmp::LlmpMsgHookResult, Error> { ) -> Result<llmp::LlmpMsgHookResult, Error> {
match tag { match tag {
TAG_SIMPLE_U32_V1 => { _TAG_SIMPLE_U32_V1 => {
println!( println!(
"Client {:?} sent message: {:?}", "Client {:?} sent message: {:?}",
client_id, client_id,
@ -65,7 +70,7 @@ fn broker_message_hook(
); );
Ok(llmp::LlmpMsgHookResult::ForwardToClients) Ok(llmp::LlmpMsgHookResult::ForwardToClients)
} }
TAG_MATH_RESULT_V1 => { _TAG_MATH_RESULT_V1 => {
println!( println!(
"Adder Client has this current result: {:?}", "Adder Client has this current result: {:?}",
u32::from_le_bytes(message.try_into().unwrap()) u32::from_le_bytes(message.try_into().unwrap())
@ -79,6 +84,12 @@ fn broker_message_hook(
} }
} }
#[cfg(not(unix))]
fn main() {
todo!("LLMP is not yet supported on this platform.");
}
#[cfg(unix)]
fn main() { fn main() {
/* The main node has a broker, and a few worker threads */ /* The main node has a broker, and a few worker threads */
@ -96,9 +107,9 @@ fn main() {
"broker" => { "broker" => {
let mut broker = llmp::LlmpBroker::<UnixShMem>::new().unwrap(); let mut broker = llmp::LlmpBroker::<UnixShMem>::new().unwrap();
broker broker
.launch_tcp_listener( .launch_listener(llmp::Listener::Tcp(
std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(), std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(),
) ))
.unwrap(); .unwrap();
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5))) broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
} }
@ -108,7 +119,7 @@ fn main() {
loop { loop {
counter = counter.wrapping_add(1); counter = counter.wrapping_add(1);
client client
.send_buf(TAG_SIMPLE_U32_V1, &counter.to_le_bytes()) .send_buf(_TAG_SIMPLE_U32_V1, &counter.to_le_bytes())
.unwrap(); .unwrap();
println!("CTR Client writing {}", counter); println!("CTR Client writing {}", counter);
thread::sleep(Duration::from_secs(1)) thread::sleep(Duration::from_secs(1))

View File

@ -1,2 +1,2 @@
#[cfg(windows)] #[cfg(all(windows, feature = "std"))]
::windows::include_bindings!(); ::windows::include_bindings!();

View File

@ -52,7 +52,7 @@ Then register some clientloops using llmp_broker_register_threaded_clientloop
*/ */
use alloc::vec::Vec; use alloc::{string::String, vec::Vec};
use core::{ use core::{
cmp::max, cmp::max,
fmt::Debug, fmt::Debug,
@ -64,14 +64,42 @@ use core::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::{ use std::{
env, env, fs,
io::{Read, Write}, io::{Read, Write},
net::{TcpListener, TcpStream}, net::{SocketAddr, TcpListener, TcpStream},
thread, thread,
}; };
use super::shmem::{ShMem, ShMemDescription}; #[cfg(all(feature = "std", unix))]
use crate::Error; use nix::{
cmsg_space,
sys::{
socket::{recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags},
uio::IoVec,
},
};
#[cfg(all(feature = "std", unix))]
use std::{
ffi::CStr,
os::unix::{
self,
net::{UnixListener, UnixStream},
{io::AsRawFd, prelude::RawFd},
},
};
#[cfg(all(feature = "std", unix))]
use libc::c_char;
#[cfg(unix)]
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler, siginfo_t, Handler, Signal};
use crate::{
bolts::shmem::{ShMem, ShMemDescription},
Error,
};
use super::shmem::HasFd;
/// We'll start off with 256 megabyte maps per fuzzer client /// We'll start off with 256 megabyte maps per fuzzer client
const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28; const LLMP_PREF_INITIAL_MAP_SIZE: usize = 1 << 28;
@ -101,9 +129,54 @@ const EOP_MSG_SIZE: usize =
/// The header length of a llmp page in a shared map (until messages start) /// The header length of a llmp page in a shared map (until messages start)
const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>(); const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
/// The llmp broker registers a signal handler for cleanups on `SIGINT`.
#[cfg(unix)]
static mut GLOBAL_SIGHANDLER_STATE: LlmpBrokerSignalHandler = LlmpBrokerSignalHandler {
shutting_down: false,
};
/// TAGs used thorughout llmp /// TAGs used thorughout llmp
pub type Tag = u32; pub type Tag = u32;
/// Abstraction for listeners
#[cfg(feature = "std")]
pub enum Listener {
Tcp(TcpListener),
#[cfg(unix)]
Unix(UnixListener),
}
#[cfg(feature = "std")]
pub enum ListenerStream {
Tcp(TcpStream, SocketAddr),
#[cfg(unix)]
Unix(UnixStream, unix::net::SocketAddr),
Empty(),
}
#[cfg(feature = "std")]
impl Listener {
fn accept(&self) -> ListenerStream {
match self {
Listener::Tcp(inner) => match inner.accept() {
Ok(res) => ListenerStream::Tcp(res.0, res.1),
Err(err) => {
dbg!("Ignoring failed accept", err);
ListenerStream::Empty()
}
},
#[cfg(unix)]
Listener::Unix(inner) => match inner.accept() {
Ok(res) => ListenerStream::Unix(res.0, res.1),
Err(err) => {
dbg!("Ignoring failed accept", err);
ListenerStream::Empty()
}
},
}
}
}
/// Get sharedmem from a page /// Get sharedmem from a page
#[inline] #[inline]
unsafe fn shmem2page_mut<SH: ShMem>(afl_shmem: &mut SH) -> *mut LlmpPage { unsafe fn shmem2page_mut<SH: ShMem>(afl_shmem: &mut SH) -> *mut LlmpPage {
@ -120,8 +193,8 @@ unsafe fn shmem2page<SH: ShMem>(afl_shmem: &SH) -> *const LlmpPage {
#[inline] #[inline]
unsafe fn llmp_msg_in_page(page: *const LlmpPage, msg: *const LlmpMsg) -> bool { unsafe fn llmp_msg_in_page(page: *const LlmpPage, msg: *const LlmpMsg) -> bool {
/* DBG("llmp_msg_in_page %p within %p-%p\n", msg, page, page + page->size_total); */ /* DBG("llmp_msg_in_page %p within %p-%p\n", msg, page, page + page->size_total); */
return (page as *const u8) < msg as *const u8 (page as *const u8) < msg as *const u8
&& (page as *const u8).offset((*page).size_total as isize) > msg as *const u8; && (page as *const u8).add((*page).size_total) > msg as *const u8
} }
/// allign to LLMP_PREF_ALIGNNMENT=64 bytes /// allign to LLMP_PREF_ALIGNNMENT=64 bytes
@ -198,9 +271,9 @@ unsafe fn llmp_next_msg_ptr_checked<SH: ShMem>(
) -> Result<*mut LlmpMsg, Error> { ) -> Result<*mut LlmpMsg, Error> {
let page = map.page_mut(); let page = map.page_mut();
let map_size = map.shmem.map().len(); let map_size = map.shmem.map().len();
let msg_begin_min = (page as *const u8).offset(size_of::<LlmpPage>() as isize); let msg_begin_min = (page as *const u8).add(size_of::<LlmpPage>());
// We still need space for this msg (alloc_size). // We still need space for this msg (alloc_size).
let msg_begin_max = (page as *const u8).offset((map_size - alloc_size) as isize); let msg_begin_max = (page as *const u8).add(map_size - alloc_size);
let next = _llmp_next_msg_ptr(last_msg); let next = _llmp_next_msg_ptr(last_msg);
let next_ptr = next as *const u8; let next_ptr = next as *const u8;
if next_ptr >= msg_begin_min && next_ptr <= msg_begin_max { if next_ptr >= msg_begin_min && next_ptr <= msg_begin_max {
@ -217,9 +290,9 @@ unsafe fn llmp_next_msg_ptr_checked<SH: ShMem>(
#[inline] #[inline]
unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg { unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg {
/* DBG("_llmp_next_msg_ptr %p %lu + %lu\n", last_msg, last_msg->buf_len_padded, sizeof(llmp_message)); */ /* DBG("_llmp_next_msg_ptr %p %lu + %lu\n", last_msg, last_msg->buf_len_padded, sizeof(llmp_message)); */
return (last_msg as *mut u8) (last_msg as *mut u8)
.offset(size_of::<LlmpMsg>() as isize) .add(size_of::<LlmpMsg>())
.offset((*last_msg).buf_len_padded as isize) as *mut LlmpMsg; .add((*last_msg).buf_len_padded as usize) as *mut LlmpMsg
} }
/// Description of a shared map. /// Description of a shared map.
@ -262,6 +335,7 @@ pub struct LlmpMsg {
/// The message we receive /// The message we receive
impl LlmpMsg { impl LlmpMsg {
/// Gets the buffer from this message as slice, with the corrent length. /// Gets the buffer from this message as slice, with the corrent length.
/// # Safety
/// This is unsafe if somebody has access to shared mem pages on the system. /// This is unsafe if somebody has access to shared mem pages on the system.
pub unsafe fn as_slice_unsafe(&self) -> &[u8] { pub unsafe fn as_slice_unsafe(&self) -> &[u8] {
slice::from_raw_parts(self.buf.as_ptr(), self.buf_len as usize) slice::from_raw_parts(self.buf.as_ptr(), self.buf_len as usize)
@ -285,14 +359,13 @@ impl LlmpMsg {
unsafe { unsafe {
let map_size = map.shmem.map().len(); let map_size = map.shmem.map().len();
let buf_ptr = self.buf.as_ptr(); let buf_ptr = self.buf.as_ptr();
if buf_ptr > (map.page_mut() as *const u8).offset(size_of::<LlmpPage>() as isize) if buf_ptr > (map.page_mut() as *const u8).add(size_of::<LlmpPage>())
&& buf_ptr && buf_ptr
<= (map.page_mut() as *const u8) <= (map.page_mut() as *const u8).add(map_size - size_of::<LlmpMsg>() as usize)
.offset((map_size - size_of::<LlmpMsg>() as usize) as isize)
{ {
// The message header is in the page. Continue with checking the body. // The message header is in the page. Continue with checking the body.
let len = self.buf_len_padded as usize + size_of::<LlmpMsg>(); let len = self.buf_len_padded as usize + size_of::<LlmpMsg>();
buf_ptr <= (map.page_mut() as *const u8).offset((map_size - len) as isize) buf_ptr <= (map.page_mut() as *const u8).add(map_size - len)
} else { } else {
false false
} }
@ -324,7 +397,7 @@ where
// We got the port. We are the broker! :) // We got the port. We are the broker! :)
dbg!("We're the broker"); dbg!("We're the broker");
let mut broker = LlmpBroker::new()?; let mut broker = LlmpBroker::new()?;
let _listener_thread = broker.launch_tcp_listener(listener)?; let _listener_thread = broker.launch_listener(Listener::Tcp(listener))?;
Ok(LlmpConnection::IsBroker { broker }) Ok(LlmpConnection::IsBroker { broker })
} }
Err(e) => { Err(e) => {
@ -368,6 +441,36 @@ where
} }
} }
impl<SH> LlmpConnection<SH>
where
SH: ShMem + HasFd,
{
#[cfg(all(feature = "std", unix))]
pub fn on_domain_socket(filename: &str) -> Result<Self, Error> {
match UnixListener::bind(filename) {
Ok(listener) => {
dbg!("We're the broker");
let mut broker = LlmpBroker::new()?;
broker.socket_name = Some(filename.to_string());
let _listener_thread = broker.launch_listener(Listener::Unix(listener))?;
Ok(LlmpConnection::IsBroker { broker })
}
Err(e) => {
match e.kind() {
std::io::ErrorKind::AddrInUse => {
// We are the client :)
dbg!("We're the client", e);
Ok(LlmpConnection::IsClient {
client: LlmpClient::create_attach_to_unix(filename)?,
})
}
_ => Err(Error::File(e)),
}
}
}
}
}
/// Contents of the share mem pages, used by llmp internally /// Contents of the share mem pages, used by llmp internally
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[repr(C, packed)] #[repr(C, packed)]
@ -448,6 +551,8 @@ where
/// Completely reset the current sender map. /// Completely reset the current sender map.
/// Afterwards, no receiver should read from it at a different location. /// Afterwards, no receiver should read from it at a different location.
/// This is only useful if all connected llmp parties start over, for example after a crash. /// This is only useful if all connected llmp parties start over, for example after a crash.
/// # Safety
/// Only safe if you really really restart the page on everything connected
pub unsafe fn reset(&mut self) { pub unsafe fn reset(&mut self) {
_llmp_page_init(&mut self.out_maps.last_mut().unwrap().shmem, self.id, true); _llmp_page_init(&mut self.out_maps.last_mut().unwrap().shmem, self.id, true);
self.last_msg_sent = ptr::null_mut(); self.last_msg_sent = ptr::null_mut();
@ -640,7 +745,7 @@ where
panic!("Allocated new message without calling send() inbetween. ret: {:?}, page: {:?}, complete_msg_size: {:?}, size_used: {:?}, last_msg: {:?}", ret, page, panic!("Allocated new message without calling send() inbetween. ret: {:?}, page: {:?}, complete_msg_size: {:?}, size_used: {:?}, last_msg: {:?}", ret, page,
buf_len_padded, (*page).size_used, last_msg); buf_len_padded, (*page).size_used, last_msg);
} }
(*page).size_used = (*page).size_used + complete_msg_size; (*page).size_used += complete_msg_size;
(*ret).buf_len_padded = buf_len_padded as u64; (*ret).buf_len_padded = buf_len_padded as u64;
(*ret).buf_len = buf_len as u64; (*ret).buf_len = buf_len as u64;
/* DBG("Returning new message at %p with len %ld, TAG was %x", ret, ret->buf_len_padded, ret->tag); */ /* DBG("Returning new message at %p with len %ld, TAG was %x", ret, ret->buf_len_padded, ret->tag); */
@ -714,16 +819,17 @@ where
} }
/// Allocates the next space on this sender page /// Allocates the next space on this sender page
pub unsafe fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, Error> { pub fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, Error> {
match self.alloc_next_if_space(buf_len) { if let Some(msg) = unsafe { self.alloc_next_if_space(buf_len) } {
Some(msg) => return Ok(msg), return Ok(msg);
_ => (),
}; };
/* no more space left! We'll have to start a new page */ /* no more space left! We'll have to start a new page */
unsafe {
self.handle_out_eop()?; self.handle_out_eop()?;
}
match self.alloc_next_if_space(buf_len) { match unsafe { self.alloc_next_if_space(buf_len) } {
Some(msg) => Ok(msg), Some(msg) => Ok(msg),
None => Err(Error::Unknown(format!( None => Err(Error::Unknown(format!(
"Error allocating {} bytes in shmap", "Error allocating {} bytes in shmap",
@ -733,6 +839,8 @@ where
} }
/// Cancel send of the next message, this allows us to allocate a new message without sending this one. /// Cancel send of the next message, this allows us to allocate a new message without sending this one.
/// # Safety
/// They msg pointer may no longer be used after `cancel_send`
pub unsafe fn cancel_send(&mut self, msg: *mut LlmpMsg) { pub unsafe fn cancel_send(&mut self, msg: *mut LlmpMsg) {
/* DBG("Client %d cancels send of msg at %p with tag 0x%X and size %ld", client->id, msg, msg->tag, /* DBG("Client %d cancels send of msg at %p with tag 0x%X and size %ld", client->id, msg, msg->tag,
* msg->buf_len_padded); */ * msg->buf_len_padded); */
@ -769,7 +877,7 @@ where
let last_message_offset = if self.last_msg_sent.is_null() { let last_message_offset = if self.last_msg_sent.is_null() {
None None
} else { } else {
Some(map.msg_to_offset(self.last_msg_sent)?) Some(unsafe { map.msg_to_offset(self.last_msg_sent) }?)
}; };
Ok(LlmpDescription { Ok(LlmpDescription {
shmem: map.shmem.description(), shmem: map.shmem.description(),
@ -872,8 +980,7 @@ where
}; };
// Let's see what we go here. // Let's see what we go here.
match ret { if let Some(msg) = ret {
Some(msg) => {
if !(*msg).in_map(&mut self.current_recv_map) { if !(*msg).in_map(&mut self.current_recv_map) {
return Err(Error::IllegalState("Unexpected message in map (out of map bounds) - bugy client or tampered shared map detedted!".into())); return Err(Error::IllegalState("Unexpected message in map (out of map bounds) - bugy client or tampered shared map detedted!".into()));
} }
@ -901,8 +1008,7 @@ where
// Mark the old page save to unmap, in case we didn't so earlier. // Mark the old page save to unmap, in case we didn't so earlier.
ptr::write_volatile(&mut (*page).save_to_unmap, 1); ptr::write_volatile(&mut (*page).save_to_unmap, 1);
// Map the new page. The old one should be unmapped by Drop // Map the new page. The old one should be unmapped by Drop
self.current_recv_map = self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice(
LlmpSharedMap::existing(SH::existing_from_shm_slice(
&pageinfo_cpy.shm_str, &pageinfo_cpy.shm_str,
pageinfo_cpy.map_size, pageinfo_cpy.map_size,
)?); )?);
@ -919,14 +1025,14 @@ where
// Store the last msg for next time // Store the last msg for next time
self.last_msg_recvd = msg; self.last_msg_recvd = msg;
}
_ => (),
}; };
Ok(ret) Ok(ret)
} }
/// Blocks/spins until the next message gets posted to the page, /// Blocks/spins until the next message gets posted to the page,
/// then returns that message. /// then returns that message.
/// # Safety
/// Returns a raw ptr, on the recv map. Should be safe in general
pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, Error> { pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, Error> {
let mut current_msg_id = 0; let mut current_msg_id = 0;
let page = self.current_recv_map.page_mut(); let page = self.current_recv_map.page_mut();
@ -982,7 +1088,7 @@ where
let last_message_offset = if self.last_msg_recvd.is_null() { let last_message_offset = if self.last_msg_recvd.is_null() {
None None
} else { } else {
Some(map.msg_to_offset(self.last_msg_recvd)?) Some(unsafe { map.msg_to_offset(self.last_msg_recvd) }?)
}; };
Ok(LlmpDescription { Ok(LlmpDescription {
shmem: map.shmem.description(), shmem: map.shmem.description(),
@ -1047,19 +1153,24 @@ where
} }
/// Get the unsafe ptr to this page, situated on the shared map /// Get the unsafe ptr to this page, situated on the shared map
/// # Safety
/// The unsafe page pointer is obviously unsafe.
pub unsafe fn page_mut(&mut self) -> *mut LlmpPage { pub unsafe fn page_mut(&mut self) -> *mut LlmpPage {
shmem2page_mut(&mut self.shmem) shmem2page_mut(&mut self.shmem)
} }
/// Get the unsafe ptr to this page, situated on the shared map /// Get the unsafe ptr to this page, situated on the shared map
/// # Safety
/// The unsafe page pointer is obviously unsafe.
pub unsafe fn page(&self) -> *const LlmpPage { pub unsafe fn page(&self) -> *const LlmpPage {
shmem2page(&self.shmem) shmem2page(&self.shmem)
} }
/// Gets the offset of a message on this here page. /// Gets the offset of a message on this here page.
/// Will return IllegalArgument error if msg is not on page. /// Will return IllegalArgument error if msg is not on page.
pub fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result<u64, Error> { /// # Safety
unsafe { /// This dereferences msg, make sure to pass a proper pointer to it.
pub unsafe fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result<u64, Error> {
let page = self.page(); let page = self.page();
if llmp_msg_in_page(page, msg) { if llmp_msg_in_page(page, msg) {
// Cast both sides to u8 arrays, get the offset, then cast the return isize to u64 // Cast both sides to u8 arrays, get the offset, then cast the return isize to u64
@ -1071,7 +1182,6 @@ where
))) )))
} }
} }
}
/// Retrieve the stored msg from env_name + _OFFSET. /// Retrieve the stored msg from env_name + _OFFSET.
/// It will restore the stored offset by env_name and return the message. /// It will restore the stored offset by env_name and return the message.
@ -1092,7 +1202,7 @@ where
} else { } else {
env::set_var( env::set_var(
&format!("{}_OFFSET", map_env_name), &format!("{}_OFFSET", map_env_name),
format!("{}", self.msg_to_offset(msg)?), format!("{}", unsafe { self.msg_to_offset(msg) }?),
) )
}; };
Ok(()) Ok(())
@ -1131,6 +1241,27 @@ where
/// This allows us to intercept messages right in the broker /// This allows us to intercept messages right in the broker
/// This keeps the out map clean. /// This keeps the out map clean.
pub llmp_clients: Vec<LlmpReceiver<SH>>, pub llmp_clients: Vec<LlmpReceiver<SH>>,
/// This is the socket name, when unix domain sockets are used.
socket_name: Option<String>,
/// This flag is used to indicate that shutdown has been requested by the SIGINT and SIGTERM
/// handlers
shutting_down: bool,
}
#[cfg(unix)]
pub struct LlmpBrokerSignalHandler {
shutting_down: bool,
}
#[cfg(all(unix))]
impl Handler for LlmpBrokerSignalHandler {
fn handle(&mut self, _signal: Signal, _info: siginfo_t, _void: c_void) {
unsafe { ptr::write_volatile(&mut self.shutting_down, true) };
}
fn signals(&self) -> Vec<Signal> {
vec![Signal::SigTerm, Signal::SigInterrupt, Signal::SigQuit]
}
} }
/// The broker forwards all messages to its own bus-like broadcast map. /// The broker forwards all messages to its own bus-like broadcast map.
@ -1151,6 +1282,8 @@ where
keep_pages_forever: true, keep_pages_forever: true,
}, },
llmp_clients: vec![], llmp_clients: vec![],
socket_name: None,
shutting_down: false,
}; };
Ok(broker) Ok(broker)
@ -1210,23 +1343,43 @@ where
Ok(()) Ok(())
} }
/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
#[inline]
#[cfg(unix)]
fn is_shutting_down(&self) -> bool {
unsafe { ptr::read_volatile(&GLOBAL_SIGHANDLER_STATE.shutting_down) }
}
/// Always returns true on platforms, where no shutdown signal handlers are supported
#[inline]
#[cfg(not(unix))]
fn is_shutting_down(&self) -> bool {
false
}
/// Loops infinitely, forwarding and handling all incoming messages from clients. /// Loops infinitely, forwarding and handling all incoming messages from clients.
/// Never returns. Panics on error. /// Never returns. Panics on error.
/// 5 millis of sleep can't hurt to keep busywait not at 100% /// 5 millis of sleep can't hurt to keep busywait not at 100%
pub fn loop_forever<F>(&mut self, on_new_msg: &mut F, sleep_time: Option<Duration>) -> ! pub fn loop_forever<F>(&mut self, on_new_msg: &mut F, sleep_time: Option<Duration>)
where where
F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>, F: FnMut(u32, Tag, &[u8]) -> Result<LlmpMsgHookResult, Error>,
{ {
loop { #[cfg(unix)]
if let Err(_e) = unsafe { setup_signal_handler(&mut GLOBAL_SIGHANDLER_STATE) } {
// We can live without a proper ctrl+c signal handler. Print and ignore.
#[cfg(feature = "std")]
println!("Failed to setup signal handlers: {}", _e);
}
while !self.is_shutting_down() {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
self.once(on_new_msg) self.once(on_new_msg)
.expect("An error occurred when brokering. Exiting."); .expect("An error occurred when brokering. Exiting.");
#[cfg(feature = "std")] #[cfg(feature = "std")]
match sleep_time { if let Some(time) = sleep_time {
Some(time) => thread::sleep(time), thread::sleep(time)
None => (), };
}
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
match sleep_time { match sleep_time {
@ -1250,15 +1403,12 @@ where
let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?; let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?;
// accept connections and process them, spawning a new thread for each one // accept connections and process them, spawning a new thread for each one
println!("Server listening on port {}", port); println!("Server listening on port {}", port);
return self.launch_tcp_listener(listener); self.launch_listener(Listener::Tcp(listener))
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
/// Launches a thread using a tcp listener socket, on which new clients may connect to this broker /// Launches a thread using a listener socket, on which new clients may connect to this broker
pub fn launch_tcp_listener( pub fn launch_listener(&mut self, listener: Listener) -> Result<thread::JoinHandle<()>, Error> {
&mut self,
listener: TcpListener,
) -> Result<thread::JoinHandle<()>, Error> {
// Later in the execution, after the initial map filled up, // Later in the execution, after the initial map filled up,
// the current broacast map will will point to a different map. // the current broacast map will will point to a different map.
// However, the original map is (as of now) never freed, new clients will start // However, the original map is (as of now) never freed, new clients will start
@ -1290,13 +1440,8 @@ where
}; };
loop { loop {
let (mut stream, addr) = match listener.accept() { match listener.accept() {
Ok(res) => res, ListenerStream::Tcp(mut stream, addr) => {
Err(e) => {
dbg!("Ignoring failed accept", e);
continue;
}
};
dbg!("New connection", addr, stream.peer_addr().unwrap()); dbg!("New connection", addr, stream.peer_addr().unwrap());
match stream.write(&broadcast_str_initial) { match stream.write(&broadcast_str_initial) {
Ok(_) => {} // fire & forget Ok(_) => {} // fire & forget
@ -1313,7 +1458,6 @@ where
continue; continue;
} }
}; };
unsafe { unsafe {
let msg = new_client_sender let msg = new_client_sender
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>()) .alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
@ -1328,6 +1472,78 @@ where
}; };
} }
} }
#[cfg(unix)]
ListenerStream::Unix(stream, addr) => unsafe {
dbg!("New connection", addr);
let broadcast_fd_initial: i32 =
CStr::from_ptr(broadcast_str_initial.as_ptr() as *const c_char)
.to_string_lossy()
.into_owned()
.parse()
.expect(&format!(
"ShmId is not a valid int file descriptor: {:?}",
broadcast_str_initial
));
match sendmsg(
stream.as_raw_fd(),
&[IoVec::from_slice(b"\x00")],
&[ControlMessage::ScmRights(&[broadcast_fd_initial])],
MsgFlags::empty(),
None,
) {
Ok(_) => {}
Err(err) => {
dbg!("Error sending fd over stream: {}", err);
continue;
}
};
let mut buf = [0u8; 5];
let mut cmsgspace = cmsg_space!([RawFd; 1]);
let msg = recvmsg(
stream.as_raw_fd(),
&[IoVec::from_mut_slice(&mut buf[..])],
Some(&mut cmsgspace),
MsgFlags::empty(),
)
.unwrap();
for cmsg in msg.cmsgs() {
if let ControlMessageOwned::ScmRights(fds) = cmsg {
for fd in fds {
let mut fdstr = [0u8; 20];
match write!(&mut fdstr[..], "{}", fd) {
Ok(_) => {}
Err(_) => {
dbg!("error converting fd to string");
}
}
let msg = new_client_sender
.alloc_next(size_of::<LlmpPayloadSharedMapInfo>())
.expect("Could not allocate a new message in shared map.");
(*msg).tag = LLMP_TAG_NEW_SHM_CLIENT;
let pageinfo =
(*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
(*pageinfo).shm_str = fdstr;
(*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
match new_client_sender.send(msg) {
Ok(()) => (),
Err(e) => {
println!("Error forwarding client on map: {:?}", e)
}
};
}
}
}
},
ListenerStream::Empty() => {
continue;
}
};
}
})) }))
} }
@ -1398,10 +1614,9 @@ where
let map = &mut self.llmp_clients[client_id as usize].current_recv_map; let map = &mut self.llmp_clients[client_id as usize].current_recv_map;
let msg_buf = (*msg).as_slice(map)?; let msg_buf = (*msg).as_slice(map)?;
match (on_new_msg)(client_id, (*msg).tag, msg_buf)? { if let LlmpMsgHookResult::Handled = (on_new_msg)(client_id, (*msg).tag, msg_buf)? {
LlmpMsgHookResult::Handled => should_forward_msg = false, should_forward_msg = false
_ => (), };
}
if should_forward_msg { if should_forward_msg {
self.forward_msg(msg)?; self.forward_msg(msg)?;
} }
@ -1410,6 +1625,24 @@ where
} }
} }
#[cfg(feature = "std")]
impl<SH> Drop for LlmpBroker<SH>
where
SH: ShMem,
{
fn drop(&mut self) {
match &self.socket_name {
Some(name) => match fs::remove_file(&name) {
Ok(_) => {}
Err(err) => {
dbg!("failed to close socket: {}", err);
}
},
None => {}
}
}
}
/// A restorable client description /// A restorable client description
#[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct LlmpClientDescription { pub struct LlmpClientDescription {
@ -1521,6 +1754,8 @@ where
} }
/// Commits a msg to the client's out map /// Commits a msg to the client's out map
/// # Safety
/// Needs to be called with a proper msg pointer
pub unsafe fn send(&mut self, msg: *mut LlmpMsg) -> Result<(), Error> { pub unsafe fn send(&mut self, msg: *mut LlmpMsg) -> Result<(), Error> {
self.sender.send(msg) self.sender.send(msg)
} }
@ -1551,6 +1786,8 @@ where
/// A client receives a broadcast message. /// A client receives a broadcast message.
/// Returns null if no message is availiable /// Returns null if no message is availiable
/// # Safety
/// Should be save, unless the internal state is corrupt. Returns raw ptr.
#[inline] #[inline]
pub unsafe fn recv(&mut self) -> Result<Option<*mut LlmpMsg>, Error> { pub unsafe fn recv(&mut self) -> Result<Option<*mut LlmpMsg>, Error> {
self.receiver.recv() self.receiver.recv()
@ -1558,6 +1795,8 @@ where
/// A client blocks/spins until the next message gets posted to the page, /// A client blocks/spins until the next message gets posted to the page,
/// then returns that message. /// then returns that message.
/// # Safety
/// Should be save, unless the internal state is corrupt. Returns raw ptr.
#[inline] #[inline]
pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, Error> { pub unsafe fn recv_blocking(&mut self) -> Result<*mut LlmpMsg, Error> {
self.receiver.recv_blocking() self.receiver.recv_blocking()
@ -1565,6 +1804,8 @@ where
/// The current page could have changed in recv (EOP) /// The current page could have changed in recv (EOP)
/// Alloc the next message, internally handling end of page by allocating a new one. /// Alloc the next message, internally handling end of page by allocating a new one.
/// # Safety
/// Should be safe, but returns an unsafe ptr
#[inline] #[inline]
pub unsafe fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, Error> { pub unsafe fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, Error> {
self.sender.alloc_next(buf_len) self.sender.alloc_next(buf_len)
@ -1602,28 +1843,93 @@ where
LLMP_PREF_INITIAL_MAP_SIZE, LLMP_PREF_INITIAL_MAP_SIZE,
)?))?; )?))?;
stream.write(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?; stream.write_all(ret.sender.out_maps.first().unwrap().shmem.shm_slice())?;
Ok(ret) Ok(ret)
} }
} }
/// `n` clients connect to a broker. They share an outgoing map with the broker,
/// and get incoming messages from the shared broker bus
/// If the Shm has a fd, we can attach to it.
impl<SH> LlmpClient<SH>
where
SH: ShMem + HasFd,
{
#[cfg(all(unix, feature = "std"))]
/// Create a LlmpClient, getting the ID from a given filename
pub fn create_attach_to_unix(filename: &str) -> Result<Self, Error> {
let stream = UnixStream::connect(filename)?;
println!("Connected to socket {}", filename);
let mut buf = [0u8; 5];
let mut cmsgspace = cmsg_space!([RawFd; 1]);
let msg = recvmsg(
stream.as_raw_fd(),
&[IoVec::from_mut_slice(&mut buf[..])],
Some(&mut cmsgspace),
MsgFlags::empty(),
)
.unwrap();
for cmsg in msg.cmsgs() {
if let ControlMessageOwned::ScmRights(fds) = cmsg {
for fd in fds {
let mut fdstr = [0u8; 20];
match write!(&mut fdstr[..], "{}", fd) {
Ok(_) => {}
Err(_) => {
dbg!("error converting fd to string");
}
}
let ret = Self::new(LlmpSharedMap::existing(SH::existing_from_shm_slice(
&fdstr,
LLMP_PREF_INITIAL_MAP_SIZE,
)?))?;
match sendmsg(
stream.as_raw_fd(),
&[IoVec::from_slice(b"\x00")],
&[ControlMessage::ScmRights(&[ret
.sender
.out_maps
.first()
.unwrap()
.shmem
.shm_id()])],
MsgFlags::empty(),
None,
) {
Ok(_) => {}
Err(err) => {
dbg!("Error sending fd over stream {}", err);
continue;
}
};
return Ok(ret);
}
}
}
panic!("Didn't receive a file descriptor from the broker!");
}
}
#[cfg(test)] #[cfg(test)]
#[cfg(all(unix, feature = "std"))]
mod tests { mod tests {
#[cfg(feature = "std")]
use std::{thread::sleep, time::Duration}; use std::{thread::sleep, time::Duration};
#[cfg(feature = "std")]
use super::{ use super::{
LlmpClient, LlmpClient,
LlmpConnection::{self, IsBroker, IsClient}, LlmpConnection::{self, IsBroker, IsClient},
LlmpMsgHookResult::ForwardToClients, LlmpMsgHookResult::ForwardToClients,
Tag, Tag,
}; };
#[cfg(feature = "std")]
use crate::bolts::shmem::UnixShMem; use crate::bolts::shmem::UnixShMem;
#[cfg(feature = "std")]
#[test] #[test]
pub fn llmp_connection() { pub fn llmp_connection() {
let mut broker = match LlmpConnection::<UnixShMem>::on_port(1337).unwrap() { let mut broker = match LlmpConnection::<UnixShMem>::on_port(1337).unwrap() {

View File

@ -2,6 +2,7 @@
pub mod bindings; pub mod bindings;
pub mod llmp; pub mod llmp;
pub mod os;
pub mod ownedref; pub mod ownedref;
pub mod serdeany; pub mod serdeany;
pub mod shmem; pub mod shmem;

View File

@ -0,0 +1,2 @@
#[cfg(unix)]
pub mod unix_signals;

View File

@ -0,0 +1,175 @@
use alloc::vec::Vec;
use core::{
cell::UnsafeCell,
convert::TryFrom,
fmt::{self, Display, Formatter},
mem, ptr,
ptr::write_volatile,
sync::atomic::{compiler_fence, Ordering},
};
#[cfg(feature = "std")]
use std::ffi::CString;
use libc::{
c_int, malloc, sigaction, sigaltstack, sigemptyset, stack_t, SA_NODEFER, SA_ONSTACK,
SA_SIGINFO, SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE,
SIGQUIT, SIGSEGV, SIGTERM, SIGUSR2,
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::Error;
pub use libc::{c_void, siginfo_t};
#[derive(IntoPrimitive, TryFromPrimitive, Hash, Clone, Copy)]
#[repr(i32)]
pub enum Signal {
SigAbort = SIGABRT,
SigBus = SIGBUS,
SigFloatingPointException = SIGFPE,
SigIllegalInstruction = SIGILL,
SigPipe = SIGPIPE,
SigSegmentationFault = SIGSEGV,
SigUser2 = SIGUSR2,
SigAlarm = SIGALRM,
SigHangUp = SIGHUP,
SigKill = SIGKILL,
SigQuit = SIGQUIT,
SigTerm = SIGTERM,
SigInterrupt = SIGINT,
}
pub static CRASH_SIGNALS: &[Signal] = &[
Signal::SigAbort,
Signal::SigBus,
Signal::SigFloatingPointException,
Signal::SigIllegalInstruction,
Signal::SigPipe,
Signal::SigSegmentationFault,
];
impl PartialEq for Signal {
fn eq(&self, other: &Self) -> bool {
*self as i32 == *other as i32
}
}
impl Eq for Signal {}
unsafe impl Sync for Signal {}
impl Display for Signal {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Signal::SigAbort => write!(f, "SIGABRT")?,
Signal::SigBus => write!(f, "SIGBUS")?,
Signal::SigFloatingPointException => write!(f, "SIGFPE")?,
Signal::SigIllegalInstruction => write!(f, "SIGILL")?,
Signal::SigPipe => write!(f, "SIGPIPE")?,
Signal::SigSegmentationFault => write!(f, "SIGSEGV")?,
Signal::SigUser2 => write!(f, "SIGUSR2")?,
Signal::SigAlarm => write!(f, "SIGALRM")?,
Signal::SigHangUp => write!(f, "SIGHUP")?,
Signal::SigKill => write!(f, "SIGKILL")?,
Signal::SigQuit => write!(f, "SIGQUIT")?,
Signal::SigTerm => write!(f, "SIGTERM")?,
Signal::SigInterrupt => write!(f, "SIGINT")?,
};
Ok(())
}
}
pub trait Handler {
/// Handle a signal
fn handle(&mut self, signal: Signal, info: siginfo_t, _void: c_void);
/// Return a list of signals to handle
fn signals(&self) -> Vec<Signal>;
}
struct HandlerHolder {
handler: UnsafeCell<*mut dyn Handler>,
}
unsafe impl Send for HandlerHolder {}
/// Let's get 8 mb for now.
const SIGNAL_STACK_SIZE: usize = 2 << 22;
/// To be able to handle SIGSEGV when the stack is exhausted, we need our own little stack space.
static mut SIGNAL_STACK_PTR: *mut c_void = ptr::null_mut();
/// Keep track of which handler is registered for which signal
static mut SIGNAL_HANDLERS: [Option<HandlerHolder>; 32] = [
// We cannot use [None; 32] because it requires Copy. Ugly, but I don't think there's an
// alternative.
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
];
/// Internal function that is being called whenever a signal we are registered for arrives.
/// # Safety
/// This should be somewhat safe to call for signals previously registered,
/// unless the signal handlers registered using [setup_signal_handler] are broken.
unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: c_void) {
let signal = &Signal::try_from(sig).unwrap();
let handler = {
match &SIGNAL_HANDLERS[*signal as usize] {
Some(handler_holder) => &mut **handler_holder.handler.get(),
None => return,
}
};
handler.handle(*signal, info, void);
}
/// Setup signal handlers in a somewhat rusty way.
/// This will allocate a signal stack and set the signal handlers accordingly.
/// It is, for example, used in the [crate::executors::InProcessExecutor] to restart the fuzzer in case of a crash,
/// or to handle `SIGINT` in the broker process.
/// # Safety
/// The signal handlers will be called on any signal. They should (tm) be async safe.
/// A lot can go south in signal handling. Be sure you know what you are doing.
pub unsafe fn setup_signal_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> {
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
if SIGNAL_STACK_PTR.is_null() {
SIGNAL_STACK_PTR = malloc(SIGNAL_STACK_SIZE);
if SIGNAL_STACK_PTR.is_null() {
// Rust always panics on OOM, so we will, too.
panic!(
"Failed to allocate signal stack with {} bytes!",
SIGNAL_STACK_SIZE
);
}
}
let mut ss: stack_t = mem::zeroed();
ss.ss_size = SIGNAL_STACK_SIZE;
ss.ss_sp = SIGNAL_STACK_PTR;
sigaltstack(&mut ss as *mut stack_t, ptr::null_mut() as _);
let mut sa: sigaction = mem::zeroed();
sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = handle_signal as usize;
let signals = handler.signals();
for sig in signals {
write_volatile(
&mut SIGNAL_HANDLERS[sig as usize],
Some(HandlerHolder {
handler: UnsafeCell::new(handler as *mut dyn Handler),
}),
);
if sigaction(sig as i32, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
#[cfg(feature = "std")]
{
let err_str = CString::new(format!("Failed to setup {} handler", sig)).unwrap();
libc::perror(err_str.as_ptr());
}
return Err(Error::Unknown(format!("Could not set up {} handler", sig)));
}
}
compiler_fence(Ordering::SeqCst);
Ok(())
}

View File

@ -31,8 +31,8 @@ where
} }
} }
impl<'a, T: Sized> Ptr<'a, T> { impl<'a, T: Sized> AsRef<T> for Ptr<'a, T> {
pub fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
match self { match self {
Ptr::Ref(r) => r, Ptr::Ref(r) => r,
Ptr::Owned(v) => v.as_ref(), Ptr::Owned(v) => v.as_ref(),
@ -69,15 +69,17 @@ where
} }
} }
impl<'a, T: Sized> PtrMut<'a, T> { impl<'a, T: Sized> AsRef<T> for PtrMut<'a, T> {
pub fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
match self { match self {
PtrMut::Ref(r) => r, PtrMut::Ref(r) => r,
PtrMut::Owned(v) => v.as_ref(), PtrMut::Owned(v) => v.as_ref(),
} }
} }
}
pub fn as_mut(&mut self) -> &T { impl<'a, T: Sized> AsMut<T> for PtrMut<'a, T> {
fn as_mut(&mut self) -> &mut T {
match self { match self {
PtrMut::Ref(r) => r, PtrMut::Ref(r) => r,
PtrMut::Owned(v) => v.as_mut(), PtrMut::Owned(v) => v.as_mut(),
@ -195,8 +197,8 @@ where
} }
} }
impl<T: Sized> Cptr<T> { impl<T: Sized> AsRef<T> for Cptr<T> {
pub fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
match self { match self {
Cptr::Cptr(p) => unsafe { p.as_ref().unwrap() }, Cptr::Cptr(p) => unsafe { p.as_ref().unwrap() },
Cptr::Owned(v) => v.as_ref(), Cptr::Owned(v) => v.as_ref(),
@ -230,15 +232,17 @@ where
} }
} }
impl<T: Sized> CptrMut<T> { impl<T: Sized> AsRef<T> for CptrMut<T> {
pub fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
match self { match self {
CptrMut::Cptr(p) => unsafe { p.as_ref().unwrap() }, CptrMut::Cptr(p) => unsafe { p.as_ref().unwrap() },
CptrMut::Owned(b) => b.as_ref(), CptrMut::Owned(b) => b.as_ref(),
} }
} }
}
pub fn as_mut(&mut self) -> &mut T { impl<T: Sized> AsMut<T> for CptrMut<T> {
fn as_mut(&mut self) -> &mut T {
match self { match self {
CptrMut::Cptr(p) => unsafe { p.as_mut().unwrap() }, CptrMut::Cptr(p) => unsafe { p.as_mut().unwrap() },
CptrMut::Owned(b) => b.as_mut(), CptrMut::Owned(b) => b.as_mut(),

View File

@ -71,7 +71,6 @@ macro_rules! create_serde_registry_for_trait {
pub mod $mod_name { pub mod $mod_name {
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::string::String;
use core::any::{Any, TypeId}; use core::any::{Any, TypeId};
use core::fmt; use core::fmt;
use postcard; use postcard;
@ -106,7 +105,7 @@ macro_rules! create_serde_registry_for_trait {
.get(&id) .get(&id)
.expect("Cannot deserialize an unregistered type") .expect("Cannot deserialize an unregistered type")
}; };
let seed = DeserializeCallbackSeed::<dyn $trait_name> { cb: cb }; let seed = DeserializeCallbackSeed::<dyn $trait_name> { cb };
let obj: Self::Value = visitor.next_element_seed(seed)?.unwrap(); let obj: Self::Value = visitor.next_element_seed(seed)?.unwrap();
Ok(obj) Ok(obj)
} }
@ -126,8 +125,7 @@ macro_rules! create_serde_registry_for_trait {
panic!("Registry is already finalized!"); panic!("Registry is already finalized!");
} }
let deserializers = let deserializers = self.deserializers.get_or_insert_with(HashMap::default);
self.deserializers.get_or_insert_with(|| HashMap::default());
deserializers.insert(unpack_type_id(TypeId::of::<T>()), |de| { deserializers.insert(unpack_type_id(TypeId::of::<T>()), |de| {
Ok(Box::new(erased_serde::deserialize::<T>(de)?)) Ok(Box::new(erased_serde::deserialize::<T>(de)?))
}); });
@ -260,7 +258,7 @@ macro_rules! create_serde_registry_for_trait {
impl NamedSerdeAnyMap { impl NamedSerdeAnyMap {
#[inline] #[inline]
pub fn get<T>(&self, name: &String) -> Option<&T> pub fn get<T>(&self, name: &str) -> Option<&T>
where where
T: Any, T: Any,
{ {
@ -273,11 +271,7 @@ macro_rules! create_serde_registry_for_trait {
} }
#[inline] #[inline]
pub fn by_typeid( pub fn by_typeid(&self, name: &str, typeid: &TypeId) -> Option<&dyn $trait_name> {
&self,
name: &String,
typeid: &TypeId,
) -> Option<&dyn $trait_name> {
match self.map.get(&unpack_type_id(*typeid)) { match self.map.get(&unpack_type_id(*typeid)) {
None => None, None => None,
Some(h) => h Some(h) => h
@ -287,7 +281,7 @@ macro_rules! create_serde_registry_for_trait {
} }
#[inline] #[inline]
pub fn get_mut<T>(&mut self, name: &String) -> Option<&mut T> pub fn get_mut<T>(&mut self, name: &str) -> Option<&mut T>
where where
T: Any, T: Any,
{ {
@ -302,7 +296,7 @@ macro_rules! create_serde_registry_for_trait {
#[inline] #[inline]
pub fn by_typeid_mut( pub fn by_typeid_mut(
&mut self, &mut self,
name: &String, name: &str,
typeid: &TypeId, typeid: &TypeId,
) -> Option<&mut dyn $trait_name> { ) -> Option<&mut dyn $trait_name> {
match self.map.get_mut(&unpack_type_id(*typeid)) { match self.map.get_mut(&unpack_type_id(*typeid)) {
@ -423,7 +417,7 @@ macro_rules! create_serde_registry_for_trait {
} }
#[inline] #[inline]
pub fn insert(&mut self, val: Box<dyn $trait_name>, name: &String) { pub fn insert(&mut self, val: Box<dyn $trait_name>, name: &str) {
let id = unpack_type_id((*val).type_id()); let id = unpack_type_id((*val).type_id());
if !self.map.contains_key(&id) { if !self.map.contains_key(&id) {
self.map.insert(id, HashMap::default()); self.map.insert(id, HashMap::default());
@ -448,7 +442,7 @@ macro_rules! create_serde_registry_for_trait {
} }
#[inline] #[inline]
pub fn contains<T>(&self, name: &String) -> bool pub fn contains<T>(&self, name: &str) -> bool
where where
T: Any, T: Any,
{ {

View File

@ -1,12 +1,10 @@
//! A generic sharememory region to be used by any functions (queues or feedbacks //! A generic sharememory region to be used by any functions (queues or feedbacks
// too.) // too.)
#[cfg(feature = "std")] #[cfg(all(feature = "std", unix))]
#[cfg(unix)] pub use unix_shmem::UnixShMem;
pub use shmem::UnixShMem;
#[cfg(feature = "std")] #[cfg(all(windows, feature = "std"))]
#[cfg(windows)]
pub use shmem::Win32ShMem; pub use shmem::Win32ShMem;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
@ -71,7 +69,7 @@ pub trait ShMem: Sized + Debug {
fn description(&self) -> ShMemDescription { fn description(&self) -> ShMemDescription {
ShMemDescription { ShMemDescription {
size: self.map().len(), size: self.map().len(),
str_bytes: self.shm_slice().clone(), str_bytes: *self.shm_slice(),
} }
} }
@ -99,17 +97,26 @@ pub trait ShMem: Sized + Debug {
} }
} }
#[cfg(unix)] /// shared maps that have an id can use this trait
#[cfg(feature = "std")] pub trait HasFd {
pub mod shmem { /// Retrieve the id of this shared map
fn shm_id(&self) -> i32;
}
use core::{mem::size_of, slice}; #[cfg(all(unix, feature = "std"))]
pub mod unix_shmem {
use core::{mem::size_of, ptr, slice};
use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void}; use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_ushort, c_void};
#[cfg(target_os = "android")]
use libc::{off_t, size_t, MAP_SHARED, O_RDWR, PROT_READ, PROT_WRITE};
use std::ffi::CStr; use std::ffi::CStr;
#[cfg(target_os = "android")]
use std::ffi::CString;
use crate::Error; use crate::Error;
use super::ShMem; use super::{HasFd, ShMem};
#[cfg(unix)] #[cfg(unix)]
extern "C" { extern "C" {
@ -117,12 +124,119 @@ pub mod shmem {
fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int; fn snprintf(_: *mut c_char, _: c_ulong, _: *const c_char, _: ...) -> c_int;
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char; fn strncpy(_: *mut c_char, _: *const c_char, _: c_ulong) -> *mut c_char;
#[cfg(feature = "std")] #[cfg(all(feature = "std", not(target_os = "android")))]
fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int; fn shmctl(__shmid: c_int, __cmd: c_int, __buf: *mut shmid_ds) -> c_int;
#[cfg(feature = "std")] #[cfg(all(feature = "std", not(target_os = "android")))]
fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int; fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int;
#[cfg(feature = "std")] #[cfg(all(feature = "std", not(target_os = "android")))]
fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void; fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void;
#[cfg(all(feature = "std", target_os = "android"))]
fn ioctl(fd: c_int, request: c_long, ...) -> c_int;
#[cfg(all(feature = "std", target_os = "android"))]
fn open(path: *const c_char, oflag: c_int, ...) -> c_int;
#[cfg(all(feature = "std", target_os = "android"))]
fn close(fd: c_int) -> c_int;
#[cfg(all(feature = "std", target_os = "android"))]
fn mmap(
addr: *mut c_void,
len: size_t,
prot: c_int,
flags: c_int,
fd: c_int,
offset: off_t,
) -> *mut c_void;
}
#[cfg(target_os = "android")]
#[derive(Copy, Clone)]
#[repr(C)]
struct ashmem_pin {
pub offset: c_uint,
pub len: c_uint,
}
#[cfg(target_os = "android")]
const ASHMEM_GET_SIZE: c_long = 0x00007704;
#[cfg(target_os = "android")]
const ASHMEM_UNPIN: c_long = 0x40087708;
#[cfg(target_os = "android")]
const ASHMEM_SET_NAME: c_long = 0x41007701;
#[cfg(target_os = "android")]
const ASHMEM_SET_SIZE: c_long = 0x40087703;
#[cfg(target_os = "android")]
const ASHMEM_DEVICE: &str = "/dev/ashmem";
#[cfg(target_os = "android")]
unsafe fn shmctl(__shmid: c_int, __cmd: c_int, _buf: *mut shmid_ds) -> c_int {
println!("shmctl(__shmid: {})", __shmid);
if __cmd == 0 {
let length = ioctl(__shmid, ASHMEM_GET_SIZE);
let ap = ashmem_pin {
offset: 0,
len: length as u32,
};
let ret = ioctl(__shmid, ASHMEM_UNPIN, &ap);
close(__shmid);
ret
} else {
0
}
}
#[cfg(target_os = "android")]
unsafe fn shmget(__key: c_int, __size: c_ulong, __shmflg: c_int) -> c_int {
let path = CString::new(ASHMEM_DEVICE).expect("CString::new failed!");
let fd = open(path.as_ptr(), O_RDWR);
let mut ourkey: [c_char; 20] = [0; 20];
snprintf(
ourkey.as_mut_ptr() as *mut c_char,
size_of::<[c_char; 20]>() as c_ulong,
b"%d\x00" as *const u8 as *const c_char,
__key,
);
println!("ourkey: {:?}", ourkey);
if ioctl(fd, ASHMEM_SET_NAME, &ourkey) != 0 {
close(fd);
return 0;
};
if ioctl(fd, ASHMEM_SET_SIZE, __size) != 0 {
close(fd);
return 0;
};
println!("shmget returns {}", fd);
fd
}
#[cfg(target_os = "android")]
unsafe fn shmat(__shmid: c_int, __shmaddr: *const c_void, __shmflg: c_int) -> *mut c_void {
println!("shmat(__shmid: {})", __shmid);
let size = ioctl(__shmid, ASHMEM_GET_SIZE);
if size < 0 {
return 0 as *mut c_void;
}
let ptr = mmap(
0 as *mut c_void,
size as usize,
PROT_READ | PROT_WRITE,
MAP_SHARED,
__shmid,
0,
);
if ptr == usize::MAX as *mut c_void {
return 0 as *mut c_void;
}
println!("shmat() = {:?}", ptr);
ptr
} }
#[cfg(unix)] #[cfg(unix)]
@ -197,6 +311,12 @@ pub mod shmem {
} }
} }
impl HasFd for UnixShMem {
fn shm_id(&self) -> i32 {
self.shm_id
}
}
/// Deinit sharedmaps on drop /// Deinit sharedmaps on drop
impl Drop for UnixShMem { impl Drop for UnixShMem {
fn drop(&mut self) { fn drop(&mut self) {
@ -222,7 +342,7 @@ pub mod shmem {
pub fn from_str(shm_str: &CStr, map_size: usize) -> Result<Self, Error> { pub fn from_str(shm_str: &CStr, map_size: usize) -> Result<Self, Error> {
let mut ret = afl_shmem_unitialized(); let mut ret = afl_shmem_unitialized();
let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) }; let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) };
if map != 0 as *mut u8 { if !map.is_null() {
Ok(ret) Ok(ret)
} else { } else {
Err(Error::Unknown(format!( Err(Error::Unknown(format!(
@ -235,7 +355,7 @@ pub mod shmem {
pub fn new(map_size: usize) -> Result<Self, Error> { pub fn new(map_size: usize) -> Result<Self, Error> {
let mut ret = afl_shmem_unitialized(); let mut ret = afl_shmem_unitialized();
let map = unsafe { afl_shmem_init(&mut ret, map_size) }; let map = unsafe { afl_shmem_init(&mut ret, map_size) };
if map != 0 as *mut u8 { if !map.is_null() {
Ok(ret) Ok(ret)
} else { } else {
Err(Error::Unknown(format!( Err(Error::Unknown(format!(
@ -253,24 +373,24 @@ pub mod shmem {
// Not set or not initialized; // Not set or not initialized;
return; return;
} }
(*shm).shm_str[0 as usize] = '\u{0}' as u8; (*shm).shm_str[0 as usize] = 0u8;
shmctl((*shm).shm_id, 0 as c_int, 0 as *mut shmid_ds); shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut());
(*shm).map = 0 as *mut c_uchar; (*shm).map = ptr::null_mut();
} }
/// Functions to create Shared memory region, for observation channels and /// Functions to create Shared memory region, for observation channels and
/// opening inputs and stuff. /// opening inputs and stuff.
unsafe fn afl_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { unsafe fn afl_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar {
(*shm).map_size = map_size; (*shm).map_size = map_size;
(*shm).map = 0 as *mut c_uchar; (*shm).map = ptr::null_mut();
(*shm).shm_id = shmget( (*shm).shm_id = shmget(
0 as c_int, 0 as c_int,
map_size as c_ulong, map_size as c_ulong,
0o1000 as c_int | 0o2000 as c_int | 0o600 as c_int, 0o1000 as c_int | 0o2000 as c_int | 0o600 as c_int,
); );
if (*shm).shm_id < 0 as c_int { if (*shm).shm_id < 0 as c_int {
(*shm).shm_str[0] = '\u{0}' as u8; (*shm).shm_str[0] = 0u8;
return 0 as *mut c_uchar; return ptr::null_mut();
} }
snprintf( snprintf(
(*shm).shm_str.as_mut_ptr() as *mut c_char, (*shm).shm_str.as_mut_ptr() as *mut c_char,
@ -280,15 +400,15 @@ pub mod shmem {
); );
(*shm).shm_str (*shm).shm_str
[(size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_int as c_ulong) as usize] = [(size_of::<[c_char; 20]>() as c_ulong).wrapping_sub(1 as c_int as c_ulong) as usize] =
'\u{0}' as u8; 0u8;
(*shm).map = shmat((*shm).shm_id, 0 as *const c_void, 0 as c_int) as *mut c_uchar; (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar;
if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar || (*shm).map.is_null() { if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar || (*shm).map.is_null() {
shmctl((*shm).shm_id, 0 as c_int, 0 as *mut shmid_ds); shmctl((*shm).shm_id, 0 as c_int, ptr::null_mut());
(*shm).shm_id = -(1 as c_int); (*shm).shm_id = -(1 as c_int);
(*shm).shm_str[0 as c_int as usize] = '\u{0}' as u8; (*shm).shm_str[0 as c_int as usize] = 0u8;
return 0 as *mut c_uchar; return ptr::null_mut();
} }
return (*shm).map; (*shm).map
} }
/// Uses a shmap id string to open a shared map /// Uses a shmap id string to open a shared map
@ -297,10 +417,10 @@ pub mod shmem {
shm_str: &CStr, shm_str: &CStr,
map_size: usize, map_size: usize,
) -> *mut c_uchar { ) -> *mut c_uchar {
if shm.is_null() || shm_str.to_bytes().len() == 0 || map_size == 0 { if shm.is_null() || shm_str.to_bytes().is_empty() || map_size == 0 {
return 0 as *mut c_uchar; return ptr::null_mut();
} }
(*shm).map = 0 as *mut c_uchar; (*shm).map = ptr::null_mut();
(*shm).map_size = map_size; (*shm).map_size = map_size;
strncpy( strncpy(
(*shm).shm_str.as_mut_ptr() as *mut c_char, (*shm).shm_str.as_mut_ptr() as *mut c_char,
@ -309,35 +429,30 @@ pub mod shmem {
); );
(*shm).shm_id = shm_str (*shm).shm_id = shm_str
.to_str() .to_str()
.expect(&format!("illegal shm_str {:?}", shm_str)) .unwrap_or_else(|_| panic!("illegal shm_str {:?}", shm_str))
.parse::<i32>() .parse::<i32>()
.unwrap(); .unwrap();
(*shm).map = shmat((*shm).shm_id, 0 as *const c_void, 0 as c_int) as *mut c_uchar; (*shm).map = shmat((*shm).shm_id, ptr::null(), 0 as c_int) as *mut c_uchar;
if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar { if (*shm).map == -(1 as c_int) as *mut c_void as *mut c_uchar {
(*shm).map = 0 as *mut c_uchar; (*shm).map = ptr::null_mut();
(*shm).map_size = 0; (*shm).map_size = 0;
(*shm).shm_str[0] = '\u{0}' as u8; (*shm).shm_str[0] = 0u8;
return 0 as *mut c_uchar; return ptr::null_mut();
} }
return (*shm).map; (*shm).map
} }
} }
#[cfg(windows)] #[cfg(all(feature = "std", windows))]
#[cfg(feature = "std")]
pub mod shmem { pub mod shmem {
use core::{mem::size_of, slice}; //TODO use super::ShMem;
use std::ffi::CStr;
use super::ShMem;
use crate::Error;
/// The default Sharedmap impl for windows using shmctl & shmget /// The default Sharedmap impl for windows using shmctl & shmget
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Win32ShMem { pub struct Win32ShMem {
pub filename: [u8; 64], pub filename: [u8; 64],
pub handle: windows::win32::system_services::HANDLE, //TODO pub handle: windows::win32::system_services::HANDLE,
pub map: *mut u8, pub map: *mut u8,
pub map_size: usize, pub map_size: usize,
} }
@ -348,10 +463,10 @@ pub mod shmem {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(feature = "std")] #[cfg(all(unix, feature = "std"))]
use super::{ShMem, UnixShMem}; use super::{ShMem, UnixShMem};
#[cfg(feature = "std")] #[cfg(all(unix, feature = "std"))]
#[test] #[test]
fn test_str_conversions() { fn test_str_conversions() {
let mut shm_str: [u8; 20] = [0; 20]; let mut shm_str: [u8; 20] = [0; 20];

View File

@ -6,6 +6,9 @@ use core::any::TypeId;
pub trait HasLen { pub trait HasLen {
fn len(&self) -> usize; fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
} }
impl HasLen for () { impl HasLen for () {
@ -65,12 +68,8 @@ pub trait MatchType {
} }
impl MatchType for () { impl MatchType for () {
fn match_type<T: 'static>(&self, _f: fn(t: &T)) { fn match_type<T: 'static>(&self, _f: fn(t: &T)) {}
() fn match_type_mut<T: 'static>(&mut self, _f: fn(t: &mut T)) {}
}
fn match_type_mut<T: 'static>(&mut self, _f: fn(t: &mut T)) {
()
}
} }
impl<Head, Tail> MatchType for (Head, Tail) impl<Head, Tail> MatchType for (Head, Tail)
@ -179,7 +178,7 @@ where
fn append(self, value: T) -> Self::AppendResult { fn append(self, value: T) -> Self::AppendResult {
let (head, tail) = self; let (head, tail) = self;
return (head, tail.append(value)); (head, tail.append(value))
} }
} }

View File

@ -37,6 +37,12 @@ impl TopRatedsMetadata {
} }
} }
impl Default for TopRatedsMetadata {
fn default() -> Self {
Self::new()
}
}
pub trait FavFactor<I> pub trait FavFactor<I>
where where
I: Input, I: Input,
@ -56,6 +62,7 @@ where
I: Input + HasLen, I: Input + HasLen,
{ {
fn compute(entry: &mut Testcase<I>) -> Result<u64, Error> { fn compute(entry: &mut Testcase<I>) -> Result<u64, Error> {
// TODO maybe enforce entry.exec_time().is_some()
Ok(entry.exec_time().map_or(1, |d| d.as_millis()) as u64 * entry.cached_len()? as u64) Ok(entry.exec_time().map_or(1, |d| d.as_millis()) as u64 * entry.cached_len()? as u64)
} }
} }
@ -208,7 +215,7 @@ where
pub fn new(base: CS) -> Self { pub fn new(base: CS) -> Self {
Self { Self {
base: base, base,
skip_not_fav_prob: DEFAULT_SKIP_NOT_FAV_PROB, skip_not_fav_prob: DEFAULT_SKIP_NOT_FAV_PROB,
phantom: PhantomData, phantom: PhantomData,
} }
@ -216,8 +223,8 @@ where
pub fn with_skip_prob(base: CS, skip_not_fav_prob: u64) -> Self { pub fn with_skip_prob(base: CS, skip_not_fav_prob: u64) -> Self {
Self { Self {
base: base, base,
skip_not_fav_prob: skip_not_fav_prob, skip_not_fav_prob,
phantom: PhantomData, phantom: PhantomData,
} }
} }

View File

@ -39,6 +39,11 @@ where
/// Returns the number of elements /// Returns the number of elements
fn count(&self) -> usize; fn count(&self) -> usize;
/// Returns true, if no elements are in this corpus yet
fn is_empty(&self) -> bool {
self.count() == 0
}
/// Add an entry to the corpus and return its index /// Add an entry to the corpus and return its index
fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error>; fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error>;
@ -128,6 +133,7 @@ where
I: Input, I: Input,
R: Rand, R: Rand,
{ {
/// Create a new RandCorpusScheduler that just schedules randomly.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
phantom: PhantomData, phantom: PhantomData,
@ -135,4 +141,16 @@ where
} }
} }
impl<C, I, R, S> Default for RandCorpusScheduler<C, I, R, S>
where
S: HasCorpus<C, I> + HasRand<R>,
C: Corpus<I>,
I: Input,
R: Rand,
{
fn default() -> Self {
Self::new()
}
}
pub type StdCorpusScheduler<C, I, R, S> = RandCorpusScheduler<C, I, R, S>; pub type StdCorpusScheduler<C, I, R, S> = RandCorpusScheduler<C, I, R, S>;

View File

@ -35,18 +35,15 @@ where
/// Add an entry to the corpus and return its index /// Add an entry to the corpus and return its index
#[inline] #[inline]
fn add(&mut self, mut testcase: Testcase<I>) -> Result<usize, Error> { fn add(&mut self, mut testcase: Testcase<I>) -> Result<usize, Error> {
match testcase.filename() { if testcase.filename().is_none() {
None => {
// TODO walk entry metadata to ask for pices of filename (e.g. :havoc in AFL) // TODO walk entry metadata to ask for pices of filename (e.g. :havoc in AFL)
let filename = self.dir_path.join(format!("id_{}", &self.entries.len())); let filename = self.dir_path.join(format!("id_{}", &self.entries.len()));
let filename_str = filename.to_str().expect("Invalid Path"); let filename_str = filename.to_str().expect("Invalid Path");
testcase.set_filename(filename_str.into()); testcase.set_filename(filename_str.into());
} };
_ => {}
}
testcase testcase
.store_input() .store_input()
.expect("Could not save testcase to disk".into()); .expect("Could not save testcase to disk");
self.entries.push(RefCell::new(testcase)); self.entries.push(RefCell::new(testcase));
Ok(self.entries.len() - 1) Ok(self.entries.len() - 1)
} }
@ -101,7 +98,7 @@ where
Ok(Self { Ok(Self {
entries: vec![], entries: vec![],
current: None, current: None,
dir_path: dir_path, dir_path,
}) })
} }
} }

View File

@ -59,48 +59,62 @@ where
} }
} }
/* impl<C, I, S> Default for QueueCorpusScheduler<C, I, S>
where
S: HasCorpus<C, I>,
C: Corpus<I>,
I: Input,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)] #[cfg(test)]
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod tests { mod tests {
use std::path::PathBuf; use std::{fs, path::PathBuf};
use crate::{ use crate::{
corpus::{Corpus, OnDiskCorpus, QueueCorpus, Testcase}, corpus::{Corpus, CorpusScheduler, OnDiskCorpus, QueueCorpusScheduler, Testcase},
inputs::bytes::BytesInput, inputs::bytes::BytesInput,
state::{HasCorpus, State},
utils::StdRand, utils::StdRand,
}; };
#[test] #[test]
fn test_queuecorpus() { fn test_queuecorpus() {
let mut rand = StdRand::new(0); let rand = StdRand::with_seed(4);
let mut q = QueueCorpus::new(OnDiskCorpus::<BytesInput, StdRand>::new(PathBuf::from( let scheduler = QueueCorpusScheduler::new();
"fancy/path",
))); let mut q =
let t = Testcase::with_filename(BytesInput::new(vec![0 as u8; 4]), "fancyfile".into()); OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/path")).unwrap();
q.add(t); let t = Testcase::with_filename(
let filename = q BytesInput::new(vec![0 as u8; 4]),
.next(&mut rand) "target/.test/fancy/path/fancyfile".into(),
);
q.add(t).unwrap();
let objective_q =
OnDiskCorpus::<BytesInput>::new(PathBuf::from("target/.test/fancy/objective/path"))
.unwrap();
let mut state = State::new(rand, q, (), objective_q, ());
let next_idx = scheduler.next(&mut state).unwrap();
let filename = state
.corpus()
.get(next_idx)
.unwrap() .unwrap()
.0
.borrow() .borrow()
.filename() .filename()
.as_ref() .as_ref()
.unwrap() .unwrap()
.to_owned(); .to_owned();
assert_eq!(
filename, assert_eq!(filename, "target/.test/fancy/path/fancyfile");
q.next(&mut rand)
.unwrap() fs::remove_dir_all("target/.test/fancy").unwrap();
.0
.borrow()
.filename()
.as_ref()
.unwrap()
.to_owned()
);
assert_eq!(filename, "fancyfile");
} }
} }
*/

View File

@ -181,9 +181,9 @@ where
#[inline] #[inline]
pub fn with_fitness(input: I, fitness: u32) -> Self { pub fn with_fitness(input: I, fitness: u32) -> Self {
Testcase { Testcase {
input: Some(input.into()), input: Some(input),
filename: None, filename: None,
fitness: fitness, fitness,
metadata: SerdeAnyMap::new(), metadata: SerdeAnyMap::new(),
exec_time: None, exec_time: None,
cached_len: None, cached_len: None,

View File

@ -1,4 +1,4 @@
use crate::bolts::llmp::LlmpSender; use crate::bolts::{llmp::LlmpSender, shmem::HasFd};
use alloc::{string::ToString, vec::Vec}; use alloc::{string::ToString, vec::Vec};
use core::{marker::PhantomData, time::Duration}; use core::{marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
@ -6,11 +6,13 @@ use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::bolts::llmp::LlmpReceiver; use crate::bolts::llmp::LlmpReceiver;
#[cfg(feature = "std")] #[cfg(all(feature = "std", windows))]
use std::{env, process::Command}; use crate::utils::startable_self;
#[cfg(feature = "std")] #[cfg(all(feature = "std", unix))]
#[cfg(unix)] use crate::utils::{fork, ForkResult};
#[cfg(all(feature = "std", unix))]
use crate::bolts::shmem::UnixShMem; use crate::bolts::shmem::UnixShMem;
use crate::{ use crate::{
bolts::{ bolts::{
@ -167,10 +169,7 @@ where
/// Returns if we are the broker /// Returns if we are the broker
pub fn is_broker(&self) -> bool { pub fn is_broker(&self) -> bool {
match self.llmp { matches!(self.llmp, llmp::LlmpConnection::IsBroker { broker: _ })
llmp::LlmpConnection::IsBroker { broker: _ } => true,
_ => false,
}
} }
/// Run forever in the broker /// Run forever in the broker
@ -194,6 +193,8 @@ where
}, },
Some(Duration::from_millis(5)), Some(Duration::from_millis(5)),
); );
Ok(())
} }
_ => Err(Error::IllegalState( _ => Err(Error::IllegalState(
"Called broker loop in the client".into(), "Called broker loop in the client".into(),
@ -283,15 +284,14 @@ where
let observers: OT = postcard::from_bytes(&observers_buf)?; let observers: OT = postcard::from_bytes(&observers_buf)?;
// TODO include ExitKind in NewTestcase // TODO include ExitKind in NewTestcase
let fitness = state.is_interesting(&input, &observers, ExitKind::Ok)?; let fitness = state.is_interesting(&input, &observers, ExitKind::Ok)?;
if fitness > 0 { if fitness > 0
if !state && state
.add_if_interesting(&input, fitness, scheduler)? .add_if_interesting(&input, fitness, scheduler)?
.is_none() .is_some()
{ {
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("Added received Testcase"); println!("Added received Testcase");
} }
}
Ok(()) Ok(())
} }
_ => Err(Error::Unknown(format!( _ => Err(Error::Unknown(format!(
@ -302,6 +302,23 @@ where
} }
} }
impl<I, S, SH, ST> LlmpEventManager<I, S, SH, ST>
where
I: Input,
S: IfInteresting<I>,
SH: ShMem + HasFd,
ST: Stats,
{
#[cfg(all(feature = "std", unix))]
pub fn new_on_domain_socket(stats: ST, filename: &str) -> Result<Self, Error> {
Ok(Self {
stats: Some(stats),
llmp: llmp::LlmpConnection::on_domain_socket(filename)?,
phantom: PhantomData,
})
}
}
impl<I, S, SH, ST> EventManager<I, S> for LlmpEventManager<I, S, SH, ST> impl<I, S, SH, ST> EventManager<I, S> for LlmpEventManager<I, S, SH, ST>
where where
I: Input, I: Input,
@ -312,13 +329,10 @@ where
/// The llmp client needs to wait until a broker mapped all pages, before shutting down. /// The llmp client needs to wait until a broker mapped all pages, before shutting down.
/// Otherwise, the OS may already have removed the shared maps, /// Otherwise, the OS may already have removed the shared maps,
fn await_restart_safe(&mut self) { fn await_restart_safe(&mut self) {
match &self.llmp { if let llmp::LlmpConnection::IsClient { client } = &self.llmp {
llmp::LlmpConnection::IsClient { client } => {
// wait until we can drop the message safely. // wait until we can drop the message safely.
client.await_save_to_unmap_blocking(); client.await_save_to_unmap_blocking();
} }
_ => (),
}
} }
fn process<CS, E, OT>( fn process<CS, E, OT>(
@ -335,18 +349,15 @@ where
// TODO: Get around local event copy by moving handle_in_client // TODO: Get around local event copy by moving handle_in_client
let mut events = vec![]; let mut events = vec![];
match &mut self.llmp { match &mut self.llmp {
llmp::LlmpConnection::IsClient { client } => loop { llmp::LlmpConnection::IsClient { client } => {
match client.recv_buf()? { while let Some((sender_id, tag, msg)) = client.recv_buf()? {
Some((sender_id, tag, msg)) => {
if tag == _LLMP_TAG_EVENT_TO_BROKER { if tag == _LLMP_TAG_EVENT_TO_BROKER {
continue; continue;
} }
let event: Event<I> = postcard::from_bytes(msg)?; let event: Event<I> = postcard::from_bytes(msg)?;
events.push((sender_id, event)); events.push((sender_id, event));
} }
None => break,
} }
},
_ => { _ => {
#[cfg(feature = "std")] #[cfg(feature = "std")]
dbg!("Skipping process in broker"); dbg!("Skipping process in broker");
@ -498,18 +509,31 @@ pub fn setup_restarting_mgr<I, S, SH, ST>(
where where
I: Input, I: Input,
S: DeserializeOwned + IfInteresting<I>, S: DeserializeOwned + IfInteresting<I>,
SH: ShMem, SH: ShMem + HasFd, // Todo: HasFd is only needed for Android
ST: Stats, ST: Stats,
{ {
let mut mgr; let mut mgr;
// We start ourself as child process to actually fuzz // We start ourself as child process to actually fuzz
if std::env::var(_ENV_FUZZER_SENDER).is_err() { let (sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_port(stats, broker_port)?; #[cfg(target_os = "android")]
{
let path = std::env::current_dir()?;
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_domain_socket(
stats,
&format!("{}/.llmp_socket", path.display()).to_string(),
)?;
};
#[cfg(not(target_os = "android"))]
{
mgr = LlmpEventManager::<I, S, SH, ST>::new_on_port(stats, broker_port)?
};
if mgr.is_broker() { if mgr.is_broker() {
// Yep, broker. Just loop here. // Yep, broker. Just loop here.
println!("Doing broker things. Run this tool again to start fuzzing in a client."); println!("Doing broker things. Run this tool again to start fuzzing in a client.");
mgr.broker_loop()?; mgr.broker_loop()?;
return Err(Error::ShuttingDown);
} else { } else {
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL); mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
@ -527,22 +551,32 @@ where
// Client->parent loop // Client->parent loop
loop { loop {
dbg!("Spawning next client (id {})", ctr); dbg!("Spawning next client (id {})", ctr);
Command::new(env::current_exe()?)
.current_dir(env::current_dir()?) // On Unix, we fork (todo: measure if that is actually faster.)
.args(env::args()) #[cfg(unix)]
.status()?; let _ = match unsafe { fork() }? {
ForkResult::Parent(handle) => handle.status(),
ForkResult::Child => break (sender, receiver),
};
// On windows, we spawn ourself again
#[cfg(windows)]
startable_self()?.status()?;
ctr += 1; ctr += 1;
} }
} }
} } else {
// We are the newly started fuzzing instance, first, connect to our own restore map.
// A sender and a receiver for single communication
(
LlmpSender::<SH>::on_existing_from_env(_ENV_FUZZER_SENDER)?,
LlmpReceiver::<SH>::on_existing_from_env(_ENV_FUZZER_RECEIVER)?,
)
};
println!("We're a client, let's fuzz :)"); println!("We're a client, let's fuzz :)");
// We are the fuzzing instance, first, connect to our own restore map.
// A sender and a receiver for single communication
let mut receiver = LlmpReceiver::<SH>::on_existing_from_env(_ENV_FUZZER_RECEIVER)?;
let sender = LlmpSender::<SH>::on_existing_from_env(_ENV_FUZZER_SENDER)?;
// If we're restarting, deserialize the old state. // If we're restarting, deserialize the old state.
let (state, mut mgr) = match receiver.recv_buf()? { let (state, mut mgr) = match receiver.recv_buf()? {
None => { None => {

View File

@ -43,7 +43,7 @@ where
OT: ObserversTuple, OT: ObserversTuple,
{ {
let count = self.events.len(); let count = self.events.len();
while self.events.len() > 0 { while !self.events.is_empty() {
let event = self.events.pop().unwrap(); let event = self.events.pop().unwrap();
self.handle_in_client(state, event)?; self.handle_in_client(state, event)?;
} }
@ -66,7 +66,7 @@ where
{ {
pub fn new(stats: ST) -> Self { pub fn new(stats: ST) -> Self {
Self { Self {
stats: stats, stats,
events: vec![], events: vec![],
phantom: PhantomData, phantom: PhantomData,
} }
@ -118,11 +118,9 @@ where
// Handle arriving events in the client // Handle arriving events in the client
fn handle_in_client(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> { fn handle_in_client(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
match event { Err(Error::Unknown(format!(
_ => Err(Error::Unknown(format!(
"Received illegal message that message should not have arrived: {:?}.", "Received illegal message that message should not have arrived: {:?}.",
event event
))), )))
}
} }
} }

View File

@ -2,10 +2,14 @@
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective. //! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
use core::marker::PhantomData; use core::marker::PhantomData;
#[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
use os_signals::set_oncrash_ptrs; use core::{
ptr::{self, write_volatile},
sync::atomic::{compiler_fence, Ordering},
};
#[cfg(unix)]
use crate::bolts::os::unix_signals::{c_void, setup_signal_handler};
use crate::{ use crate::{
bolts::tuples::Named, bolts::tuples::Named,
corpus::Corpus, corpus::Corpus,
@ -18,17 +22,6 @@ use crate::{
Error, Error,
}; };
#[cfg(feature = "std")]
#[cfg(unix)]
use unix_signals as os_signals;
#[cfg(feature = "std")]
#[cfg(unix)]
use self::os_signals::reset_oncrash_ptrs;
#[cfg(feature = "std")]
#[cfg(unix)]
use self::os_signals::setup_crash_handlers;
/// The inmem executor harness /// The inmem executor harness
type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind; type HarnessFunction<E> = fn(&E, &[u8]) -> ExitKind;
@ -58,27 +51,19 @@ where
_state: &mut S, _state: &mut S,
_event_mgr: &mut EM, _event_mgr: &mut EM,
_input: &I, _input: &I,
) -> Result<(), Error> ) -> Result<(), Error> {
where
EM: EventManager<I, S>,
{
#[cfg(unix)] #[cfg(unix)]
#[cfg(feature = "std")]
unsafe { unsafe {
set_oncrash_ptrs(_state, _event_mgr, self.observers(), _input); let data = &mut unix_signal_handler::GLOBAL_STATE;
} write_volatile(
Ok(()) &mut data.current_input_ptr,
} _input as *const _ as *const c_void,
);
#[inline] // Direct raw pointers access /aliasing is pretty undefined behavior.
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> // Since the state and event may have moved in memory, refresh them right before the signal may happen
where write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void);
EM: EventManager<I, S>, write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void);
{ compiler_fence(Ordering::SeqCst);
#[cfg(unix)]
#[cfg(feature = "std")]
unsafe {
reset_oncrash_ptrs();
} }
Ok(()) Ok(())
} }
@ -87,6 +72,14 @@ where
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> { fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
let bytes = input.target_bytes(); let bytes = input.target_bytes();
let ret = (self.harness_fn)(self, bytes.as_slice()); let ret = (self.harness_fn)(self, bytes.as_slice());
#[cfg(unix)]
unsafe {
write_volatile(
&mut unix_signal_handler::GLOBAL_STATE.current_input_ptr,
ptr::null(),
);
compiler_fence(Ordering::SeqCst);
}
Ok(ret) Ok(ret)
} }
} }
@ -124,100 +117,274 @@ where
{ {
/// Create a new in mem executor. /// Create a new in mem executor.
/// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used,
/// depnding on different corpus or state. /// depending on different corpus or state.
/// * `name` - the name of this executor (to address it along the way) /// * `name` - the name of this executor (to address it along the way)
/// * `harness_fn` - the harness, executiong the function /// * `harness_fn` - the harness, executiong the function
/// * `observers` - the observers observing the target during execution /// * `observers` - the observers observing the target during execution
/// This may return an error on unix, if signal handler setup fails
pub fn new<EM, OC, OFT, S>( pub fn new<EM, OC, OFT, S>(
name: &'static str, name: &'static str,
harness_fn: HarnessFunction<Self>, harness_fn: HarnessFunction<Self>,
observers: OT, observers: OT,
_state: &mut S, _state: &mut S,
_event_mgr: &mut EM, _event_mgr: &mut EM,
) -> Self ) -> Result<Self, Error>
where where
EM: EventManager<I, S>, EM: EventManager<I, S>,
OC: Corpus<I>, OC: Corpus<I>,
OFT: FeedbacksTuple<I>, OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>, S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
{ {
#[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
unsafe { unsafe {
setup_crash_handlers::<EM, I, OC, OFT, OT, S>(); let data = &mut unix_signal_handler::GLOBAL_STATE;
write_volatile(
&mut data.observers_ptr,
&observers as *const _ as *const c_void,
);
write_volatile(
&mut data.crash_handler,
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
);
write_volatile(
&mut data.timeout_handler,
unix_signal_handler::inproc_timeout_handler::<EM, I, OC, OFT, OT, S>,
);
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
} }
Self { Ok(Self {
harness_fn, harness_fn,
observers, observers,
name, name,
phantom: PhantomData, phantom: PhantomData,
} })
} }
} }
#[cfg(feature = "std")]
#[cfg(unix)] #[cfg(unix)]
pub mod unix_signals { mod unix_signal_handler {
use alloc::vec::Vec;
extern crate libc; use core::ptr;
use libc::{c_void, siginfo_t};
// Unhandled signals: SIGALRM, SIGHUP, SIGINT, SIGKILL, SIGQUIT, SIGTERM #[cfg(feature = "std")]
use libc::{
c_int, c_void, malloc, sigaction, sigaltstack, siginfo_t, SA_NODEFER, SA_ONSTACK,
SA_SIGINFO, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV, SIGUSR2,
};
use std::{ use std::{
fs, fs,
io::{stdout, Write}, io::{stdout, Write},
mem, ptr,
}; };
use crate::{ use crate::{
bolts::os::unix_signals::{Handler, Signal},
corpus::{Corpus, Testcase}, corpus::{Corpus, Testcase},
events::{Event, EventManager}, events::{Event, EventManager},
executors::ExitKind, executors::ExitKind,
feedbacks::FeedbacksTuple, feedbacks::FeedbacksTuple,
inputs::Input, inputs::{HasTargetBytes, Input},
observers::ObserversTuple, observers::ObserversTuple,
state::{HasObjectives, HasSolutions}, state::{HasObjectives, HasSolutions},
}; };
/// Let's get 8 mb for now.
const SIGNAL_STACK_SIZE: usize = 2 << 22;
/// To be able to handle SIGSEGV when the stack is exhausted, we need our own little stack space.
static mut SIGNAL_STACK_PTR: *const c_void = ptr::null_mut();
/// Pointers to values only needed on crash. As the program will not continue after a crash, /// Signal handling on unix systems needs some nasty unsafe.
/// we should (tm) be okay with raw pointers here, pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
static mut STATE_PTR: *mut c_void = ptr::null_mut(); /// The state ptr for signal handling
static mut EVENT_MGR_PTR: *mut c_void = ptr::null_mut(); state_ptr: ptr::null_mut(),
static mut OBSERVERS_PTR: *const c_void = ptr::null(); /// The event manager ptr for signal handling
/// The (unsafe) pointer to the current inmem input, for the current run. event_mgr_ptr: ptr::null_mut(),
/// This is needed for certain non-rust side effects, as well as unix signal handling. /// The observers ptr for signal handling
static mut CURRENT_INPUT_PTR: *const c_void = ptr::null(); observers_ptr: ptr::null(),
/// The current input for signal handling
current_input_ptr: ptr::null(),
/// The crash handler fn
crash_handler: nop_handler,
/// The timeout handler fn
timeout_handler: nop_handler,
};
unsafe fn inmem_handle_crash<EM, I, OC, OFT, OT, S>(_sig: c_int, info: siginfo_t, _void: c_void) pub struct InProcessExecutorHandlerData {
where pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: unsafe fn(Signal, siginfo_t, c_void, data: &mut Self),
pub timeout_handler: unsafe fn(Signal, siginfo_t, c_void, data: &mut Self),
}
unsafe impl Send for InProcessExecutorHandlerData {}
unsafe impl Sync for InProcessExecutorHandlerData {}
unsafe fn nop_handler(
_signal: Signal,
_info: siginfo_t,
_void: c_void,
_data: &mut InProcessExecutorHandlerData,
) {
}
#[cfg(unix)]
impl Handler for InProcessExecutorHandlerData {
fn handle(&mut self, signal: Signal, info: siginfo_t, void: c_void) {
unsafe {
let data = &mut GLOBAL_STATE;
match signal {
Signal::SigUser2 => (data.timeout_handler)(signal, info, void, data),
_ => (data.crash_handler)(signal, info, void, data),
}
}
}
fn signals(&self) -> Vec<Signal> {
vec![
Signal::SigUser2,
Signal::SigAbort,
Signal::SigBus,
Signal::SigPipe,
Signal::SigFloatingPointException,
Signal::SigIllegalInstruction,
Signal::SigSegmentationFault,
]
}
}
#[cfg(unix)]
pub unsafe fn inproc_timeout_handler<EM, I, OC, OFT, OT, S>(
_signal: Signal,
_info: siginfo_t,
_void: c_void,
data: &mut InProcessExecutorHandlerData,
) where
EM: EventManager<I, S>, EM: EventManager<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
OC: Corpus<I>, OC: Corpus<I>,
OFT: FeedbacksTuple<I>, OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>, S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input, I: Input + HasTargetBytes,
{ {
if CURRENT_INPUT_PTR == ptr::null() { let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
if data.current_input_ptr.is_null() {
#[cfg(feature = "std")]
dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting");
} else {
#[cfg(feature = "std")]
println!("Timeout in fuzz run.");
#[cfg(feature = "std")]
let _ = stdout().flush();
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
data.current_input_ptr = ptr::null();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In timeout handler objectives failure.");
if obj_fitness > 0 {
state
.solutions_mut()
.add(Testcase::new(input.clone()))
.expect("In timeout handler solutions failure.");
event_mgr
.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)
.expect("Could not send timeouting input");
}
event_mgr.on_restart(state).unwrap();
#[cfg(feature = "std")]
println!("Waiting for broker...");
event_mgr.await_restart_safe();
#[cfg(feature = "std")]
println!("Bye!");
event_mgr.await_restart_safe();
libc::_exit(1);
}
}
pub unsafe fn inproc_crash_handler<EM, I, OC, OFT, OT, S>(
_signal: Signal,
_info: siginfo_t,
_void: c_void,
data: &mut InProcessExecutorHandlerData,
) where
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input + HasTargetBytes,
{
#[cfg(feature = "std")]
println!("Crashed with {}", _signal);
if !data.current_input_ptr.is_null() {
let state = (data.state_ptr as *mut S).as_mut().unwrap();
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let observers = (data.observers_ptr as *const OT).as_ref().unwrap();
#[cfg(feature = "std")]
println!("Child crashed!");
#[cfg(feature = "std")]
let _ = stdout().flush();
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
// Make sure we don't crash in the crash handler forever.
data.current_input_ptr = ptr::null();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In crash handler objectives failure.");
if obj_fitness > 0 {
let new_input = input.clone();
state
.solutions_mut()
.add(Testcase::new(new_input))
.expect("In crash handler solutions failure.");
event_mgr
.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)
.expect("Could not send crashing input");
}
event_mgr.on_restart(state).unwrap();
#[cfg(feature = "std")]
println!("Waiting for broker...");
event_mgr.await_restart_safe();
#[cfg(feature = "std")]
println!("Bye!");
libc::_exit(1);
} else {
#[cfg(feature = "std")]
{
println!("Double crash\n");
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
let si_addr = { ((info._pad[0] as usize) | ((info._pad[1] as usize) << 32)) as usize }; let si_addr =
{ ((_info._pad[0] as usize) | ((_info._pad[1] as usize) << 32)) as usize };
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
let si_addr = { info.si_addr() as usize }; let si_addr = { _info.si_addr() as usize };
println!( println!(
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.", "We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
si_addr si_addr
); );
}
// let's yolo-cat the maps for debugging, if possible. // let's yolo-cat the maps for debugging, if possible.
#[cfg(target_os = "linux")] #[cfg(all(target_os = "linux", feature = "std"))]
match fs::read_to_string("/proc/self/maps") { match fs::read_to_string("/proc/self/maps") {
Ok(maps) => println!("maps:\n{}", maps), Ok(maps) => println!("maps:\n{}", maps),
Err(e) => println!("Couldn't load mappings: {:?}", e), Err(e) => println!("Couldn't load mappings: {:?}", e),
@ -232,167 +399,7 @@ pub mod unix_signals {
} }
// TODO tell the parent to not restart // TODO tell the parent to not restart
std::process::exit(1); libc::_exit(1);
}
#[cfg(feature = "std")]
println!("Child crashed!");
#[cfg(feature = "std")]
let _ = stdout().flush();
let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap();
// Make sure we don't crash in the crash handler forever.
CURRENT_INPUT_PTR = ptr::null();
let state = (STATE_PTR as *mut S).as_mut().unwrap();
let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap();
let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In crash handler objectives failure.".into());
if obj_fitness > 0 {
state
.solutions_mut()
.add(Testcase::new(input.clone()))
.expect("In crash handler solutions failure.".into());
mgr.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)
.expect("Could not send crashing input".into());
}
mgr.on_restart(state).unwrap();
println!("Waiting for broker...");
mgr.await_restart_safe();
println!("Bye!");
std::process::exit(1);
}
unsafe fn inmem_handle_timeout<EM, I, OC, OFT, OT, S>(
_sig: c_int,
_info: siginfo_t,
_void: c_void,
) where
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input,
{
dbg!("TIMEOUT/SIGUSR2 received");
if CURRENT_INPUT_PTR.is_null() {
dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting");
return;
}
println!("Timeout in fuzz run.");
let _ = stdout().flush();
let input = (CURRENT_INPUT_PTR as *const I).as_ref().unwrap();
// Make sure we don't crash in the crash handler forever.
CURRENT_INPUT_PTR = ptr::null();
let state = (STATE_PTR as *mut S).as_mut().unwrap();
let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap();
let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, ExitKind::Crash)
.expect("In timeout handler objectives failure.".into());
if obj_fitness > 0 {
state
.solutions_mut()
.add(Testcase::new(input.clone()))
.expect("In timeout handler solutions failure.".into());
mgr.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)
.expect("Could not send timeouting input".into());
}
mgr.on_restart(state).unwrap();
println!("Waiting for broker...");
mgr.await_restart_safe();
println!("Bye!");
mgr.await_restart_safe();
std::process::exit(1);
}
#[inline]
pub unsafe fn set_oncrash_ptrs<EM, I, OT, S>(
state: &mut S,
event_mgr: &mut EM,
observers: &OT,
input: &I,
) {
CURRENT_INPUT_PTR = input as *const _ as *const c_void;
STATE_PTR = state as *mut _ as *mut c_void;
EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void;
OBSERVERS_PTR = observers as *const _ as *const c_void;
}
#[inline]
pub unsafe fn reset_oncrash_ptrs() {
CURRENT_INPUT_PTR = ptr::null();
STATE_PTR = ptr::null_mut();
EVENT_MGR_PTR = ptr::null_mut();
OBSERVERS_PTR = ptr::null();
}
pub unsafe fn setup_crash_handlers<EM, I, OC, OFT, OT, S>()
where
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
I: Input,
{
// First, set up our own stack to be used during segfault handling. (and specify `SA_ONSTACK` in `sigaction`)
if SIGNAL_STACK_PTR.is_null() {
SIGNAL_STACK_PTR = malloc(SIGNAL_STACK_SIZE);
if SIGNAL_STACK_PTR.is_null() {
panic!(
"Failed to allocate signal stack with {} bytes!",
SIGNAL_STACK_SIZE
);
}
}
sigaltstack(SIGNAL_STACK_PTR as _, ptr::null_mut() as _);
let mut sa: sigaction = mem::zeroed();
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = inmem_handle_crash::<EM, I, OC, OFT, OT, S> as usize;
for (sig, msg) in &[
(SIGSEGV, "segfault"),
(SIGBUS, "sigbus"),
(SIGABRT, "sigabrt"),
(SIGILL, "illegal instruction"),
(SIGFPE, "fp exception"),
(SIGPIPE, "pipe"),
] {
if sigaction(*sig, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
panic!("Could not set up {} handler", &msg);
}
}
sa.sa_sigaction = inmem_handle_timeout::<EM, I, OC, OFT, OT, S> as usize;
if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
panic!("Could not set up sigusr2 handler for timeouts");
} }
} }
} }

View File

@ -9,7 +9,7 @@ use core::cmp::PartialEq;
use core::marker::PhantomData; use core::marker::PhantomData;
use crate::{ use crate::{
bolts::tuples::{MatchNameAndType, MatchType, Named, TupleList}, bolts::tuples::Named,
events::EventManager, events::EventManager,
inputs::{HasTargetBytes, Input}, inputs::{HasTargetBytes, Input},
observers::ObserversTuple, observers::ObserversTuple,
@ -59,7 +59,7 @@ where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
{ {
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> { fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
if input.target_bytes().as_slice().len() == 0 { if input.target_bytes().as_slice().is_empty() {
Err(Error::Empty("Input Empty".into())) Err(Error::Empty("Input Empty".into()))
} else { } else {
Ok(ExitKind::Ok) Ok(ExitKind::Ok)
@ -78,8 +78,8 @@ pub trait Executor<I>: Named
where where
I: Input, I: Input,
{ {
#[inline]
/// Called right before exexution starts /// Called right before exexution starts
#[inline]
fn pre_exec<EM, S>( fn pre_exec<EM, S>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
@ -92,8 +92,8 @@ where
Ok(()) Ok(())
} }
#[inline]
/// Called right after execution finished. /// Called right after execution finished.
#[inline]
fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> fn post_exec<EM, S>(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error>
where where
EM: EventManager<I, S>, EM: EventManager<I, S>,
@ -105,39 +105,6 @@ where
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error>; fn run_target(&mut self, input: &I) -> Result<ExitKind, Error>;
} }
pub trait ExecutorsTuple<I>: MatchType + MatchNameAndType
where
I: Input,
{
//fn for_each(&self, f: fn(&dyn Executor<I>));
//fn for_each_mut(&mut self, f: fn(&mut dyn Executor<I>));
}
impl<I> ExecutorsTuple<I> for ()
where
I: Input,
{
//fn for_each(&self, _f: fn(&dyn Executor<I>)) {}
//fn for_each_mut(&mut self, _f: fn(&mut dyn Executor<I>)) {}
}
impl<Head, Tail, I> ExecutorsTuple<I> for (Head, Tail)
where
Head: Executor<I> + 'static,
Tail: ExecutorsTuple<I> + TupleList,
I: Input,
{
/*fn for_each(&self, f: fn(&dyn Executor<I>)) {
f(&self.0);
self.1.for_each(f)
}
fn for_each_mut(&mut self, f: fn(&mut dyn Executor<I>)) {
f(&mut self.0);
self.1.for_each_mut(f)
}*/
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use core::marker::PhantomData; use core::marker::PhantomData;

View File

@ -218,19 +218,13 @@ where
} }
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> { fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
match self.indexes.as_mut() { if let Some(v) = self.indexes.as_mut() {
Some(v) => {
let meta = MapIndexesMetadata::new(core::mem::take(v)); let meta = MapIndexesMetadata::new(core::mem::take(v));
testcase.add_metadata(meta); testcase.add_metadata(meta);
}
None => {}
}; };
match self.novelties.as_mut() { if let Some(v) = self.novelties.as_mut() {
Some(v) => {
let meta = MapNoveltiesMetadata::new(core::mem::take(v)); let meta = MapNoveltiesMetadata::new(core::mem::take(v));
testcase.add_metadata(meta); testcase.add_metadata(meta);
}
None => {}
}; };
Ok(()) Ok(())
} }
@ -329,7 +323,7 @@ where
/// The map can be shared. /// The map can be shared.
pub fn with_history_map(name: &'static str, history_map: Vec<T>) -> Self { pub fn with_history_map(name: &'static str, history_map: Vec<T>) -> Self {
Self { Self {
history_map: history_map, history_map,
name: name.to_string(), name: name.to_string(),
indexes: None, indexes: None,
novelties: None, novelties: None,

View File

@ -11,10 +11,12 @@ use crate::{
corpus::Testcase, corpus::Testcase,
executors::ExitKind, executors::ExitKind,
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::{ObserversTuple, TimeObserver},
Error, Error,
}; };
use core::time::Duration;
/// Feedbacks evaluate the observers. /// Feedbacks evaluate the observers.
/// Basically, they reduce the information provided by an observer to a value, /// Basically, they reduce the information provided by an observer to a value,
/// indicating the "interestingness" of the last run. /// indicating the "interestingness" of the last run.
@ -41,30 +43,6 @@ where
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> { fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
Ok(()) Ok(())
} }
/*
/// Serialize this feedback's state only, to be restored later using deserialize_state
/// As opposed to completely serializing the observer, this is only needed when the fuzzer is to be restarted
/// If no state is needed to be kept, just return an empty vec.
/// Example:
/// >> The virgin_bits map in AFL needs to be in sync with the corpus
#[inline]
fn serialize_state(&mut self) -> Result<Vec<u8>, Error> {
Ok(vec![])
}
/// Restore the state from a given vec, priviously stored using `serialize_state`
#[inline]
fn deserialize_state(&mut self, serialized_state: &[u8]) -> Result<(), Error> {
let _ = serialized_state;
Ok(())
}
// TODO: Restore_from
fn restore_from(&mut self, restore_from: Self) -> Result<(), Error> {
Ok(())
}
*/
} }
pub trait FeedbacksTuple<I>: serde::Serialize + serde::de::DeserializeOwned pub trait FeedbacksTuple<I>: serde::Serialize + serde::de::DeserializeOwned
@ -84,12 +62,6 @@ where
/// Discards metadata - the end of this input's execution /// Discards metadata - the end of this input's execution
fn discard_metadata_all(&mut self, input: &I) -> Result<(), Error>; fn discard_metadata_all(&mut self, input: &I) -> Result<(), Error>;
/*
/// Restores the state from each of the containing feedbacks in a list of the same shape.
/// Used (prette exclusively) to restore the feedback states after a crash.
fn restore_state_from_all(&mut self, restore_from: &Self) -> Result<(), Error>;
*/
} }
impl<I> FeedbacksTuple<I> for () impl<I> FeedbacksTuple<I> for ()
@ -115,12 +87,6 @@ where
fn discard_metadata_all(&mut self, _input: &I) -> Result<(), Error> { fn discard_metadata_all(&mut self, _input: &I) -> Result<(), Error> {
Ok(()) Ok(())
} }
/*
fn restore_state_from_all(&mut self, restore_from: &Self) -> Result<(), Error> {
Ok(())
}
*/
} }
impl<Head, Tail, I> FeedbacksTuple<I> for (Head, Tail) impl<Head, Tail, I> FeedbacksTuple<I> for (Head, Tail)
@ -184,3 +150,65 @@ impl CrashFeedback {
Self {} Self {}
} }
} }
impl Default for CrashFeedback {
fn default() -> Self {
Self::new()
}
}
/// Nop feedback that annotates execution time in the new testcase, if any
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeFeedback {
exec_time: Option<Duration>,
}
impl<I> Feedback<I> for TimeFeedback
where
I: Input,
{
fn is_interesting<OT: ObserversTuple>(
&mut self,
_input: &I,
observers: &OT,
_exit_kind: ExitKind,
) -> Result<u32, Error> {
let observer = observers.match_first_type::<TimeObserver>().unwrap();
self.exec_time = *observer.last_runtime();
Ok(0)
}
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
*testcase.exec_time_mut() = self.exec_time;
self.exec_time = None;
Ok(())
}
/// Discard the stored metadata in case that the testcase is not added to the corpus
#[inline]
fn discard_metadata(&mut self, _input: &I) -> Result<(), Error> {
self.exec_time = None;
Ok(())
}
}
impl Named for TimeFeedback {
#[inline]
fn name(&self) -> &str {
"TimeFeedback"
}
}
impl TimeFeedback {
pub fn new() -> Self {
Self { exec_time: None }
}
}
impl Default for TimeFeedback {
fn default() -> Self {
Self::new()
}
}

View File

@ -142,8 +142,8 @@ where
{ {
pub fn new(scheduler: CS, stages: ST) -> Self { pub fn new(scheduler: CS, stages: ST) -> Self {
Self { Self {
scheduler: scheduler, scheduler,
stages: stages, stages,
phantom: PhantomData, phantom: PhantomData,
} }
} }

View File

@ -61,7 +61,7 @@ where
{ {
pub fn new(max_size: usize) -> Self { pub fn new(max_size: usize) -> Self {
Self { Self {
max_size: max_size, max_size,
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -93,7 +93,7 @@ where
/// Generates up to DUMMY_BYTES_MAX non-random dummy bytes (0) /// Generates up to DUMMY_BYTES_MAX non-random dummy bytes (0)
fn generate_dummy(&self) -> BytesInput { fn generate_dummy(&self) -> BytesInput {
let size = min(self.max_size, DUMMY_BYTES_MAX); let size = min(self.max_size, DUMMY_BYTES_MAX);
BytesInput::new(vec!['0' as u8; size]) BytesInput::new(vec![0u8; size])
} }
} }
@ -103,7 +103,7 @@ where
{ {
pub fn new(max_size: usize) -> Self { pub fn new(max_size: usize) -> Self {
Self { Self {
max_size: max_size, max_size,
phantom: PhantomData, phantom: PhantomData,
} }
} }

View File

@ -64,7 +64,7 @@ impl From<&[u8]> for BytesInput {
impl BytesInput { impl BytesInput {
/// Creates a new bytes input using the given bytes /// Creates a new bytes input using the given bytes
pub fn new(bytes: Vec<u8>) -> Self { pub fn new(bytes: Vec<u8>) -> Self {
Self { bytes: bytes } Self { bytes }
} }
} }

View File

@ -97,6 +97,10 @@ pub trait HasBytesVec {
/// Has a length field /// Has a length field
pub trait HasLen { pub trait HasLen {
/// The lenght /// The length
fn len(&self) -> usize; fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
} }

View File

@ -66,6 +66,8 @@ pub enum Error {
IllegalState(String), IllegalState(String),
/// The argument passed to this method or function is not valid /// The argument passed to this method or function is not valid
IllegalArgument(String), IllegalArgument(String),
/// Shutting down, not really an error.
ShuttingDown,
/// Something else happened /// Something else happened
Unknown(String), Unknown(String),
} }
@ -85,6 +87,7 @@ impl fmt::Display for Error {
Self::NotImplemented(s) => write!(f, "Not implemented: {0}", &s), Self::NotImplemented(s) => write!(f, "Not implemented: {0}", &s),
Self::IllegalState(s) => write!(f, "Illegal state: {0}", &s), Self::IllegalState(s) => write!(f, "Illegal state: {0}", &s),
Self::IllegalArgument(s) => write!(f, "Illegal argument: {0}", &s), Self::IllegalArgument(s) => write!(f, "Illegal argument: {0}", &s),
Self::ShuttingDown => write!(f, "Shutting down!"),
Self::Unknown(s) => write!(f, "Unknown error: {0}", &s), Self::Unknown(s) => write!(f, "Unknown error: {0}", &s),
} }
} }
@ -179,7 +182,8 @@ mod tests {
//Box::new(|_, _, _, _, _| ()), //Box::new(|_, _, _, _, _| ()),
&mut state, &mut state,
&mut event_manager, &mut event_manager,
); )
.unwrap();
let mut mutator = StdScheduledMutator::new(); let mut mutator = StdScheduledMutator::new();
mutator.add_mutation(mutation_bitflip); mutator.add_mutation(mutation_bitflip);

View File

@ -41,32 +41,26 @@ where
/// Mem move in the own vec /// Mem move in the own vec
#[inline] #[inline]
pub fn buffer_self_copy(data: &mut [u8], from: usize, to: usize, len: usize) { pub fn buffer_self_copy(data: &mut [u8], from: usize, to: usize, len: usize) {
debug_assert!(data.len() > 0); debug_assert!(!data.is_empty());
debug_assert!(from + len <= data.len()); debug_assert!(from + len <= data.len());
debug_assert!(to + len <= data.len()); debug_assert!(to + len <= data.len());
if len != 0 && from != to { if len != 0 && from != to {
let ptr = data.as_mut_ptr(); let ptr = data.as_mut_ptr();
unsafe { core::ptr::copy(ptr.offset(from as isize), ptr.offset(to as isize), len) } unsafe { core::ptr::copy(ptr.add(from), ptr.add(to), len) }
} }
} }
/// Mem move between vecs /// Mem move between vecs
#[inline] #[inline]
pub fn buffer_copy(dst: &mut [u8], src: &[u8], from: usize, to: usize, len: usize) { pub fn buffer_copy(dst: &mut [u8], src: &[u8], from: usize, to: usize, len: usize) {
debug_assert!(dst.len() > 0); debug_assert!(!dst.is_empty());
debug_assert!(src.len() > 0); debug_assert!(!src.is_empty());
debug_assert!(from + len <= src.len()); debug_assert!(from + len <= src.len());
debug_assert!(to + len <= dst.len()); debug_assert!(to + len <= dst.len());
let dst_ptr = dst.as_mut_ptr(); let dst_ptr = dst.as_mut_ptr();
let src_ptr = src.as_ptr(); let src_ptr = src.as_ptr();
if len != 0 { if len != 0 {
unsafe { unsafe { core::ptr::copy(src_ptr.add(from), dst_ptr.add(to), len) }
core::ptr::copy(
src_ptr.offset(from as isize),
dst_ptr.offset(to as isize),
len,
)
}
} }
} }
@ -124,7 +118,7 @@ where
S: HasRand<R> + HasMaxSize, S: HasRand<R> + HasMaxSize,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize; let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize;
@ -142,7 +136,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -160,7 +154,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -179,7 +173,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -198,7 +192,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -216,7 +210,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -234,7 +228,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -335,7 +329,7 @@ where
S: HasRand<R>, S: HasRand<R>,
R: Rand, R: Rand,
{ {
if input.bytes().len() == 0 { if input.bytes().is_empty() {
Ok(MutationResult::Skipped) Ok(MutationResult::Skipped)
} else { } else {
let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; let idx = state.rand_mut().below(input.bytes().len() as u64) as usize;
@ -767,7 +761,7 @@ fn from_hex(hex: u8) -> Result<u8, Error> {
if hex >= 97 && hex <= 102 { if hex >= 97 && hex <= 102 {
return Ok(hex - 87); return Ok(hex - 87);
} }
return Err(Error::IllegalArgument("".to_owned())); Err(Error::IllegalArgument("".to_owned()))
} }
/// Decodes a dictionary token: 'foo\x41\\and\"bar' -> 'fooA\and"bar' /// Decodes a dictionary token: 'foo\x41\\and\"bar' -> 'fooA\and"bar'
@ -801,7 +795,7 @@ pub fn str_decode(item: &str) -> Result<Vec<u8>, Error> {
} }
} }
return Ok(token); Ok(token)
} }
#[cfg(test)] #[cfg(test)]

View File

@ -133,12 +133,23 @@ where
/// Create a new StdScheduledMutator instance specifying mutations /// Create a new StdScheduledMutator instance specifying mutations
pub fn with_mutations(mutations: Vec<MutationFunction<I, S>>) -> Self { pub fn with_mutations(mutations: Vec<MutationFunction<I, S>>) -> Self {
StdScheduledMutator { StdScheduledMutator {
mutations: mutations, mutations,
phantom: PhantomData, phantom: PhantomData,
} }
} }
} }
impl<I, R, S> Default for StdScheduledMutator<I, R, S>
where
I: Input,
S: HasRand<R>,
R: Rand,
{
fn default() -> Self {
Self::new()
}
}
/// Schedule some selected byte level mutations given a ScheduledMutator type /// Schedule some selected byte level mutations given a ScheduledMutator type
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct HavocBytesMutator<C, I, R, S, SM> pub struct HavocBytesMutator<C, I, R, S, SM>
@ -202,7 +213,7 @@ where
scheduled.add_mutation(mutation_bitflip); scheduled.add_mutation(mutation_bitflip);
scheduled.add_mutation(mutation_splice); scheduled.add_mutation(mutation_splice);
Self { Self {
scheduled: scheduled, scheduled,
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -253,13 +264,12 @@ where
//scheduled.add_mutation(mutation_splice); //scheduled.add_mutation(mutation_splice);
HavocBytesMutator { HavocBytesMutator {
scheduled: scheduled, scheduled,
phantom: PhantomData, phantom: PhantomData,
} }
} }
} }
/*
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@ -276,28 +286,23 @@ mod tests {
#[test] #[test]
fn test_mut_scheduled() { fn test_mut_scheduled() {
// With the current impl, seed of 1 will result in a split at pos 2. // With the current impl, seed of 1 will result in a split at pos 2.
let mut rand = XKCDRand::new(); let mut rand = XKCDRand::with_seed(5);
let mut corpus: InMemoryCorpus<BytesInput, _> = InMemoryCorpus::new(); let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
corpus.add(Testcase::new(vec!['a' as u8, 'b' as u8, 'c' as u8]).into()); corpus
corpus.add(Testcase::new(vec!['d' as u8, 'e' as u8, 'f' as u8]).into()); .add(Testcase::new(vec!['a' as u8, 'b' as u8, 'c' as u8]).into())
.unwrap();
corpus
.add(Testcase::new(vec!['d' as u8, 'e' as u8, 'f' as u8]).into())
.unwrap();
let (testcase, _) = corpus let testcase = corpus.get(0).expect("Corpus did not contain entries");
.next(&mut rand)
.expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone(); let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let mut state = State::new(corpus, (), InMemoryCorpus::new(), ()); let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
rand.set_seed(5); rand.set_seed(5);
let mut mutator = StdScheduledMutator::< mutation_splice(&mut state, &mut input).unwrap();
InMemoryCorpus<BytesInput, XKCDRand>,
_,
_,
State<_, (), _, InMemoryCorpus<BytesInput, XKCDRand>, (), _>,
>::new();
mutation_splice(&mut mutator, &mut rand, &mut state, &mut input).unwrap();
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("{:?}", input.bytes()); println!("{:?}", input.bytes());
@ -310,27 +315,37 @@ mod tests {
#[test] #[test]
fn test_havoc() { fn test_havoc() {
// With the current impl, seed of 1 will result in a split at pos 2. // With the current impl, seed of 1 will result in a split at pos 2.
let mut rand = StdRand::new(0x1337); let rand = StdRand::with_seed(0x1337);
let mut corpus: InMemoryCorpus<BytesInput, StdRand> = InMemoryCorpus::new(); let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
corpus.add(Testcase::new(vec!['a' as u8, 'b' as u8, 'c' as u8]).into()); corpus
corpus.add(Testcase::new(vec!['d' as u8, 'e' as u8, 'f' as u8]).into()); .add(Testcase::new(vec!['a' as u8, 'b' as u8, 'c' as u8]).into())
.unwrap();
corpus
.add(Testcase::new(vec!['d' as u8, 'e' as u8, 'f' as u8]).into())
.unwrap();
let (testcase, _) = corpus let testcase = corpus.get(0).expect("Corpus did not contain entries");
.next(&mut rand)
.expect("Corpus did not contain entries");
let mut input = testcase.borrow_mut().load_input().unwrap().clone(); let mut input = testcase.borrow_mut().load_input().unwrap().clone();
let input_prior = input.clone(); let input_prior = input.clone();
let mut state = State::new(corpus, (), InMemoryCorpus::new(), ()); let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
let mut havoc = HavocBytesMutator::new(StdScheduledMutator::new()); let havoc = HavocBytesMutator::new(StdScheduledMutator::new());
assert_eq!(input, input_prior); assert_eq!(input, input_prior);
let mut equal_in_a_row = 0;
for i in 0..42 { for i in 0..42 {
havoc.mutate(&mut rand, &mut state, &mut input, i).unwrap(); havoc.mutate(&mut state, &mut input, i).unwrap();
assert_ne!(input, input_prior);
// Make sure we actually mutate something, at least sometimes
equal_in_a_row = if input == input_prior {
equal_in_a_row + 1
} else {
0
};
assert_ne!(equal_in_a_row, 5);
} }
} }
} }
*/

View File

@ -54,7 +54,7 @@ impl Tokens {
return false; return false;
} }
self.token_vec.push(token.to_vec()); self.token_vec.push(token.to_vec());
return true; true
} }
/// Reads a tokens file, returning the count of new entries read /// Reads a tokens file, returning the count of new entries read
@ -75,11 +75,11 @@ impl Tokens {
let line = line.trim_start().trim_end(); let line = line.trim_start().trim_end();
// we are only interested in '"..."', not prefixed 'foo = ' // we are only interested in '"..."', not prefixed 'foo = '
let start = line.chars().nth(0); let start = line.chars().next();
if line.len() == 0 || start == Some('#') { if line.is_empty() || start == Some('#') {
continue; continue;
} }
let pos_quote = match line.find("\"") { let pos_quote = match line.find('\"') {
Some(x) => x, Some(x) => x,
_ => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)), _ => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)),
}; };
@ -92,7 +92,7 @@ impl Tokens {
Some(x) => x, Some(x) => x,
_ => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)), _ => return Err(Error::IllegalArgument("Illegal line: ".to_owned() + line)),
}; };
if item.len() == 0 { if item.is_empty() {
continue; continue;
} }
@ -117,7 +117,7 @@ impl Tokens {
/// Gets the tokens stored in this db /// Gets the tokens stored in this db
pub fn tokens(&self) -> &[Vec<u8>] { pub fn tokens(&self) -> &[Vec<u8>] {
return &self.token_vec; &self.token_vec
} }
} }
@ -134,7 +134,7 @@ where
if meta.is_none() { if meta.is_none() {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
if meta.unwrap().tokens().len() == 0 { if meta.unwrap().tokens().is_empty() {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
meta.unwrap().tokens().len() meta.unwrap().tokens().len()
@ -180,7 +180,7 @@ where
if meta.is_none() { if meta.is_none() {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
if meta.unwrap().tokens().len() == 0 { if meta.unwrap().tokens().is_empty() {
return Ok(MutationResult::Skipped); return Ok(MutationResult::Skipped);
} }
meta.unwrap().tokens().len() meta.unwrap().tokens().len()

View File

@ -118,7 +118,7 @@ where
{ {
/// Creates a new MapObserver /// Creates a new MapObserver
pub fn new(name: &'static str, map: &'static mut [T]) -> Self { pub fn new(name: &'static str, map: &'static mut [T]) -> Self {
let initial = if map.len() > 0 { map[0] } else { T::default() }; let initial = if map.is_empty() { T::default() } else { map[0] };
Self { Self {
map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())),
name: name.to_string(), name: name.to_string(),
@ -127,8 +127,9 @@ where
} }
/// Creates a new MapObserver from a raw pointer /// Creates a new MapObserver from a raw pointer
pub fn new_from_ptr(name: &'static str, map_ptr: *mut T, len: usize) -> Self { /// # Safety
unsafe { /// Will dereference the map_ptr with up to len elements.
pub unsafe fn new_from_ptr(name: &'static str, map_ptr: *mut T, len: usize) -> Self {
let initial = if len > 0 { *map_ptr } else { T::default() }; let initial = if len > 0 { *map_ptr } else { T::default() };
StdMapObserver { StdMapObserver {
map: ArrayMut::Cptr((map_ptr, len)), map: ArrayMut::Cptr((map_ptr, len)),
@ -136,7 +137,6 @@ where
initial, initial,
} }
} }
}
} }
/// Overlooking a variable bitmap /// Overlooking a variable bitmap
@ -213,7 +213,7 @@ where
{ {
/// Creates a new MapObserver /// Creates a new MapObserver
pub fn new(name: &'static str, map: &'static mut [T], size: &usize) -> Self { pub fn new(name: &'static str, map: &'static mut [T], size: &usize) -> Self {
let initial = if map.len() > 0 { map[0] } else { T::default() }; let initial = if map.is_empty() { T::default() } else { map[0] };
Self { Self {
map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())),
size: Cptr::Cptr(size as *const _), size: Cptr::Cptr(size as *const _),
@ -223,13 +223,14 @@ where
} }
/// Creates a new MapObserver from a raw pointer /// Creates a new MapObserver from a raw pointer
pub fn new_from_ptr( /// # Safety
/// Dereferences map_ptr with up to max_len elements of size_ptr.
pub unsafe fn new_from_ptr(
name: &'static str, name: &'static str,
map_ptr: *mut T, map_ptr: *mut T,
max_len: usize, max_len: usize,
size_ptr: *const usize, size_ptr: *const usize,
) -> Self { ) -> Self {
unsafe {
let initial = if max_len > 0 { *map_ptr } else { T::default() }; let initial = if max_len > 0 { *map_ptr } else { T::default() };
VariableMapObserver { VariableMapObserver {
map: ArrayMut::Cptr((map_ptr, max_len)), map: ArrayMut::Cptr((map_ptr, max_len)),
@ -238,7 +239,6 @@ where
initial, initial,
} }
} }
}
} }
/// Map observer with hitcounts postprocessing /// Map observer with hitcounts postprocessing
@ -335,6 +335,6 @@ where
{ {
/// Creates a new MapObserver /// Creates a new MapObserver
pub fn new(base: M) -> Self { pub fn new(base: M) -> Self {
Self { base: base } Self { base }
} }
} }

View File

@ -9,7 +9,7 @@ use core::time::Duration;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
bolts::tuples::{MatchNameAndType, MatchType, Named, TupleList}, bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named, TupleList},
utils::current_time, utils::current_time,
Error, Error,
}; };
@ -52,17 +52,15 @@ pub trait Observer: Named + serde::Serialize + serde::de::DeserializeOwned + 'st
/// A hastkel-style tuple of observers /// A hastkel-style tuple of observers
pub trait ObserversTuple: pub trait ObserversTuple:
MatchNameAndType + MatchType + serde::Serialize + serde::de::DeserializeOwned MatchNameAndType + MatchType + MatchFirstType + serde::Serialize + serde::de::DeserializeOwned
{ {
/// Reset all executors in the tuple /// Reset all executors in the tuple
/// This is called right before the next execution. /// This is called right before the next execution.
fn pre_exec_all(&mut self) -> Result<(), Error>; fn pre_exec_all(&mut self) -> Result<(), Error>;
/// Do whatever you need to do after a run. /// Do whatever you need to do after a run.
/// This is called right after the last execution /// This is called right after the last execution
fn post_exec_all(&mut self) -> Result<(), Error>; fn post_exec_all(&mut self) -> Result<(), Error>;
//fn for_each(&self, f: fn(&dyn Observer));
//fn for_each_mut(&mut self, f: fn(&mut dyn Observer));
} }
impl ObserversTuple for () { impl ObserversTuple for () {
@ -72,9 +70,6 @@ impl ObserversTuple for () {
fn post_exec_all(&mut self) -> Result<(), Error> { fn post_exec_all(&mut self) -> Result<(), Error> {
Ok(()) Ok(())
} }
//fn for_each(&self, f: fn(&dyn Observer)) { }
//fn for_each_mut(&mut self, f: fn(&mut dyn Observer)) { }
} }
impl<Head, Tail> ObserversTuple for (Head, Tail) impl<Head, Tail> ObserversTuple for (Head, Tail)
@ -91,16 +86,6 @@ where
self.0.post_exec()?; self.0.post_exec()?;
self.1.post_exec_all() self.1.post_exec_all()
} }
/*fn for_each(&self, f: fn(&dyn Observer)) {
f(&self.0);
self.1.for_each(f)
}
fn for_each_mut(&mut self, f: fn(&mut dyn Observer)) {
f(&mut self.0);
self.1.for_each_mut(f)
}*/
} }
/// A simple observer, just overlooking the runtime of the target. /// A simple observer, just overlooking the runtime of the target.
@ -120,6 +105,10 @@ impl TimeObserver {
last_runtime: None, last_runtime: None,
} }
} }
pub fn last_runtime(&self) -> &Option<Duration> {
&self.last_runtime
}
} }
impl Observer for TimeObserver { impl Observer for TimeObserver {

View File

@ -1,5 +1,8 @@
pub mod mutational; pub mod mutational;
pub use mutational::StdMutationalStage; pub use mutational::{MutationalStage, StdMutationalStage};
//pub mod power;
//pub use power::PowerMutationalStage;
use crate::{ use crate::{
bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input, Error, bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input, Error,

View File

@ -157,7 +157,7 @@ where
/// Creates a new default mutational stage /// Creates a new default mutational stage
pub fn new(mutator: M) -> Self { pub fn new(mutator: M) -> Self {
Self { Self {
mutator: mutator, mutator,
phantom: PhantomData, phantom: PhantomData,
} }
} }

110
libafl/src/stages/power.rs Normal file
View File

@ -0,0 +1,110 @@
use core::marker::PhantomData;
use crate::{
corpus::{Corpus, CorpusScheduler},
events::EventManager,
executors::{Executor, HasObservers},
inputs::Input,
mutators::Mutator,
observers::ObserversTuple,
stages::{Stage, MutationalStage},
state::{Evaluator, HasCorpus, HasRand},
utils::Rand,
Error,
};
/// The mutational stage using power schedules
#[derive(Clone, Debug)]
pub struct PowerMutationalStage<C, CS, E, EM, I, M, OT, R, S>
where
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
CS: CorpusScheduler<I, S>,
R: Rand,
{
mutator: M,
phantom: PhantomData<(C, CS, E, EM, I, OT, R, S)>,
}
impl<C, CS, E, EM, I, M, OT, R, S> MutationalStage<C, CS, E, EM, I, M, OT, S>
for PowerMutationalStage<C, CS, E, EM, I, M, OT, R, S>
where
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
CS: CorpusScheduler<I, S>,
R: Rand,
{
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut S) -> usize {
1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize
}
}
impl<C, CS, E, EM, I, M, OT, R, S> Stage<CS, E, EM, I, S>
for PowerMutationalStage<C, CS, E, EM, I, M, OT, R, S>
where
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
CS: CorpusScheduler<I, S>,
R: Rand,
{
#[inline]
fn perform(
&self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
corpus_idx: usize,
) -> Result<(), Error> {
self.perform_mutational(state, executor, manager, scheduler, corpus_idx)
}
}
impl<C, CS, E, EM, I, M, OT, R, S> PowerMutationalStage<C, CS, E, EM, I, M, OT, R, S>
where
M: Mutator<I, S>,
I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>,
C: Corpus<I>,
EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>,
OT: ObserversTuple,
CS: CorpusScheduler<I, S>,
R: Rand,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
Self {
mutator: mutator,
phantom: PhantomData,
}
}
}

View File

@ -515,15 +515,15 @@ where
self.solutions_mut().add(Testcase::new(input.clone()))?; self.solutions_mut().add(Testcase::new(input.clone()))?;
} }
if !self if self
.add_if_interesting(&input, fitness, scheduler)? .add_if_interesting(&input, fitness, scheduler)?
.is_none() .is_some()
{ {
let observers_buf = manager.serialize_observers(observers)?; let observers_buf = manager.serialize_observers(observers)?;
manager.fire( manager.fire(
self, self,
Event::NewTestcase { Event::NewTestcase {
input: input, input,
observers_buf, observers_buf,
corpus_size: self.corpus().count() + 1, corpus_size: self.corpus().count() + 1,
client_config: "TODO".into(), client_config: "TODO".into(),
@ -564,7 +564,7 @@ where
let path = entry.path(); let path = entry.path();
let attributes = fs::metadata(&path); let attributes = fs::metadata(&path);
if !attributes.is_ok() { if attributes.is_err() {
continue; continue;
} }
@ -647,7 +647,7 @@ where
executor.pre_exec(self, event_mgr, input)?; executor.pre_exec(self, event_mgr, input)?;
let exit_kind = executor.run_target(input)?; let exit_kind = executor.run_target(input)?;
//executor.post_exec(&self, event_mgr, input)?; executor.post_exec(self, event_mgr, input)?;
*self.executions_mut() += 1; *self.executions_mut() += 1;
executor.post_exec_observers()?; executor.post_exec_observers()?;

View File

@ -187,7 +187,7 @@ where
{ {
pub fn new(print_fn: F) -> Self { pub fn new(print_fn: F) -> Self {
Self { Self {
print_fn: print_fn, print_fn,
start_time: current_time(), start_time: current_time(),
corpus_size: 0, corpus_size: 0,
client_stats: vec![], client_stats: vec![],
@ -196,8 +196,8 @@ where
pub fn with_time(print_fn: F, start_time: time::Duration) -> Self { pub fn with_time(print_fn: F, start_time: time::Duration) -> Self {
Self { Self {
print_fn: print_fn, print_fn,
start_time: start_time, start_time,
corpus_size: 0, corpus_size: 0,
client_stats: vec![], client_stats: vec![],
} }

View File

@ -4,8 +4,19 @@ use core::{cell::RefCell, debug_assert, fmt::Debug, time};
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use xxhash_rust::xxh3::xxh3_64_with_seed; use xxhash_rust::xxh3::xxh3_64_with_seed;
#[cfg(unix)]
use libc::pid_t;
#[cfg(all(unix, feature = "std"))]
use std::ffi::CString;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH}; use std::{
env,
process::Command,
time::{SystemTime, UNIX_EPOCH},
};
#[cfg(any(unix, feature = "std"))]
use crate::Error;
pub trait AsSlice<T> { pub trait AsSlice<T> {
/// Convert to a slice /// Convert to a slice
@ -204,7 +215,7 @@ impl Rand for Xoshiro256StarRand {
self.rand_seed[3] = self.rand_seed[3].rotate_left(45); self.rand_seed[3] = self.rand_seed[3].rotate_left(45);
return ret; ret
} }
} }
@ -235,7 +246,7 @@ impl Rand for XorShift64Rand {
x ^= x >> 7; x ^= x >> 7;
x ^= x << 17; x ^= x << 17;
self.rand_seed = x; self.rand_seed = x;
return x; x
} }
} }
@ -262,7 +273,7 @@ impl Rand for Lehmer64Rand {
#[inline] #[inline]
fn next(&mut self) -> u64 { fn next(&mut self) -> u64 {
self.rand_seed *= 0xda942042e4dd58b5; self.rand_seed *= 0xda942042e4dd58b5;
return (self.rand_seed >> 64) as u64; (self.rand_seed >> 64) as u64
} }
} }
@ -378,6 +389,61 @@ impl XKCDRand {
} }
} }
/// Child Process Handle
#[cfg(unix)]
pub struct ChildHandle {
pid: pid_t,
}
#[cfg(unix)]
impl ChildHandle {
/// Block until the child exited and the status code becomes available
pub fn status(&self) -> i32 {
let mut status = -1;
unsafe {
libc::waitpid(self.pid, &mut status, 0);
}
status
}
}
#[cfg(unix)]
/// The ForkResult
pub enum ForkResult {
Parent(ChildHandle),
Child,
}
/// Unix has forks.
/// # Safety
/// A Normal fork. Runs on in two processes. Should be memory safe in general.
#[cfg(unix)]
pub unsafe fn fork() -> Result<ForkResult, Error> {
match libc::fork() {
pid if pid > 0 => Ok(ForkResult::Parent(ChildHandle { pid })),
pid if pid < 0 => {
// Getting errno from rust is hard, we'll just let the libc print to stderr for now.
// In any case, this should usually not happen.
#[cfg(feature = "std")]
{
let err_str = CString::new("Fork failed").unwrap();
libc::perror(err_str.as_ptr());
}
Err(Error::Unknown(format!("Fork failed ({})", pid)))
}
_ => Ok(ForkResult::Child),
}
}
/// Executes the current process from the beginning, as subprocess.
/// use `start_self.status()?` to wait for the child
#[cfg(feature = "std")]
pub fn startable_self() -> Result<Command, Error> {
let mut startable = Command::new(env::current_exe()?);
startable.current_dir(env::current_dir()?).args(env::args());
Ok(startable)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
//use xxhash_rust::xxh3::xxh3_64_with_seed; //use xxhash_rust::xxh3::xxh3_64_with_seed;