Merge branch 'dev' into main
This commit is contained in:
commit
8a60d806eb
38
.github/workflows/build_and_test.yml
vendored
38
.github/workflows/build_and_test.yml
vendored
@ -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
166
LICENSE
@ -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
201
LICENSE-APACHE
Normal 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
23
LICENSE-MIT
Normal 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.
|
@ -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
|
||||||
|
|
||||||
|
1
TODO.md
1
TODO.md
@ -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)
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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"
|
||||||
|
@ -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, ¤t_result.to_le_bytes())
|
.send_buf(_TAG_MATH_RESULT_V1, ¤t_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))
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#[cfg(windows)]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
::windows::include_bindings!();
|
::windows::include_bindings!();
|
||||||
|
@ -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() {
|
||||||
|
@ -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;
|
||||||
|
2
libafl/src/bolts/os/mod.rs
Normal file
2
libafl/src/bolts/os/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#[cfg(unix)]
|
||||||
|
pub mod unix_signals;
|
175
libafl/src/bolts/os/unix_signals.rs
Normal file
175
libafl/src/bolts/os/unix_signals.rs
Normal 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(())
|
||||||
|
}
|
@ -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(),
|
||||||
|
@ -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,
|
||||||
{
|
{
|
||||||
|
@ -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];
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -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,
|
||||||
|
@ -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 => {
|
||||||
|
@ -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
|
||||||
))),
|
)))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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)]
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -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()
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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
110
libafl/src/stages/power.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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()?;
|
||||||
|
@ -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![],
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user