diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index ae73618122..621f174256 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -1,52 +1,46 @@
name: Build and Test
-on: [push]
+on:
+ push:
+ branches: [ main ]
+ pull_requests:
+ branches: [ main, dev ]
env:
CARGO_TERM_COLOR: always
jobs:
- default:
+ ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- - name: Build
+ - name: Default Build
run: cargo build --verbose
- - name: Test
+ - name: Default Test
run: cargo test --verbose
- all-features:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- name: Build all features
run: cd libafl && cargo build --all-features --verbose
- name: Test all features
run: cd libafl && cargo test --all-features --verbose
- no-std:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- name: Build no_std
run: cd libafl && cargo build --no-default-features --verbose
- name: Test no_std
run: cd libafl && cargo test --no-default-features --verbose
- examples:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- name: Build examples
run: cargo build --examples --verbose
- fmt:
- runs-on: ubuntu-latest
- steps:
- uses: actions/checkout@v2
- name: Format
run: cargo fmt -- --check
- docs:
- runs-on: ubuntu-latest
- steps:
- uses: actions/checkout@v2
- name: Build Docs
run: cargo doc
- name: Test Docs
- run: cargo test --doc
\ No newline at end of file
+ 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
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 5357f69183..0000000000
--- a/LICENSE
+++ /dev/null
@@ -1,166 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- 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.
-
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000000..f49a4e16e6
--- /dev/null
+++ b/LICENSE-APACHE
@@ -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.
\ No newline at end of file
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000000..468cd79a8f
--- /dev/null
+++ b/LICENSE-MIT
@@ -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.
\ No newline at end of file
diff --git a/README.md b/README.md
index 05acde12d3..0ff353378b 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,7 @@ Advanced Fuzzing Library - Slot your own fuzzers together and extend their featu
LibAFL is written and maintained by Andrea Fioraldi and Dominik Maier .
-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
diff --git a/TODO.md b/TODO.md
index ac96a219af..c5a59e1a49 100644
--- a/TODO.md
+++ b/TODO.md
@@ -15,6 +15,7 @@
- [ ] Restart Count in Fuzzing Loop
- [ ] LAIN / structured fuzzing example
- [ ] Errors in the Fuzzer should exit the fuzz run
+- [ ] More informative outpus, deeper introspection (stats, what mutation did x, etc.)
- [ ] Timeouts for executors
- [ ] Timeout handling for llmp clients (no ping for n seconds -> treat as disconnected)
- [ ] LLMP Cross Machine Link (2 brokers connected via TCP)
diff --git a/fuzzers/libfuzzer_libmozjpeg/build.rs b/fuzzers/libfuzzer_libmozjpeg/build.rs
index ee0062f3e3..96f09a9db0 100644
--- a/fuzzers/libfuzzer_libmozjpeg/build.rs
+++ b/fuzzers/libfuzzer_libmozjpeg/build.rs
@@ -1,12 +1,19 @@
// build.rs
-use std::env;
-use std::path::Path;
-use std::process::Command;
+use std::{
+ env,
+ path::Path,
+ process::{exit, Command},
+};
const LIBMOZJPEG_URL: &str = "https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz";
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 cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string();
diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs
index 58ec07983a..ffbbcdfa78 100644
--- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs
+++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs
@@ -3,6 +3,7 @@
use std::{env, path::PathBuf};
+#[cfg(unix)]
use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
@@ -22,6 +23,7 @@ use libafl::{
};
/// We will interact with a C++ target, so use external c functionality
+#[cfg(unix)]
extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
@@ -35,6 +37,7 @@ extern "C" {
}
/// The wrapped harness function, calling out to the LLVM-style harness
+#[cfg(unix)]
fn harness(_executor: &E, buf: &[u8]) -> ExitKind
where
E: Executor,
@@ -65,7 +68,14 @@ pub fn main() {
.expect("An error occurred while fuzzing");
}
+/// Not supported on windows right now
+#[cfg(windows)]
+fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
+ todo!("Example not supported on Windows");
+}
+
/// The actual fuzzer
+#[cfg(unix)]
fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s));
@@ -76,10 +86,9 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) ->
.expect("Failed to setup the restarter".into());
// Create an observation channel using the coverage map
- let edges_observer =
- StdMapObserver::new_from_ptr("edges", unsafe { __lafl_edges_map }, unsafe {
- __lafl_max_edges_size as usize
- });
+ let edges_observer = unsafe {
+ StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
+ };
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
@@ -119,7 +128,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) ->
tuple_list!(edges_observer),
&mut state,
&mut restarting_mgr,
- );
+ )?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
diff --git a/fuzzers/libfuzzer_libpng/build.rs b/fuzzers/libfuzzer_libpng/build.rs
index 1b270dbd71..49f3cfba94 100644
--- a/fuzzers/libfuzzer_libpng/build.rs
+++ b/fuzzers/libfuzzer_libpng/build.rs
@@ -1,13 +1,20 @@
// build.rs
-use std::env;
-use std::path::Path;
-use std::process::Command;
+use std::{
+ env,
+ path::Path,
+ process::{exit, Command},
+};
const LIBPNG_URL: &str =
"https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz";
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 cwd = env::current_dir().unwrap().to_string_lossy().to_string();
let out_dir = out_dir.to_string_lossy().to_string();
diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng/harness.cc
index 15c204dc9f..65faff685d 100644
--- a/fuzzers/libfuzzer_libpng/harness.cc
+++ b/fuzzers/libfuzzer_libpng/harness.cc
@@ -158,7 +158,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (width && height > 100000000 / width) {
PNG_CLEANUP
#ifdef HAS_DUMMY_CRASH
- asm("ud2");
#ifdef __aarch64__
asm volatile (".word 0xf7f0a000\n");
#else
diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs
index 62845e7799..4640433c40 100644
--- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs
+++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs
@@ -3,6 +3,7 @@
use std::{env, path::PathBuf};
+#[cfg(unix)]
use libafl::{
bolts::{shmem::UnixShMem, tuples::tuple_list},
corpus::{
@@ -11,11 +12,11 @@ use libafl::{
},
events::setup_restarting_mgr,
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
- feedbacks::{CrashFeedback, MaxMapFeedback},
+ feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
inputs::Input,
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
- observers::{HitcountsMapObserver, StdMapObserver},
+ observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State},
stats::SimpleStats,
@@ -24,6 +25,7 @@ use libafl::{
};
/// We will interact with a C++ target, so use external c functionality
+#[cfg(unix)]
extern "C" {
/// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32;
@@ -37,6 +39,7 @@ extern "C" {
}
/// The wrapped harness function, calling out to the LLVM-style harness
+#[cfg(unix)]
fn harness(_executor: &E, buf: &[u8]) -> ExitKind
where
E: Executor,
@@ -67,22 +70,36 @@ pub fn main() {
.expect("An error occurred while fuzzing");
}
+/// Not supported on windows right now
+#[cfg(windows)]
+fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> {
+ todo!("Example not supported on Windows");
+}
+
/// The actual fuzzer
+#[cfg(unix)]
fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s));
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) =
- setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port)
- .expect("Failed to setup the restarter".into());
+ match setup_restarting_mgr::<_, _, UnixShMem, _>(stats, broker_port) {
+ Ok(res) => res,
+ Err(err) => match err {
+ Error::ShuttingDown => {
+ return Ok(());
+ }
+ _ => {
+ panic!("Failed to setup the restarter: {}", err);
+ }
+ },
+ };
// Create an observation channel using the coverage map
- let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
- "edges",
- unsafe { __lafl_edges_map },
- unsafe { __lafl_max_edges_size as usize },
- ));
+ let edges_observer = HitcountsMapObserver::new(unsafe {
+ StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize)
+ });
// If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| {
@@ -92,11 +109,10 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) ->
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
- tuple_list!(MaxMapFeedback::new_with_observer_track(
- &edges_observer,
- true,
- false
- )),
+ tuple_list!(
+ MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
+ TimeFeedback::new()
+ ),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
@@ -130,10 +146,10 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) ->
let mut executor = InProcessExecutor::new(
"in-process(edges)",
harness,
- tuple_list!(edges_observer),
+ tuple_list!(edges_observer, TimeObserver::new("time")),
&mut state,
&mut restarting_mgr,
- );
+ )?;
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
diff --git a/fuzzers/libfuzzer_runtime/rt.c b/fuzzers/libfuzzer_runtime/rt.c
index 90f4fea89a..ec90be0d7e 100644
--- a/fuzzers/libfuzzer_runtime/rt.c
+++ b/fuzzers/libfuzzer_runtime/rt.c
@@ -1,5 +1,7 @@
#include
#include
+#include
+#include
#define MAP_SIZE 65536
@@ -8,9 +10,11 @@ char **orig_argv;
char **orig_envp;
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_cmp_map = __lafl_dummy_map;
+size_t *__lafl_alloc_map = __lafl_dummy_map_usize;
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) {
orig_argc = argc;
orig_argv = argv;
diff --git a/fuzzers/qemufuzzer/src/lib.rs b/fuzzers/qemufuzzer/src/lib.rs
index 8ed917e4b0..4413e0f67e 100644
--- a/fuzzers/qemufuzzer/src/lib.rs
+++ b/fuzzers/qemufuzzer/src/lib.rs
@@ -72,7 +72,7 @@ pub extern "C" fn fuzz_main_loop() {
});
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 engine = Engine::new(executor);
diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml
index 7c7f9f5e00..01a78fc319 100644
--- a/libafl/Cargo.toml
+++ b/libafl/Cargo.toml
@@ -54,9 +54,11 @@ ctor = "*"
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
#TODO: for llmp brotli = { version = "3.3.0", default-features = false } # brotli compression
+num_enum = "0.5.1"
[target.'cfg(unix)'.dependencies]
libc = "0.2" # For (*nix) libc
+nix = "0.20.0"
[target.'cfg(windows)'.dependencies]
windows = "0.3.1"
diff --git a/libafl/examples/llmp_test/main.rs b/libafl/examples/llmp_test/main.rs
index 139030a833..4dadc2ebf4 100644
--- a/libafl/examples/llmp_test/main.rs
+++ b/libafl/examples/llmp_test/main.rs
@@ -3,17 +3,21 @@ This shows how llmp can be used directly, without libafl abstractions
*/
extern crate alloc;
+#[cfg(all(unix, feature = "std"))]
use core::{convert::TryInto, time::Duration};
+#[cfg(all(unix, feature = "std"))]
use std::{thread, time};
+#[cfg(all(unix, feature = "std"))]
use libafl::{
bolts::{llmp, shmem::UnixShMem},
Error,
};
-const TAG_SIMPLE_U32_V1: u32 = 0x51300321;
-const TAG_MATH_RESULT_V1: u32 = 0x77474331;
+const _TAG_SIMPLE_U32_V1: u32 = 0x51300321;
+const _TAG_MATH_RESULT_V1: u32 = 0x77474331;
+#[cfg(all(unix, feature = "std"))]
fn adder_loop(port: u16) -> ! {
let mut client = llmp::LlmpClient::::create_attach_to_tcp(port).unwrap();
let mut last_result: u32 = 0;
@@ -27,7 +31,7 @@ fn adder_loop(port: u16) -> ! {
};
msg_counter += 1;
match tag {
- TAG_SIMPLE_U32_V1 => {
+ _TAG_SIMPLE_U32_V1 => {
current_result =
current_result.wrapping_add(u32::from_le_bytes(buf.try_into().unwrap()));
}
@@ -42,7 +46,7 @@ fn adder_loop(port: u16) -> ! {
);
client
- .send_buf(TAG_MATH_RESULT_V1, ¤t_result.to_le_bytes())
+ .send_buf(_TAG_MATH_RESULT_V1, ¤t_result.to_le_bytes())
.unwrap();
last_result = current_result;
}
@@ -51,13 +55,14 @@ fn adder_loop(port: u16) -> ! {
}
}
+#[cfg(all(unix, feature = "std"))]
fn broker_message_hook(
client_id: u32,
tag: llmp::Tag,
message: &[u8],
) -> Result {
match tag {
- TAG_SIMPLE_U32_V1 => {
+ _TAG_SIMPLE_U32_V1 => {
println!(
"Client {:?} sent message: {:?}",
client_id,
@@ -65,7 +70,7 @@ fn broker_message_hook(
);
Ok(llmp::LlmpMsgHookResult::ForwardToClients)
}
- TAG_MATH_RESULT_V1 => {
+ _TAG_MATH_RESULT_V1 => {
println!(
"Adder Client has this current result: {:?}",
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() {
/* The main node has a broker, and a few worker threads */
@@ -96,9 +107,9 @@ fn main() {
"broker" => {
let mut broker = llmp::LlmpBroker::::new().unwrap();
broker
- .launch_tcp_listener(
+ .launch_listener(llmp::Listener::Tcp(
std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).unwrap(),
- )
+ ))
.unwrap();
broker.loop_forever(&mut broker_message_hook, Some(Duration::from_millis(5)))
}
@@ -108,7 +119,7 @@ fn main() {
loop {
counter = counter.wrapping_add(1);
client
- .send_buf(TAG_SIMPLE_U32_V1, &counter.to_le_bytes())
+ .send_buf(_TAG_SIMPLE_U32_V1, &counter.to_le_bytes())
.unwrap();
println!("CTR Client writing {}", counter);
thread::sleep(Duration::from_secs(1))
diff --git a/libafl/src/bolts/bindings.rs b/libafl/src/bolts/bindings.rs
index 02f7aa456c..a3fa41413e 100644
--- a/libafl/src/bolts/bindings.rs
+++ b/libafl/src/bolts/bindings.rs
@@ -1,2 +1,2 @@
-#[cfg(windows)]
+#[cfg(all(windows, feature = "std"))]
::windows::include_bindings!();
diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs
index 0055354f10..96cee0bbc6 100644
--- a/libafl/src/bolts/llmp.rs
+++ b/libafl/src/bolts/llmp.rs
@@ -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::{
cmp::max,
fmt::Debug,
@@ -64,14 +64,42 @@ use core::{
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use std::{
- env,
+ env, fs,
io::{Read, Write},
- net::{TcpListener, TcpStream},
+ net::{SocketAddr, TcpListener, TcpStream},
thread,
};
-use super::shmem::{ShMem, ShMemDescription};
-use crate::Error;
+#[cfg(all(feature = "std", unix))]
+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
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)
const LLMP_PAGE_HEADER_LEN: usize = size_of::();
+/// 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
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
#[inline]
unsafe fn shmem2page_mut(afl_shmem: &mut SH) -> *mut LlmpPage {
@@ -120,8 +193,8 @@ unsafe fn shmem2page(afl_shmem: &SH) -> *const LlmpPage {
#[inline]
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); */
- return (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) < msg as *const u8
+ && (page as *const u8).add((*page).size_total) > msg as *const u8
}
/// allign to LLMP_PREF_ALIGNNMENT=64 bytes
@@ -198,9 +271,9 @@ unsafe fn llmp_next_msg_ptr_checked(
) -> Result<*mut LlmpMsg, Error> {
let page = map.page_mut();
let map_size = map.shmem.map().len();
- let msg_begin_min = (page as *const u8).offset(size_of::() as isize);
+ let msg_begin_min = (page as *const u8).add(size_of::());
// 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_ptr = next as *const u8;
if next_ptr >= msg_begin_min && next_ptr <= msg_begin_max {
@@ -217,9 +290,9 @@ unsafe fn llmp_next_msg_ptr_checked(
#[inline]
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)); */
- return (last_msg as *mut u8)
- .offset(size_of::() as isize)
- .offset((*last_msg).buf_len_padded as isize) as *mut LlmpMsg;
+ (last_msg as *mut u8)
+ .add(size_of::())
+ .add((*last_msg).buf_len_padded as usize) as *mut LlmpMsg
}
/// Description of a shared map.
@@ -262,6 +335,7 @@ pub struct LlmpMsg {
/// The message we receive
impl LlmpMsg {
/// 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.
pub unsafe fn as_slice_unsafe(&self) -> &[u8] {
slice::from_raw_parts(self.buf.as_ptr(), self.buf_len as usize)
@@ -285,14 +359,13 @@ impl LlmpMsg {
unsafe {
let map_size = map.shmem.map().len();
let buf_ptr = self.buf.as_ptr();
- if buf_ptr > (map.page_mut() as *const u8).offset(size_of::() as isize)
+ if buf_ptr > (map.page_mut() as *const u8).add(size_of::())
&& buf_ptr
- <= (map.page_mut() as *const u8)
- .offset((map_size - size_of::() as usize) as isize)
+ <= (map.page_mut() as *const u8).add(map_size - size_of::() as usize)
{
// The message header is in the page. Continue with checking the body.
let len = self.buf_len_padded as usize + size_of::();
- 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 {
false
}
@@ -324,7 +397,7 @@ where
// We got the port. We are the broker! :)
dbg!("We're the broker");
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 })
}
Err(e) => {
@@ -368,6 +441,36 @@ where
}
}
+impl LlmpConnection
+where
+ SH: ShMem + HasFd,
+{
+ #[cfg(all(feature = "std", unix))]
+ pub fn on_domain_socket(filename: &str) -> Result {
+ 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
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
@@ -448,6 +551,8 @@ where
/// Completely reset the current sender map.
/// 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.
+ /// # Safety
+ /// Only safe if you really really restart the page on everything connected
pub unsafe fn reset(&mut self) {
_llmp_page_init(&mut self.out_maps.last_mut().unwrap().shmem, self.id, true);
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,
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 = buf_len as u64;
/* 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
- pub unsafe fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, Error> {
- match self.alloc_next_if_space(buf_len) {
- Some(msg) => return Ok(msg),
- _ => (),
+ pub fn alloc_next(&mut self, buf_len: usize) -> Result<*mut LlmpMsg, Error> {
+ if let Some(msg) = unsafe { self.alloc_next_if_space(buf_len) } {
+ return Ok(msg);
};
/* no more space left! We'll have to start a new page */
- self.handle_out_eop()?;
+ unsafe {
+ 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),
None => Err(Error::Unknown(format!(
"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.
+ /// # Safety
+ /// They msg pointer may no longer be used after `cancel_send`
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,
* msg->buf_len_padded); */
@@ -769,7 +877,7 @@ where
let last_message_offset = if self.last_msg_sent.is_null() {
None
} else {
- Some(map.msg_to_offset(self.last_msg_sent)?)
+ Some(unsafe { map.msg_to_offset(self.last_msg_sent) }?)
};
Ok(LlmpDescription {
shmem: map.shmem.description(),
@@ -872,61 +980,59 @@ where
};
// Let's see what we go here.
- match ret {
- Some(msg) => {
- 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()));
- }
- // Handle special, LLMP internal, messages.
- match (*msg).tag {
- LLMP_TAG_UNSET => panic!("BUG: Read unallocated msg"),
- LLMP_TAG_END_OF_PAGE => {
- #[cfg(feature = "std")]
- dbg!("Got end of page, allocing next");
- // Handle end of page
- if (*msg).buf_len < size_of::() as u64 {
- panic!(
- "Illegal message length for EOP (is {}, expected {})",
- (*msg).buf_len_padded,
- size_of::()
- );
- }
- let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
-
- /* We can reuse the map mem space, no need to free and calloc.
- However, the pageinfo points to the map we're about to unmap.
- Clone the contents first to be safe (probably fine in rust eitner way). */
- let pageinfo_cpy = (*pageinfo).clone();
-
- // Mark the old page save to unmap, in case we didn't so earlier.
- ptr::write_volatile(&mut (*page).save_to_unmap, 1);
- // Map the new page. The old one should be unmapped by Drop
- self.current_recv_map =
- LlmpSharedMap::existing(SH::existing_from_shm_slice(
- &pageinfo_cpy.shm_str,
- pageinfo_cpy.map_size,
- )?);
- // Mark the new page save to unmap also (it's mapped by us, the broker now)
- ptr::write_volatile(&mut (*page).save_to_unmap, 1);
-
- #[cfg(feature = "std")]
- dbg!("Got a new recv map", self.current_recv_map.shmem.shm_str());
- // After we mapped the new page, return the next message, if available
- return self.recv();
- }
- _ => (),
- }
-
- // Store the last msg for next time
- self.last_msg_recvd = msg;
+ if let Some(msg) = ret {
+ 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()));
}
- _ => (),
+ // Handle special, LLMP internal, messages.
+ match (*msg).tag {
+ LLMP_TAG_UNSET => panic!("BUG: Read unallocated msg"),
+ LLMP_TAG_END_OF_PAGE => {
+ #[cfg(feature = "std")]
+ dbg!("Got end of page, allocing next");
+ // Handle end of page
+ if (*msg).buf_len < size_of::() as u64 {
+ panic!(
+ "Illegal message length for EOP (is {}, expected {})",
+ (*msg).buf_len_padded,
+ size_of::()
+ );
+ }
+ let pageinfo = (*msg).buf.as_mut_ptr() as *mut LlmpPayloadSharedMapInfo;
+
+ /* We can reuse the map mem space, no need to free and calloc.
+ However, the pageinfo points to the map we're about to unmap.
+ Clone the contents first to be safe (probably fine in rust eitner way). */
+ let pageinfo_cpy = (*pageinfo).clone();
+
+ // Mark the old page save to unmap, in case we didn't so earlier.
+ ptr::write_volatile(&mut (*page).save_to_unmap, 1);
+ // Map the new page. The old one should be unmapped by Drop
+ self.current_recv_map = LlmpSharedMap::existing(SH::existing_from_shm_slice(
+ &pageinfo_cpy.shm_str,
+ pageinfo_cpy.map_size,
+ )?);
+ // Mark the new page save to unmap also (it's mapped by us, the broker now)
+ ptr::write_volatile(&mut (*page).save_to_unmap, 1);
+
+ #[cfg(feature = "std")]
+ dbg!("Got a new recv map", self.current_recv_map.shmem.shm_str());
+ // After we mapped the new page, return the next message, if available
+ return self.recv();
+ }
+ _ => (),
+ }
+
+ // Store the last msg for next time
+ self.last_msg_recvd = msg;
};
Ok(ret)
}
/// Blocks/spins until the next message gets posted to the page,
/// 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> {
let mut current_msg_id = 0;
let page = self.current_recv_map.page_mut();
@@ -982,7 +1088,7 @@ where
let last_message_offset = if self.last_msg_recvd.is_null() {
None
} else {
- Some(map.msg_to_offset(self.last_msg_recvd)?)
+ Some(unsafe { map.msg_to_offset(self.last_msg_recvd) }?)
};
Ok(LlmpDescription {
shmem: map.shmem.description(),
@@ -1047,29 +1153,33 @@ where
}
/// 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 {
shmem2page_mut(&mut self.shmem)
}
/// 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 {
shmem2page(&self.shmem)
}
/// Gets the offset of a message on this here page.
/// Will return IllegalArgument error if msg is not on page.
- pub fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result {
- unsafe {
- let page = self.page();
- if llmp_msg_in_page(page, msg) {
- // Cast both sides to u8 arrays, get the offset, then cast the return isize to u64
- Ok((msg as *const u8).offset_from((*page).messages.as_ptr() as *const u8) as u64)
- } else {
- Err(Error::IllegalArgument(format!(
- "Message (0x{:X}) not in page (0x{:X})",
- page as u64, msg as u64
- )))
- }
+ /// # Safety
+ /// This dereferences msg, make sure to pass a proper pointer to it.
+ pub unsafe fn msg_to_offset(&self, msg: *const LlmpMsg) -> Result {
+ let page = self.page();
+ if llmp_msg_in_page(page, msg) {
+ // Cast both sides to u8 arrays, get the offset, then cast the return isize to u64
+ Ok((msg as *const u8).offset_from((*page).messages.as_ptr() as *const u8) as u64)
+ } else {
+ Err(Error::IllegalArgument(format!(
+ "Message (0x{:X}) not in page (0x{:X})",
+ page as u64, msg as u64
+ )))
}
}
@@ -1092,7 +1202,7 @@ where
} else {
env::set_var(
&format!("{}_OFFSET", map_env_name),
- format!("{}", self.msg_to_offset(msg)?),
+ format!("{}", unsafe { self.msg_to_offset(msg) }?),
)
};
Ok(())
@@ -1131,6 +1241,27 @@ where
/// This allows us to intercept messages right in the broker
/// This keeps the out map clean.
pub llmp_clients: Vec>,
+ /// This is the socket name, when unix domain sockets are used.
+ socket_name: Option,
+ /// 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 {
+ vec![Signal::SigTerm, Signal::SigInterrupt, Signal::SigQuit]
+ }
}
/// The broker forwards all messages to its own bus-like broadcast map.
@@ -1151,6 +1282,8 @@ where
keep_pages_forever: true,
},
llmp_clients: vec![],
+ socket_name: None,
+ shutting_down: false,
};
Ok(broker)
@@ -1210,23 +1343,43 @@ where
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.
/// Never returns. Panics on error.
/// 5 millis of sleep can't hurt to keep busywait not at 100%
- pub fn loop_forever(&mut self, on_new_msg: &mut F, sleep_time: Option) -> !
+ pub fn loop_forever(&mut self, on_new_msg: &mut F, sleep_time: Option)
where
F: FnMut(u32, Tag, &[u8]) -> Result,
{
- 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);
self.once(on_new_msg)
.expect("An error occurred when brokering. Exiting.");
#[cfg(feature = "std")]
- match sleep_time {
- Some(time) => thread::sleep(time),
- None => (),
- }
+ if let Some(time) = sleep_time {
+ thread::sleep(time)
+ };
#[cfg(not(feature = "std"))]
match sleep_time {
@@ -1250,15 +1403,12 @@ where
let listener = TcpListener::bind(format!("127.0.0.1:{}", port))?;
// accept connections and process them, spawning a new thread for each one
println!("Server listening on port {}", port);
- return self.launch_tcp_listener(listener);
+ self.launch_listener(Listener::Tcp(listener))
}
#[cfg(feature = "std")]
- /// Launches a thread using a tcp listener socket, on which new clients may connect to this broker
- pub fn launch_tcp_listener(
- &mut self,
- listener: TcpListener,
- ) -> Result, Error> {
+ /// Launches a thread using a listener socket, on which new clients may connect to this broker
+ pub fn launch_listener(&mut self, listener: Listener) -> Result, Error> {
// Later in the execution, after the initial map filled up,
// 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
@@ -1290,43 +1440,109 @@ where
};
loop {
- let (mut stream, addr) = match listener.accept() {
- Ok(res) => res,
- Err(e) => {
- dbg!("Ignoring failed accept", e);
- continue;
+ match listener.accept() {
+ ListenerStream::Tcp(mut stream, addr) => {
+ dbg!("New connection", addr, stream.peer_addr().unwrap());
+ match stream.write(&broadcast_str_initial) {
+ Ok(_) => {} // fire & forget
+ Err(e) => {
+ dbg!("Could not send to shmap to client", e);
+ continue;
+ }
+ };
+ let mut new_client_map_str: [u8; 20] = Default::default();
+ match stream.read_exact(&mut new_client_map_str) {
+ Ok(()) => (),
+ Err(e) => {
+ dbg!("Ignoring failed read from client", e);
+ continue;
+ }
+ };
+ unsafe {
+ let msg = new_client_sender
+ .alloc_next(size_of::())
+ .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 = new_client_map_str;
+ (*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
+ match new_client_sender.send(msg) {
+ Ok(()) => (),
+ Err(e) => println!("Error forwarding client on map: {:?}", e),
+ };
+ }
}
- };
- dbg!("New connection", addr, stream.peer_addr().unwrap());
- match stream.write(&broadcast_str_initial) {
- Ok(_) => {} // fire & forget
- Err(e) => {
- dbg!("Could not send to shmap to client", e);
- continue;
- }
- };
- let mut new_client_map_str: [u8; 20] = Default::default();
- match stream.read_exact(&mut new_client_map_str) {
- Ok(()) => (),
- Err(e) => {
- dbg!("Ignoring failed read from client", e);
- continue;
- }
- };
+ #[cfg(unix)]
+ ListenerStream::Unix(stream, addr) => unsafe {
+ dbg!("New connection", addr);
- unsafe {
- let msg = new_client_sender
- .alloc_next(size_of::())
- .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 = new_client_map_str;
- (*pageinfo).map_size = LLMP_PREF_INITIAL_MAP_SIZE;
- match new_client_sender.send(msg) {
- Ok(()) => (),
- Err(e) => println!("Error forwarding client on map: {:?}", e),
- };
- }
+ 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::())
+ .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 msg_buf = (*msg).as_slice(map)?;
- match (on_new_msg)(client_id, (*msg).tag, msg_buf)? {
- LlmpMsgHookResult::Handled => should_forward_msg = false,
- _ => (),
- }
+ if let LlmpMsgHookResult::Handled = (on_new_msg)(client_id, (*msg).tag, msg_buf)? {
+ should_forward_msg = false
+ };
if should_forward_msg {
self.forward_msg(msg)?;
}
@@ -1410,6 +1625,24 @@ where
}
}
+#[cfg(feature = "std")]
+impl Drop for LlmpBroker
+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
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct LlmpClientDescription {
@@ -1521,6 +1754,8 @@ where
}
/// 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> {
self.sender.send(msg)
}
@@ -1551,6 +1786,8 @@ where
/// A client receives a broadcast message.
/// Returns null if no message is availiable
+ /// # Safety
+ /// Should be save, unless the internal state is corrupt. Returns raw ptr.
#[inline]
pub unsafe fn recv(&mut self) -> Result