diff options
36 files changed, 277 insertions, 994 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 4d6cfd1..600bdb7 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "a89115ac1105fa0c7c7d9cb5cdb479af36031aff" + "sha1": "4b33ad40af65972c122a16180f51278830dff0a5" } } @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --config cargo2android.json. +// This file is generated by cargo2android.py --run --dependencies --device --features arbitrary-derive --patch patches/Android.bp.patch. // Do not modify this file as changes will be overridden on upgrade. package { @@ -38,13 +38,19 @@ rust_library_rlib { name: "liblibfuzzer_sys", host_supported: true, crate_name: "libfuzzer_sys", - cargo_env_compat: true, - cargo_pkg_version: "0.4.2", srcs: ["src/lib.rs"], edition: "2018", features: ["arbitrary-derive"], - rustlibs: [ + rlibs: [ "libarbitrary", - "libonce_cell", ], } + +// dependent_library ["feature_list"] +// arbitrary-1.0.0 "derive,derive_arbitrary" +// cc-1.0.67 +// derive_arbitrary-1.0.0 +// proc-macro2-1.0.26 "default,proc-macro" +// quote-1.0.9 "default,proc-macro" +// syn-1.0.68 "clone-impls,default,derive,parsing,printing,proc-macro,quote" +// unicode-xid-0.2.1 "default" diff --git a/CHANGELOG.md b/CHANGELOG.md index 77be1fd..26345b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,46 +28,6 @@ Released YYYY-MM-DD. -------------------------------------------------------------------------------- -## 0.4.2 - -Released 2020-05-26. - -### Changed - -* Improved performance of checking for whether `cargo fuzz` is requesting the - `std::fmt::Debug` output of an input or not. This is always false during - regular fuzzing, so making this check faster should give slightly better - fuzzing throughput. - --------------------------------------------------------------------------------- - -## 0.4.1 - -Released 2020-05-13. - -### Added - -* Added support for defining custom mutators. See [the documentation for the - `fuzz_mutator!` - macro](https://docs.rs/libfuzzer-sys/0.4.1/libfuzzer_sys/macro.fuzz_mutator.html) - for details. - -### Changed - -* Upgraded libfuzzer to llvm/llvm-project's 70cbc6d. - --------------------------------------------------------------------------------- - -## 0.4.0 - -Released 2021-02-24. - -### Changed - -* The public `arbitrary` dependency was updated to version 1.0. - --------------------------------------------------------------------------------- - ## 0.3.5 Released 2020-11-18. @@ -13,7 +13,7 @@ [package] edition = "2018" name = "libfuzzer-sys" -version = "0.4.2" +version = "0.4.0" authors = ["The rust-fuzz Project Developers"] description = "A wrapper around LLVM's libFuzzer runtime." readme = "./README.md" @@ -21,14 +21,6 @@ license = "MIT/Apache-2.0/NCSA" repository = "https://github.com/rust-fuzz/libfuzzer" [dependencies.arbitrary] version = "1" - -[dependencies.once_cell] -version = "1" -[dev-dependencies.flate2] -version = "1.0.20" - -[dev-dependencies.rand] -version = "0.8.3" [build-dependencies.cc] version = "1.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 72285b9..db1d7b3 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -6,25 +6,13 @@ license = "MIT/Apache-2.0/NCSA" name = "libfuzzer-sys" readme = "./README.md" repository = "https://github.com/rust-fuzz/libfuzzer" -version = "0.4.2" +version = "0.4.0" [dependencies] arbitrary = "1" -once_cell = "1" [build-dependencies] cc = "1.0" [features] arbitrary-derive = ["arbitrary/derive"] - -[workspace] -members = [ - "./example", - "./example_arbitrary", - "./example_mutator", -] - -[dev-dependencies] -flate2 = "1.0.20" -rand = "0.8.3" diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index 16fe87b..0000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - 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. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 25597d5..0000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2010 The Rust Project Developers - -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. @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/libfuzzer-sys/libfuzzer-sys-0.4.2.crate" + value: "https://static.crates.io/crates/libfuzzer-sys/libfuzzer-sys-0.4.0.crate" } - version: "0.4.2" + version: "0.4.0" license_type: NOTICE last_upgrade_date { year: 2021 - month: 6 - day: 21 + month: 4 + day: 1 } } @@ -29,7 +29,7 @@ Then add a dependency on the `fuzzer-sys` crate and your own crate: ```toml [dependencies] -libfuzzer-sys = "0.4.0" +libfuzzer-sys = "0.3.0" your_crate = { path = "../path/to/your/crate" } ``` diff --git a/cargo2android.json b/cargo2android.json deleted file mode 100644 index 01e723e..0000000 --- a/cargo2android.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "dependencies": true, - "device": true, - "features": "arbitrary-derive", - "force-rlib": true, - "lib-blocklist": [ - "fuzzer", - "stdc++" - ], - "run": true -}
\ No newline at end of file diff --git a/ci/script.sh b/ci/script.sh index 91fad80..25914fd 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -5,8 +5,6 @@ cd $(dirname $0)/.. export CARGO_TARGET_DIR=$(pwd)/target -cargo test --doc - pushd ./example cargo rustc \ --release \ @@ -41,20 +39,3 @@ RUST_LIBFUZZER_DEBUG_PATH=$(pwd)/debug_output \ cat $(pwd)/debug_output grep -q Rgb $(pwd)/debug_output popd - -pushd ./example_mutator -cargo rustc \ - --release \ - -- \ - -Cpasses='sancov' \ - -Cllvm-args=-sanitizer-coverage-level=3 \ - -Cllvm-args=-sanitizer-coverage-trace-compares \ - -Cllvm-args=-sanitizer-coverage-inline-8bit-counters \ - -Cllvm-args=-sanitizer-coverage-stack-depth \ - -Cllvm-args=-sanitizer-coverage-trace-geps \ - -Cllvm-args=-sanitizer-coverage-prune-blocks=0 \ - -Zsanitizer=address -(! $CARGO_TARGET_DIR/release/example_mutator -runs=10000000) -popd - -echo "All good!" diff --git a/libfuzzer/FuzzerBuiltins.h b/libfuzzer/FuzzerBuiltins.h index ce0bd5c..4c0ada8 100644 --- a/libfuzzer/FuzzerBuiltins.h +++ b/libfuzzer/FuzzerBuiltins.h @@ -26,6 +26,7 @@ inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); } +inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); } inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); } } // namespace fuzzer diff --git a/libfuzzer/FuzzerBuiltinsMsvc.h b/libfuzzer/FuzzerBuiltinsMsvc.h index ab191b6..c5bec97 100644 --- a/libfuzzer/FuzzerBuiltinsMsvc.h +++ b/libfuzzer/FuzzerBuiltinsMsvc.h @@ -52,6 +52,12 @@ inline uint32_t Clzll(uint64_t X) { return 64; } +inline uint32_t Clz(uint32_t X) { + unsigned long LeadZeroIdx = 0; + if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx; + return 32; +} + inline int Popcountll(unsigned long long X) { #if !defined(_M_ARM) && !defined(_M_X64) return __popcnt(X) + __popcnt(X >> 32); diff --git a/libfuzzer/FuzzerCorpus.h b/libfuzzer/FuzzerCorpus.h index f8c1260..daea4f5 100644 --- a/libfuzzer/FuzzerCorpus.h +++ b/libfuzzer/FuzzerCorpus.h @@ -44,7 +44,7 @@ struct InputInfo { // Power schedule. bool NeedsEnergyUpdate = false; double Energy = 0.0; - double SumIncidence = 0.0; + size_t SumIncidence = 0; Vector<std::pair<uint32_t, uint16_t>> FeatureFreqs; // Delete feature Idx and its frequency from FeatureFreqs. @@ -74,28 +74,27 @@ struct InputInfo { void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime, std::chrono::microseconds AverageUnitExecutionTime) { Energy = 0.0; - SumIncidence = 0.0; + SumIncidence = 0; // Apply add-one smoothing to locally discovered features. for (auto F : FeatureFreqs) { - double LocalIncidence = F.second + 1; - Energy -= LocalIncidence * log(LocalIncidence); + size_t LocalIncidence = F.second + 1; + Energy -= LocalIncidence * logl(LocalIncidence); SumIncidence += LocalIncidence; } // Apply add-one smoothing to locally undiscovered features. - // PreciseEnergy -= 0; // since log(1.0) == 0) - SumIncidence += - static_cast<double>(GlobalNumberOfFeatures - FeatureFreqs.size()); + // PreciseEnergy -= 0; // since logl(1.0) == 0) + SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size()); // Add a single locally abundant feature apply add-one smoothing. - double AbdIncidence = static_cast<double>(NumExecutedMutations + 1); - Energy -= AbdIncidence * log(AbdIncidence); + size_t AbdIncidence = NumExecutedMutations + 1; + Energy -= AbdIncidence * logl(AbdIncidence); SumIncidence += AbdIncidence; // Normalize. if (SumIncidence != 0) - Energy = Energy / SumIncidence + log(SumIncidence); + Energy = (Energy / SumIncidence) + logl(SumIncidence); if (ScalePerExecTime) { // Scaling to favor inputs with lower execution time. @@ -214,8 +213,6 @@ public: assert(!U.empty()); if (FeatureDebug) Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); - // Inputs.size() is cast to uint32_t below. - assert(Inputs.size() < std::numeric_limits<uint32_t>::max()); Inputs.push_back(new InputInfo()); InputInfo &II = *Inputs.back(); II.U = U; @@ -227,7 +224,7 @@ public: II.HasFocusFunction = HasFocusFunction; // Assign maximal energy to the new seed. II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size()); - II.SumIncidence = static_cast<double>(RareFeatures.size()); + II.SumIncidence = RareFeatures.size(); II.NeedsEnergyUpdate = false; std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end()); ComputeSHA1(U.data(), U.size(), II.Sha1); @@ -402,7 +399,7 @@ public: // Zero energy seeds will never be fuzzed and remain zero energy. if (II->Energy > 0.0) { II->SumIncidence += 1; - II->Energy += log(II->SumIncidence) / II->SumIncidence; + II->Energy += logl(II->SumIncidence) / II->SumIncidence; } } @@ -429,8 +426,7 @@ public: NumUpdatedFeatures++; if (FeatureDebug) Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); - // Inputs.size() is guaranteed to be less than UINT32_MAX by AddToCorpus. - SmallestElementPerFeature[Idx] = static_cast<uint32_t>(Inputs.size()); + SmallestElementPerFeature[Idx] = Inputs.size(); InputSizesPerFeature[Idx] = NewSize; return true; } @@ -468,7 +464,7 @@ private: static const bool FeatureDebug = false; - uint32_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } void ValidateFeatureSet() { if (FeatureDebug) @@ -543,11 +539,9 @@ private: if (VanillaSchedule) { for (size_t i = 0; i < N; i++) - Weights[i] = - Inputs[i]->NumFeatures - ? static_cast<double>((i + 1) * - (Inputs[i]->HasFocusFunction ? 1000 : 1)) - : 0.; + Weights[i] = Inputs[i]->NumFeatures + ? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1) + : 0.; } if (FeatureDebug) { diff --git a/libfuzzer/FuzzerDataFlowTrace.cpp b/libfuzzer/FuzzerDataFlowTrace.cpp index 23d4225..0e9cdf7 100644 --- a/libfuzzer/FuzzerDataFlowTrace.cpp +++ b/libfuzzer/FuzzerDataFlowTrace.cpp @@ -60,7 +60,6 @@ bool BlockCoverage::AppendCoverage(std::istream &IN) { CoveredBlocks.push_back(BB); } if (CoveredBlocks.empty()) return false; - // Ensures no CoverageVector is longer than UINT32_MAX. uint32_t NumBlocks = CoveredBlocks.back(); CoveredBlocks.pop_back(); for (auto BB : CoveredBlocks) @@ -201,8 +200,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx, FunctionNames[FocusFuncIdx].c_str()); for (size_t i = 0; i < NumFunctions; i++) { - if (Weights[i] == 0.0) - continue; + if (!Weights[i]) continue; Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i, Weights[i], Coverage.GetNumberOfBlocks(i), Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0), diff --git a/libfuzzer/FuzzerDataFlowTrace.h b/libfuzzer/FuzzerDataFlowTrace.h index 07c03bb..d6e3de3 100644 --- a/libfuzzer/FuzzerDataFlowTrace.h +++ b/libfuzzer/FuzzerDataFlowTrace.h @@ -42,8 +42,7 @@ int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, const Vector<SizedFile> &CorporaFiles); class BlockCoverage { -public: - // These functions guarantee no CoverageVector is longer than UINT32_MAX. + public: bool AppendCoverage(std::istream &IN); bool AppendCoverage(const std::string &S); @@ -51,8 +50,7 @@ public: uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) { auto It = Functions.find(FunctionId); - if (It == Functions.end()) - return 0; + if (It == Functions.end()) return 0; const auto &Counters = It->second; if (BasicBlockId < Counters.size()) return Counters[BasicBlockId]; @@ -63,7 +61,7 @@ public: auto It = Functions.find(FunctionId); if (It == Functions.end()) return 0; const auto &Counters = It->second; - return static_cast<uint32_t>(Counters.size()); + return Counters.size(); } uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) { @@ -80,7 +78,8 @@ public: Vector<double> FunctionWeights(size_t NumFunctions) const; void clear() { Functions.clear(); } -private: + private: + typedef Vector<uint32_t> CoverageVector; uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const { @@ -92,8 +91,7 @@ private: } uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const { - return static_cast<uint32_t>(Counters.size()) - - NumberOfCoveredBlocks(Counters); + return Counters.size() - NumberOfCoveredBlocks(Counters); } uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const { diff --git a/libfuzzer/FuzzerDictionary.h b/libfuzzer/FuzzerDictionary.h index db55907..301c5d9 100644 --- a/libfuzzer/FuzzerDictionary.h +++ b/libfuzzer/FuzzerDictionary.h @@ -23,14 +23,12 @@ template <size_t kMaxSizeT> class FixedWord { public: static const size_t kMaxSize = kMaxSizeT; FixedWord() {} - FixedWord(const uint8_t *B, size_t S) { Set(B, S); } + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } - void Set(const uint8_t *B, size_t S) { - static_assert(kMaxSizeT <= std::numeric_limits<uint8_t>::max(), - "FixedWord::kMaxSizeT cannot fit in a uint8_t."); + void Set(const uint8_t *B, uint8_t S) { assert(S <= kMaxSize); memcpy(Data, B, S); - Size = static_cast<uint8_t>(S); + Size = S; } bool operator==(const FixedWord<kMaxSize> &w) const { diff --git a/libfuzzer/FuzzerDriver.cpp b/libfuzzer/FuzzerDriver.cpp index ceaa907..447cafc 100644 --- a/libfuzzer/FuzzerDriver.cpp +++ b/libfuzzer/FuzzerDriver.cpp @@ -159,14 +159,14 @@ static bool ParseOneFlag(const char *Param) { const char *Str = FlagValue(Param, Name); if (Str) { if (FlagDescriptions[F].IntFlag) { - auto Val = MyStol(Str); - *FlagDescriptions[F].IntFlag = static_cast<int>(Val); + int Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = Val; if (Flags.verbosity >= 2) Printf("Flag: %s %d\n", Name, Val); return true; } else if (FlagDescriptions[F].UIntFlag) { - auto Val = std::stoul(Str); - *FlagDescriptions[F].UIntFlag = static_cast<unsigned int>(Val); + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; if (Flags.verbosity >= 2) Printf("Flag: %s %u\n", Name, Val); return true; @@ -789,8 +789,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { unsigned Seed = Flags.seed; // Initialize Seed. if (Seed == 0) - Seed = static_cast<unsigned>( - std::chrono::system_clock::now().time_since_epoch().count() + GetPid()); + Seed = + std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); if (Flags.verbosity) Printf("INFO: Seed: %u\n", Seed); diff --git a/libfuzzer/FuzzerFork.cpp b/libfuzzer/FuzzerFork.cpp index 5134a5d..84725d2 100644 --- a/libfuzzer/FuzzerFork.cpp +++ b/libfuzzer/FuzzerFork.cpp @@ -142,9 +142,7 @@ struct GlobalEnv { CollectDFT(SF); } auto Time2 = std::chrono::system_clock::now(); - auto DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count(); - assert(DftTimeInSeconds < std::numeric_limits<int>::max()); - Job->DftTimeInSeconds = static_cast<int>(DftTimeInSeconds); + Job->DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count(); } if (!Seeds.empty()) { Job->SeedListPath = @@ -316,11 +314,8 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, Env.Files.push_back(File.File); } else { auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); - Set<uint32_t> NewFeatures, NewCov; - CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, Env.Features, - &NewFeatures, Env.Cov, &NewCov, CFPath, false); - Env.Features.insert(NewFeatures.begin(), NewFeatures.end()); - Env.Cov.insert(NewFeatures.begin(), NewFeatures.end()); + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, + {}, &Env.Cov, CFPath, false); RemoveFile(CFPath); } Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, diff --git a/libfuzzer/FuzzerIO.cpp b/libfuzzer/FuzzerIO.cpp index 7f149ac..54a7219 100644 --- a/libfuzzer/FuzzerIO.cpp +++ b/libfuzzer/FuzzerIO.cpp @@ -90,9 +90,8 @@ void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) { fclose(Out); } -void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, long *Epoch, - size_t MaxSize, bool ExitOnError, - Vector<std::string> *VPaths) { +void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, + long *Epoch, size_t MaxSize, bool ExitOnError) { long E = Epoch ? *Epoch : 0; Vector<std::string> Files; ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); @@ -104,14 +103,12 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, long *Epoch, if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024) Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path); auto S = FileToVector(X, MaxSize, ExitOnError); - if (!S.empty()) { + if (!S.empty()) V->push_back(S); - if (VPaths) - VPaths->push_back(X); - } } } + void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) { Vector<std::string> Files; ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); diff --git a/libfuzzer/FuzzerIO.h b/libfuzzer/FuzzerIO.h index bde1826..abd2511 100644 --- a/libfuzzer/FuzzerIO.h +++ b/libfuzzer/FuzzerIO.h @@ -32,9 +32,8 @@ void WriteToFile(const Unit &U, const std::string &Path); void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path); void AppendToFile(const std::string &Data, const std::string &Path); -void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, long *Epoch, - size_t MaxSize, bool ExitOnError, - Vector<std::string> *VPaths = 0); +void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, + long *Epoch, size_t MaxSize, bool ExitOnError); // Returns "Dir/FileName" or equivalent for the current OS. std::string DirPlusFile(const std::string &DirPath, diff --git a/libfuzzer/FuzzerLoop.cpp b/libfuzzer/FuzzerLoop.cpp index 86a78ab..6e3bf44 100644 --- a/libfuzzer/FuzzerLoop.cpp +++ b/libfuzzer/FuzzerLoop.cpp @@ -414,25 +414,19 @@ void Fuzzer::RereadOutputCorpus(size_t MaxSize) { if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; Vector<Unit> AdditionalCorpus; - Vector<std::string> AdditionalCorpusPaths; - ReadDirToVectorOfUnits( - Options.OutputCorpus.c_str(), &AdditionalCorpus, - &EpochOfLastReadOfOutputCorpus, MaxSize, - /*ExitOnError*/ false, - (Options.Verbosity >= 2 ? &AdditionalCorpusPaths : nullptr)); + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false); if (Options.Verbosity >= 2) Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); bool Reloaded = false; - for (size_t i = 0; i != AdditionalCorpus.size(); ++i) { - auto &U = AdditionalCorpus[i]; + for (auto &U : AdditionalCorpus) { if (U.size() > MaxSize) U.resize(MaxSize); if (!Corpus.HasUnit(U)) { if (RunOne(U.data(), U.size())) { CheckExitOnSrcPosOrItem(); Reloaded = true; - if (Options.Verbosity >= 2) - Printf("Reloaded %s\n", AdditionalCorpusPaths[i].c_str()); } } } @@ -446,9 +440,8 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) { if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && secondsSinceProcessStartUp() >= 2) PrintStats("pulse "); - auto Threshhold = - static_cast<long>(static_cast<double>(TimeOfLongestUnitInSeconds) * 1.1); - if (TimeOfUnit > Threshhold && TimeOfUnit >= Options.ReportSlowUnits) { + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { TimeOfLongestUnitInSeconds = TimeOfUnit; Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); @@ -508,8 +501,6 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, bool *FoundUniqFeatures) { if (!Size) return false; - // Largest input length should be INT_MAX. - assert(Size < std::numeric_limits<uint32_t>::max()); ExecuteCallback(Data, Size); auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime); @@ -517,8 +508,8 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, UniqFeatureSetTmp.clear(); size_t FoundUniqFeaturesOfII = 0; size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); - TPC.CollectFeatures([&](uint32_t Feature) { - if (Corpus.AddFeature(Feature, static_cast<uint32_t>(Size), Options.Shrink)) + TPC.CollectFeatures([&](size_t Feature) { + if (Corpus.AddFeature(Feature, Size, Options.Shrink)) UniqFeatureSetTmp.push_back(Feature); if (Options.Entropic) Corpus.UpdateFeatureFrequency(II, Feature); @@ -584,10 +575,7 @@ static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2); } -// This method is not inlined because it would cause a test to fail where it -// is part of the stack unwinding. See D97975 for details. -ATTRIBUTE_NOINLINE void Fuzzer::ExecuteCallback(const uint8_t *Data, - size_t Size) { +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { TPC.RecordInitialStack(); TotalNumberOfRuns++; assert(InFuzzingThread()); diff --git a/libfuzzer/FuzzerMerge.cpp b/libfuzzer/FuzzerMerge.cpp index 162453c..e3ad8b3 100644 --- a/libfuzzer/FuzzerMerge.cpp +++ b/libfuzzer/FuzzerMerge.cpp @@ -82,9 +82,9 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { while (std::getline(IS, Line, '\n')) { std::istringstream ISS1(Line); std::string Marker; - uint32_t N; - if (!(ISS1 >> Marker) || !(ISS1 >> N)) - return false; + size_t N; + ISS1 >> Marker; + ISS1 >> N; if (Marker == "STARTED") { // STARTED FILE_ID FILE_SIZE if (ExpectedStartMarker != N) @@ -137,8 +137,6 @@ size_t Merger::Merge(const Set<uint32_t> &InitialFeatures, const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov, Vector<std::string> *NewFiles) { NewFiles->clear(); - NewFeatures->clear(); - NewCov->clear(); assert(NumFilesInFirstCorpus <= Files.size()); Set<uint32_t> AllFeatures = InitialFeatures; diff --git a/libfuzzer/FuzzerMutate.cpp b/libfuzzer/FuzzerMutate.cpp index 4650f1b..cf34a9f 100644 --- a/libfuzzer/FuzzerMutate.cpp +++ b/libfuzzer/FuzzerMutate.cpp @@ -61,20 +61,14 @@ MutationDispatcher::MutationDispatcher(Random &Rand, } static char RandCh(Random &Rand) { - if (Rand.RandBool()) - return static_cast<char>(Rand(256)); + if (Rand.RandBool()) return Rand(256); const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; return Special[Rand(sizeof(Special) - 1)]; } size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize) { - if (EF->__msan_unpoison) - EF->__msan_unpoison(Data, Size); - if (EF->__msan_unpoison_param) - EF->__msan_unpoison_param(4); - return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, - Rand.Rand<unsigned int>()); + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); } size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, @@ -87,18 +81,8 @@ size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, return 0; CustomCrossOverInPlaceHere.resize(MaxSize); auto &U = CustomCrossOverInPlaceHere; - - if (EF->__msan_unpoison) { - EF->__msan_unpoison(Data, Size); - EF->__msan_unpoison(Other.data(), Other.size()); - EF->__msan_unpoison(U.data(), U.size()); - } - if (EF->__msan_unpoison_param) - EF->__msan_unpoison_param(7); size_t NewSize = EF->LLVMFuzzerCustomCrossOver( - Data, Size, Other.data(), Other.size(), U.data(), U.size(), - Rand.Rand<unsigned int>()); - + Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); if (!NewSize) return 0; assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); @@ -151,8 +135,7 @@ size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, // Insert new values at Data[Idx]. memmove(Data + Idx + N, Data + Idx, Size - Idx); // Give preference to 0x00 and 0xff. - uint8_t Byte = static_cast<uint8_t>( - Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255)); + uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); for (size_t i = 0; i < N; i++) Data[Idx + i] = Byte; return Size + N; @@ -195,8 +178,7 @@ size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, Size += W.size(); } else { // Overwrite some bytes with W. if (W.size() > Size) return 0; - size_t Idx = - UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1 - W.size()); + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); memcpy(Data + Idx, W.data(), W.size()); } return Size; @@ -245,8 +227,8 @@ DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( T Arg1, T Arg2, const uint8_t *Data, size_t Size) { if (Rand.RandBool()) Arg1 = Bswap(Arg1); if (Rand.RandBool()) Arg2 = Bswap(Arg2); - T Arg1Mutation = static_cast<T>(Arg1 + Rand(-1, 1)); - T Arg2Mutation = static_cast<T>(Arg2 + Rand(-1, 1)); + T Arg1Mutation = Arg1 + Rand(-1, 1); + T Arg2Mutation = Arg2 + Rand(-1, 1); return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, sizeof(Arg1), Data, Size); } @@ -263,23 +245,23 @@ size_t MutationDispatcher::Mutate_AddWordFromTORC( DictionaryEntry DE; switch (Rand(4)) { case 0: { - auto X = TPC.TORC8.Get(Rand.Rand<size_t>()); + auto X = TPC.TORC8.Get(Rand.Rand()); DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 1: { - auto X = TPC.TORC4.Get(Rand.Rand<size_t>()); + auto X = TPC.TORC4.Get(Rand.Rand()); if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, Size); else DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 2: { - auto X = TPC.TORCW.Get(Rand.Rand<size_t>()); + auto X = TPC.TORCW.Get(Rand.Rand()); DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 3: if (Options.UseMemmem) { - auto X = TPC.MMT.Get(Rand.Rand<size_t>()); - DE = DictionaryEntry(X); + auto X = TPC.MMT.Get(Rand.Rand()); + DE = DictionaryEntry(X); } break; default: assert(0); @@ -405,12 +387,12 @@ size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { assert(Off + sizeof(T) <= Size); T Val; if (Off < 64 && !Rand(4)) { - Val = static_cast<T>(Size); + Val = Size; if (Rand.RandBool()) Val = Bswap(Val); } else { memcpy(&Val, Data + Off, sizeof(Val)); - T Add = static_cast<T>(Rand(21)); + T Add = Rand(21); Add -= 10; if (Rand.RandBool()) Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. @@ -480,7 +462,7 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() { assert(DE->GetW().size()); // Linear search is fine here as this happens seldom. if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) - PersistentAutoDictionary.push_back(*DE); + PersistentAutoDictionary.push_back({DE->GetW(), 1}); } } diff --git a/libfuzzer/FuzzerRandom.h b/libfuzzer/FuzzerRandom.h index ad6c07e..659283e 100644 --- a/libfuzzer/FuzzerRandom.h +++ b/libfuzzer/FuzzerRandom.h @@ -18,27 +18,18 @@ class Random : public std::minstd_rand { public: Random(unsigned int seed) : std::minstd_rand(seed) {} result_type operator()() { return this->std::minstd_rand::operator()(); } - template <typename T> - typename std::enable_if<std::is_integral<T>::value, T>::type Rand() { - return static_cast<T>(this->operator()()); - } - size_t RandBool() { return this->operator()() % 2; } + size_t Rand() { return this->operator()(); } + size_t RandBool() { return Rand() % 2; } size_t SkewTowardsLast(size_t n) { size_t T = this->operator()(n * n); - size_t Res = static_cast<size_t>(sqrt(T)); + size_t Res = sqrt(T); return Res; } - template <typename T> - typename std::enable_if<std::is_integral<T>::value, T>::type operator()(T n) { - return n ? Rand<T>() % n : 0; - } - template <typename T> - typename std::enable_if<std::is_integral<T>::value, T>::type - operator()(T From, T To) { + size_t operator()(size_t n) { return n ? Rand() % n : 0; } + intptr_t operator()(intptr_t From, intptr_t To) { assert(From < To); - auto RangeSize = static_cast<unsigned long long>(To) - - static_cast<unsigned long long>(From) + 1; - return static_cast<T>(this->operator()(RangeSize) + From); + intptr_t RangeSize = To - From + 1; + return operator()(RangeSize) + From; } }; diff --git a/libfuzzer/FuzzerSHA1.cpp b/libfuzzer/FuzzerSHA1.cpp index b05655c..2005dc7 100644 --- a/libfuzzer/FuzzerSHA1.cpp +++ b/libfuzzer/FuzzerSHA1.cpp @@ -134,13 +134,12 @@ void sha1_hashBlock(sha1nfo *s) { s->state[4] += e; } -// Adds the least significant byte of |data|. -void sha1_addUncounted(sha1nfo *s, uint32_t data) { - uint8_t *const b = (uint8_t *)s->buffer; +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; #ifdef SHA_BIG_ENDIAN - b[s->bufferOffset] = static_cast<uint8_t>(data); + b[s->bufferOffset] = data; #else - b[s->bufferOffset ^ 3] = static_cast<uint8_t>(data); + b[s->bufferOffset ^ 3] = data; #endif s->bufferOffset++; if (s->bufferOffset == BLOCK_LENGTH) { diff --git a/libfuzzer/FuzzerTracePC.cpp b/libfuzzer/FuzzerTracePC.cpp index d808b9b..91e94d8 100644 --- a/libfuzzer/FuzzerTracePC.cpp +++ b/libfuzzer/FuzzerTracePC.cpp @@ -106,15 +106,6 @@ void TracePC::PrintModuleInfo() { } if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin()) Printf("INFO: %zd Extra Counters\n", NumExtraCounters); - - size_t MaxFeatures = CollectFeatures([](uint32_t) {}); - if (MaxFeatures > std::numeric_limits<uint32_t>::max()) - Printf("WARNING: The coverage PC tables may produce up to %zu features.\n" - "This exceeds the maximum 32-bit value. Some features may be\n" - "ignored, and fuzzing may become less precise. If possible,\n" - "consider refactoring the fuzzer into several smaller fuzzers\n" - "linked against only a portion of the current target.\n", - MaxFeatures); } ATTRIBUTE_NO_SANITIZE_ALL @@ -365,7 +356,7 @@ void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, uint8_t HammingDistance = 0; for (; I < Len; I++) { if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) { - HammingDistance = static_cast<uint8_t>(Popcountll(B1[I] ^ B2[I])); + HammingDistance = Popcountll(B1[I] ^ B2[I]); break; } } diff --git a/libfuzzer/FuzzerTracePC.h b/libfuzzer/FuzzerTracePC.h index a937329..0090923 100644 --- a/libfuzzer/FuzzerTracePC.h +++ b/libfuzzer/FuzzerTracePC.h @@ -54,7 +54,7 @@ struct MemMemTable { void Add(const uint8_t *Data, size_t Size) { if (Size <= 2) return; Size = std::min(Size, Word::GetMaxSize()); - auto Idx = SimpleFastHash(Data, Size) % kSize; + size_t Idx = SimpleFastHash(Data, Size) % kSize; MemMemWords[Idx].Set(Data, Size); } const Word &Get(size_t Idx) { @@ -79,7 +79,7 @@ class TracePC { void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; } void UpdateObservedPCs(); - template <class Callback> size_t CollectFeatures(Callback CB) const; + template <class Callback> void CollectFeatures(Callback CB) const; void ResetMaps() { ValueProfileMap.Reset(); @@ -193,7 +193,7 @@ size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, Handle8bitCounter(FirstFeature, P - Begin, V); // Iterate by Step bytes at a time. - for (; P + Step <= End; P += Step) + for (; P < End; P += Step) if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P)) { Bundle = HostToLE(Bundle); for (size_t I = 0; I < Step; I++, Bundle >>= 8) @@ -234,16 +234,16 @@ unsigned CounterToFeature(T Counter) { return Bit; } -template <class Callback> // void Callback(uint32_t Feature) -ATTRIBUTE_NO_SANITIZE_ADDRESS ATTRIBUTE_NOINLINE size_t -TracePC::CollectFeatures(Callback HandleFeature) const { +template <class Callback> // void Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS +ATTRIBUTE_NOINLINE +void TracePC::CollectFeatures(Callback HandleFeature) const { auto Handle8bitCounter = [&](size_t FirstFeature, size_t Idx, uint8_t Counter) { if (UseCounters) - HandleFeature(static_cast<uint32_t>(FirstFeature + Idx * 8 + - CounterToFeature(Counter))); + HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter)); else - HandleFeature(static_cast<uint32_t>(FirstFeature + Idx)); + HandleFeature(FirstFeature + Idx); }; size_t FirstFeature = 0; @@ -263,18 +263,16 @@ TracePC::CollectFeatures(Callback HandleFeature) const { if (UseValueProfileMask) { ValueProfileMap.ForEach([&](size_t Idx) { - HandleFeature(static_cast<uint32_t>(FirstFeature + Idx)); + HandleFeature(FirstFeature + Idx); }); FirstFeature += ValueProfileMap.SizeInBits(); } // Step function, grows similar to 8 * Log_2(A). - auto StackDepthStepFunction = [](size_t A) -> size_t { - if (!A) - return A; - auto Log2 = Log(A); - if (Log2 < 3) - return A; + auto StackDepthStepFunction = [](uint32_t A) -> uint32_t { + if (!A) return A; + uint32_t Log2 = Log(A); + if (Log2 < 3) return A; Log2 -= 3; return (Log2 + 1) * 8 + ((A >> Log2) & 7); }; @@ -282,13 +280,8 @@ TracePC::CollectFeatures(Callback HandleFeature) const { assert(StackDepthStepFunction(1024 * 4) == 80); assert(StackDepthStepFunction(1024 * 1024) == 144); - if (auto MaxStackOffset = GetMaxStackOffset()) { - HandleFeature(static_cast<uint32_t>( - FirstFeature + StackDepthStepFunction(MaxStackOffset / 8))); - FirstFeature += StackDepthStepFunction(std::numeric_limits<size_t>::max()); - } - - return FirstFeature; + if (auto MaxStackOffset = GetMaxStackOffset()) + HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8)); } extern TracePC TPC; diff --git a/libfuzzer/FuzzerUtil.cpp b/libfuzzer/FuzzerUtil.cpp index 0518549..7eecb68 100644 --- a/libfuzzer/FuzzerUtil.cpp +++ b/libfuzzer/FuzzerUtil.cpp @@ -111,7 +111,7 @@ bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { char Hex[] = "0xAA"; Hex[2] = Str[Pos + 2]; Hex[3] = Str[Pos + 3]; - U->push_back(static_cast<uint8_t>(strtol(Hex, nullptr, 16))); + U->push_back(strtol(Hex, nullptr, 16)); Pos += 3; continue; } @@ -226,11 +226,10 @@ unsigned NumberOfCpuCores() { return N; } -uint64_t SimpleFastHash(const void *Data, size_t Size, uint64_t Initial) { - uint64_t Res = Initial; - const uint8_t *Bytes = static_cast<const uint8_t *>(Data); +size_t SimpleFastHash(const uint8_t *Data, size_t Size) { + size_t Res = 0; for (size_t i = 0; i < Size; i++) - Res = Res * 11 + Bytes[i]; + Res = Res * 11 + Data[i]; return Res; } diff --git a/libfuzzer/FuzzerUtil.h b/libfuzzer/FuzzerUtil.h index a188a7b..e90be08 100644 --- a/libfuzzer/FuzzerUtil.h +++ b/libfuzzer/FuzzerUtil.h @@ -88,11 +88,9 @@ std::string DisassembleCmd(const std::string &FileName); std::string SearchRegexCmd(const std::string &Regex); -uint64_t SimpleFastHash(const void *Data, size_t Size, uint64_t Initial = 0); +size_t SimpleFastHash(const uint8_t *Data, size_t Size); -inline size_t Log(size_t X) { - return static_cast<size_t>((sizeof(unsigned long long) * 8) - Clzll(X) - 1); -} +inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; } inline size_t PageSize() { return 4096; } inline uint8_t *RoundUpByPage(uint8_t *P) { diff --git a/libfuzzer/FuzzerUtilFuchsia.cpp b/libfuzzer/FuzzerUtilFuchsia.cpp index 5034b4a..af43946 100644 --- a/libfuzzer/FuzzerUtilFuchsia.cpp +++ b/libfuzzer/FuzzerUtilFuchsia.cpp @@ -515,7 +515,7 @@ int ExecuteCommand(const Command &Cmd) { return rc; } - return static_cast<int>(Info.return_code); + return Info.return_code; } bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) { diff --git a/libfuzzer/FuzzerUtilPosix.cpp b/libfuzzer/FuzzerUtilPosix.cpp index 0446d73..afb7334 100644 --- a/libfuzzer/FuzzerUtilPosix.cpp +++ b/libfuzzer/FuzzerUtilPosix.cpp @@ -77,13 +77,10 @@ static void SetSigaction(int signum, return; } - struct sigaction new_sigact = {}; - // Address sanitizer needs SA_ONSTACK (causing the signal handler to run on a - // dedicated stack) in order to be able to detect stack overflows; keep the - // flag if it's set. - new_sigact.sa_flags = SA_SIGINFO | (sigact.sa_flags & SA_ONSTACK); - new_sigact.sa_sigaction = callback; - if (sigaction(signum, &new_sigact, nullptr)) { + sigact = {}; + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { Printf("libFuzzer: sigaction failed with %d\n", errno); exit(1); } diff --git a/libfuzzer/tests/FuzzedDataProviderUnittest.cpp b/libfuzzer/tests/FuzzedDataProviderUnittest.cpp index ea6774e..99d9d8e 100644 --- a/libfuzzer/tests/FuzzedDataProviderUnittest.cpp +++ b/libfuzzer/tests/FuzzedDataProviderUnittest.cpp @@ -283,20 +283,6 @@ TEST(FuzzedDataProvider, ConsumeBool) { EXPECT_EQ(false, DataProv.ConsumeBool()); } -TEST(FuzzedDataProvider, PickValueInStdArray) { - FuzzedDataProvider DataProv(Data, sizeof(Data)); - const std::array<int, 5> Array = {1, 2, 3, 4, 5}; - EXPECT_EQ(5, DataProv.PickValueInArray(Array)); - EXPECT_EQ(2, DataProv.PickValueInArray(Array)); - EXPECT_EQ(2, DataProv.PickValueInArray(Array)); - EXPECT_EQ(3, DataProv.PickValueInArray(Array)); - EXPECT_EQ(3, DataProv.PickValueInArray(Array)); - EXPECT_EQ(3, DataProv.PickValueInArray(Array)); - EXPECT_EQ(1, DataProv.PickValueInArray(Array)); - EXPECT_EQ(3, DataProv.PickValueInArray(Array)); - EXPECT_EQ(2, DataProv.PickValueInArray(Array)); -} - TEST(FuzzedDataProvider, PickValueInArray) { FuzzedDataProvider DataProv(Data, sizeof(Data)); const int Array[] = {1, 2, 3, 4, 5}; diff --git a/libfuzzer/tests/FuzzerUnittest.cpp b/libfuzzer/tests/FuzzerUnittest.cpp index 974a01f..d2b5cbb 100644 --- a/libfuzzer/tests/FuzzerUnittest.cpp +++ b/libfuzzer/tests/FuzzerUnittest.cpp @@ -614,80 +614,73 @@ TEST(Corpus, Distribution) { } } -template <typename T> void EQ(const Vector<T> &A, const Vector<T> &B) { - EXPECT_EQ(A, B); +TEST(Merge, Bad) { + const char *kInvalidInputs[] = { + "", + "x", + "3\nx", + "2\n3", + "2\n2", + "2\n2\nA\n", + "2\n2\nA\nB\nC\n", + "0\n0\n", + "1\n1\nA\nFT 0", + "1\n1\nA\nSTARTED 1", + }; + Merger M; + for (auto S : kInvalidInputs) { + // fprintf(stderr, "TESTING:\n%s\n", S); + EXPECT_FALSE(M.Parse(S, false)); + } } -template <typename T> void EQ(const Set<T> &A, const Vector<T> &B) { - EXPECT_EQ(A, Set<T>(B.begin(), B.end())); +void EQ(const Vector<uint32_t> &A, const Vector<uint32_t> &B) { + EXPECT_EQ(A, B); } -void EQ(const Vector<MergeFileInfo> &A, const Vector<std::string> &B) { - Set<std::string> a; - for (const auto &File : A) - a.insert(File.Name); +void EQ(const Vector<std::string> &A, const Vector<std::string> &B) { + Set<std::string> a(A.begin(), A.end()); Set<std::string> b(B.begin(), B.end()); EXPECT_EQ(a, b); } -#define TRACED_EQ(A, ...) \ - { \ - SCOPED_TRACE(#A); \ - EQ(A, __VA_ARGS__); \ - } - -TEST(Merger, Parse) { +static void Merge(const std::string &Input, + const Vector<std::string> Result, + size_t NumNewFeatures) { Merger M; + Vector<std::string> NewFiles; + Set<uint32_t> NewFeatures, NewCov; + EXPECT_TRUE(M.Parse(Input, true)); + EXPECT_EQ(NumNewFeatures, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles)); + EQ(NewFiles, Result); +} - const char *kInvalidInputs[] = { - // Bad file numbers - "", - "x", - "0\n0", - "3\nx", - "2\n3", - "2\n2", - // Bad file names - "2\n2\nA\n", - "2\n2\nA\nB\nC\n", - // Unknown markers - "2\n1\nA\nSTARTED 0\nBAD 0 0x0", - // Bad file IDs - "1\n1\nA\nSTARTED 1", - "2\n1\nA\nSTARTED 0\nFT 1 0x0", - }; - for (auto S : kInvalidInputs) { - SCOPED_TRACE(S); - EXPECT_FALSE(M.Parse(S, false)); - } +TEST(Merge, Good) { + Merger M; - // Parse initial control file EXPECT_TRUE(M.Parse("1\n0\nAA\n", false)); - ASSERT_EQ(M.Files.size(), 1U); + EXPECT_EQ(M.Files.size(), 1U); EXPECT_EQ(M.NumFilesInFirstCorpus, 0U); EXPECT_EQ(M.Files[0].Name, "AA"); EXPECT_TRUE(M.LastFailure.empty()); EXPECT_EQ(M.FirstNotProcessedFile, 0U); - // Parse control file that failed on first attempt EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false)); - ASSERT_EQ(M.Files.size(), 2U); + EXPECT_EQ(M.Files.size(), 2U); EXPECT_EQ(M.NumFilesInFirstCorpus, 1U); EXPECT_EQ(M.Files[0].Name, "AA"); EXPECT_EQ(M.Files[1].Name, "BB"); EXPECT_EQ(M.LastFailure, "AA"); EXPECT_EQ(M.FirstNotProcessedFile, 1U); - // Parse control file that failed on later attempt EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n" - "STARTED 0 1000\n" - "FT 0 1 2 3\n" - "STARTED 1 1001\n" - "FT 1 4 5 6 \n" - "STARTED 2 1002\n" - "", - true)); - ASSERT_EQ(M.Files.size(), 3U); + "STARTED 0 1000\n" + "FT 0 1 2 3\n" + "STARTED 1 1001\n" + "FT 1 4 5 6 \n" + "STARTED 2 1002\n" + "", true)); + EXPECT_EQ(M.Files.size(), 3U); EXPECT_EQ(M.NumFilesInFirstCorpus, 1U); EXPECT_EQ(M.Files[0].Name, "AA"); EXPECT_EQ(M.Files[0].Size, 1000U); @@ -697,171 +690,82 @@ TEST(Merger, Parse) { EXPECT_EQ(M.Files[2].Size, 1002U); EXPECT_EQ(M.LastFailure, "C"); EXPECT_EQ(M.FirstNotProcessedFile, 3U); - TRACED_EQ(M.Files[0].Features, {1, 2, 3}); - TRACED_EQ(M.Files[1].Features, {4, 5, 6}); - - // Parse control file without features or PCs - EXPECT_TRUE(M.Parse("2\n0\nAA\nBB\n" - "STARTED 0 1000\n" - "FT 0\n" - "COV 0\n" - "STARTED 1 1001\n" - "FT 1\n" - "COV 1\n" - "", - true)); - ASSERT_EQ(M.Files.size(), 2U); - EXPECT_EQ(M.NumFilesInFirstCorpus, 0U); - EXPECT_TRUE(M.LastFailure.empty()); - EXPECT_EQ(M.FirstNotProcessedFile, 2U); - EXPECT_TRUE(M.Files[0].Features.empty()); - EXPECT_TRUE(M.Files[0].Cov.empty()); - EXPECT_TRUE(M.Files[1].Features.empty()); - EXPECT_TRUE(M.Files[1].Cov.empty()); + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + + + Vector<std::string> NewFiles; + Set<uint32_t> NewFeatures, NewCov; - // Parse features and PCs EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n" - "STARTED 0 1000\n" - "FT 0 1 2 3\n" - "COV 0 11 12 13\n" - "STARTED 1 1001\n" - "FT 1 4 5 6\n" - "COV 1 7 8 9\n" - "STARTED 2 1002\n" - "FT 2 6 1 3\n" - "COV 2 16 11 13\n" - "", - true)); - ASSERT_EQ(M.Files.size(), 3U); + "STARTED 0 1000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3 \n" + "", true)); + EXPECT_EQ(M.Files.size(), 3U); EXPECT_EQ(M.NumFilesInFirstCorpus, 2U); EXPECT_TRUE(M.LastFailure.empty()); EXPECT_EQ(M.FirstNotProcessedFile, 3U); - TRACED_EQ(M.Files[0].Features, {1, 2, 3}); - TRACED_EQ(M.Files[0].Cov, {11, 12, 13}); - TRACED_EQ(M.Files[1].Features, {4, 5, 6}); - TRACED_EQ(M.Files[1].Cov, {7, 8, 9}); - TRACED_EQ(M.Files[2].Features, {1, 3, 6}); - TRACED_EQ(M.Files[2].Cov, {16}); -} - -TEST(Merger, Merge) { - Merger M; - Set<uint32_t> Features, NewFeatures; - Set<uint32_t> Cov, NewCov; - Vector<std::string> NewFiles; + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + EQ(M.Files[2].Features, {1, 3, 6}); + EXPECT_EQ(0U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles)); + EQ(NewFiles, {}); - // Adds new files and features - EXPECT_TRUE(M.Parse("3\n0\nA\nB\nC\n" - "STARTED 0 1000\n" - "FT 0 1 2 3\n" - "STARTED 1 1001\n" - "FT 1 4 5 6 \n" - "STARTED 2 1002\n" - "FT 2 6 1 3\n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 6U); - TRACED_EQ(M.Files, {"A", "B", "C"}); - TRACED_EQ(NewFiles, {"A", "B"}); - TRACED_EQ(NewFeatures, {1, 2, 3, 4, 5, 6}); - - // Doesn't return features or files in the initial corpus. EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n" - "STARTED 0 1000\n" - "FT 0 1 2 3\n" - "STARTED 1 1001\n" - "FT 1 4 5 6 \n" - "STARTED 2 1002\n" - "FT 2 6 1 3\n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 3U); - TRACED_EQ(M.Files, {"A", "B", "C"}); - TRACED_EQ(NewFiles, {"B"}); - TRACED_EQ(NewFeatures, {4, 5, 6}); - - // No new features, so no new files - EXPECT_TRUE(M.Parse("3\n2\nA\nB\nC\n" - "STARTED 0 1000\n" - "FT 0 1 2 3\n" - "STARTED 1 1001\n" - "FT 1 4 5 6 \n" - "STARTED 2 1002\n" - "FT 2 6 1 3\n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 0U); - TRACED_EQ(M.Files, {"A", "B", "C"}); - TRACED_EQ(NewFiles, {}); - TRACED_EQ(NewFeatures, {}); - - // Can pass initial features and coverage. - Features = {1, 2, 3}; - Cov = {}; - EXPECT_TRUE(M.Parse("2\n0\nA\nB\n" - "STARTED 0 1000\n" - "FT 0 1 2 3\n" - "STARTED 1 1001\n" - "FT 1 4 5 6\n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 3U); - TRACED_EQ(M.Files, {"A", "B"}); - TRACED_EQ(NewFiles, {"B"}); - TRACED_EQ(NewFeatures, {4, 5, 6}); - Features.clear(); - Cov.clear(); - - // Parse smaller files first - EXPECT_TRUE(M.Parse("3\n0\nA\nB\nC\n" - "STARTED 0 2000\n" - "FT 0 1 2 3\n" - "STARTED 1 1001\n" - "FT 1 4 5 6 \n" - "STARTED 2 1002\n" - "FT 2 6 1 3 \n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 6U); - TRACED_EQ(M.Files, {"B", "C", "A"}); - TRACED_EQ(NewFiles, {"B", "C", "A"}); - TRACED_EQ(NewFeatures, {1, 2, 3, 4, 5, 6}); - - EXPECT_TRUE(M.Parse("4\n0\nA\nB\nC\nD\n" - "STARTED 0 2000\n" - "FT 0 1 2 3\n" - "STARTED 1 1101\n" - "FT 1 4 5 6 \n" - "STARTED 2 1102\n" - "FT 2 6 1 3 100 \n" - "STARTED 3 1000\n" - "FT 3 1 \n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 7U); - TRACED_EQ(M.Files, {"A", "B", "C", "D"}); - TRACED_EQ(NewFiles, {"D", "B", "C", "A"}); - TRACED_EQ(NewFeatures, {1, 2, 3, 4, 5, 6, 100}); - - // For same sized file, parse more features first - EXPECT_TRUE(M.Parse("4\n1\nA\nB\nC\nD\n" - "STARTED 0 2000\n" - "FT 0 4 5 6 7 8\n" - "STARTED 1 1100\n" - "FT 1 1 2 3 \n" - "STARTED 2 1100\n" - "FT 2 2 3 \n" - "STARTED 3 1000\n" - "FT 3 1 \n" - "", - true)); - EXPECT_EQ(M.Merge(Features, &NewFeatures, Cov, &NewCov, &NewFiles), 3U); - TRACED_EQ(M.Files, {"A", "B", "C", "D"}); - TRACED_EQ(NewFiles, {"D", "B"}); - TRACED_EQ(NewFeatures, {1, 2, 3}); + "STARTED 0 1000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3\n" + "", true)); + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + EQ(M.Files[2].Features, {1, 3, 6}); + EXPECT_EQ(3U, M.Merge({}, &NewFeatures, {}, &NewCov, &NewFiles)); + EQ(NewFiles, {"B"}); + + // Same as the above, but with InitialFeatures. + EXPECT_TRUE(M.Parse("2\n0\nB\nC\n" + "STARTED 0 1001\nFT 0 4 5 6 \n" + "STARTED 1 1002\nFT 1 6 1 3\n" + "", true)); + EQ(M.Files[0].Features, {4, 5, 6}); + EQ(M.Files[1].Features, {1, 3, 6}); + Set<uint32_t> InitialFeatures; + InitialFeatures.insert(1); + InitialFeatures.insert(2); + InitialFeatures.insert(3); + EXPECT_EQ(3U, M.Merge(InitialFeatures, &NewFeatures, {}, &NewCov, &NewFiles)); + EQ(NewFiles, {"B"}); } -#undef TRACED_EQ +TEST(Merge, Merge) { + + Merge("3\n1\nA\nB\nC\n" + "STARTED 0 1000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3 \n", + {"B"}, 3); + + Merge("3\n0\nA\nB\nC\n" + "STARTED 0 2000\nFT 0 1 2 3\n" + "STARTED 1 1001\nFT 1 4 5 6 \n" + "STARTED 2 1002\nFT 2 6 1 3 \n", + {"A", "B", "C"}, 6); + + Merge("4\n0\nA\nB\nC\nD\n" + "STARTED 0 2000\nFT 0 1 2 3\n" + "STARTED 1 1101\nFT 1 4 5 6 \n" + "STARTED 2 1102\nFT 2 6 1 3 100 \n" + "STARTED 3 1000\nFT 3 1 \n", + {"A", "B", "C", "D"}, 7); + + Merge("4\n1\nA\nB\nC\nD\n" + "STARTED 0 2000\nFT 0 4 5 6 7 8\n" + "STARTED 1 1100\nFT 1 1 2 3 \n" + "STARTED 2 1100\nFT 2 2 3 \n" + "STARTED 3 1000\nFT 3 1 \n", + {"B", "D"}, 3); +} TEST(DFT, BlockCoverage) { BlockCoverage Cov; diff --git a/patches/Android.bp.patch b/patches/Android.bp.patch new file mode 100644 index 0000000..c0d6fd7 --- /dev/null +++ b/patches/Android.bp.patch @@ -0,0 +1,25 @@ +diff --git a/Android.bp b/Android.bp +index 86434dd..e565b86 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -1,17 +1,15 @@ + // This file is generated by cargo2android.py --run --dependencies --device --features arbitrary-derive --patch patches/Android.bp.patch. + +-rust_library { ++rust_library_rlib { + name: "liblibfuzzer_sys", + host_supported: true, + crate_name: "libfuzzer_sys", + srcs: ["src/lib.rs"], + edition: "2018", + features: ["arbitrary-derive"], +- rustlibs: [ ++ rlibs: [ + "libarbitrary", + ], +- static_libs: ["libfuzzer"], +- shared_libs: ["libstdc++"], + } + + // dependent_library ["feature_list"] + @@ -12,14 +12,11 @@ #![deny(missing_docs, missing_debug_implementations)] pub use arbitrary; -use once_cell::sync::OnceCell; extern "C" { // We do not actually cross the FFI bound here. #[allow(improper_ctypes)] fn rust_fuzzer_test_input(input: &[u8]); - - fn LLVMFuzzerMutate(data: *mut u8, size: usize, max_size: usize) -> usize; } #[doc(hidden)] @@ -38,9 +35,6 @@ pub fn test_input_wrap(data: *const u8, size: usize) -> i32 { } #[doc(hidden)] -pub static RUST_LIBFUZZER_DEBUG_PATH: OnceCell<String> = OnceCell::new(); - -#[doc(hidden)] #[export_name = "LLVMFuzzerInitialize"] pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize { // Registers a panic hook that aborts the process before unwinding. @@ -56,14 +50,6 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize default_hook(panic_info); ::std::process::abort(); })); - - // Initialize the `RUST_LIBFUZZER_DEBUG_PATH` cell with the path so it can be - // reused with little overhead. - if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") { - RUST_LIBFUZZER_DEBUG_PATH - .set(path) - .expect("Since this is initialize it is only called once so can never fail"); - } 0 } @@ -98,9 +84,8 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize /// /// ```no_run /// #![no_main] -/// # mod foo { /// -/// use libfuzzer_sys::{arbitrary::{Arbitrary, Error, Unstructured}, fuzz_target}; +/// use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target}; /// /// #[derive(Debug)] /// pub struct Rgb { @@ -109,8 +94,11 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize /// b: u8, /// } /// -/// impl<'a> Arbitrary<'a> for Rgb { -/// fn arbitrary(raw: &mut Unstructured<'a>) -> Result<Self, Error> { +/// impl Arbitrary for Rgb { +/// fn arbitrary<U>(raw: &mut U) -> Result<Self, U::Error> +/// where +/// U: Unstructured + ?Sized +/// { /// let mut buf = [0; 3]; /// raw.fill_buffer(&mut buf)?; /// let r = buf[0]; @@ -124,27 +112,16 @@ pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize /// fuzz_target!(|color: Rgb| { /// my_crate::convert_color(color); /// }); -/// # mod my_crate { -/// # use super::Rgb; -/// # pub fn convert_color(_: Rgb) {} -/// # } -/// # } -/// ``` -/// -/// You can also enable the `arbitrary` crate's custom derive via this crate's -/// `"arbitrary-derive"` cargo feature. +/// # mod my_crate { fn convert_color(_: super::Rgb) {} } #[macro_export] macro_rules! fuzz_target { (|$bytes:ident| $body:block) => { - /// Auto-generated function #[no_mangle] pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) { // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug // formatting of the input to that file. This is only intended for // `cargo fuzz`'s use! - - // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization. - if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() { + if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") { use std::io::Write; let mut file = std::fs::File::create(path) .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file"); @@ -162,10 +139,9 @@ macro_rules! fuzz_target { }; (|$data:ident: $dty: ty| $body:block) => { - /// Auto-generated function #[no_mangle] pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) { - use $crate::arbitrary::{Arbitrary, Unstructured}; + use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; // Early exit if we don't have enough bytes for the `Arbitrary` // implementation. This helps the fuzzer avoid exploring all the @@ -183,9 +159,7 @@ macro_rules! fuzz_target { // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug // formatting of the input to that file. This is only intended for // `cargo fuzz`'s use! - - // `RUST_LIBFUZZER_DEBUG_PATH` is set in initialization. - if let Some(path) = $crate::RUST_LIBFUZZER_DEBUG_PATH.get() { + if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") { use std::io::Write; let mut file = std::fs::File::create(path) .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file"); @@ -206,221 +180,3 @@ macro_rules! fuzz_target { } }; } - -/// Define a custom mutator. -/// -/// This is optional, and libFuzzer will use its own, default mutation strategy -/// if this is not provided. -/// -/// You might consider using a custom mutator when your fuzz target is very -/// particular about the shape of its input: -/// -/// * You want to fuzz "deeper" than just the parser. -/// * The input contains checksums that have to match the hash of some subset of -/// the data or else the whole thing is invalid, and therefore mutating any of -/// that subset means you need to recompute the checksums. -/// * Small random changes to the input buffer make it invalid. -/// -/// That is, a custom mutator is useful in similar situations where [a `T: -/// Arbitrary` input type](macro.fuzz_target.html#arbitrary-input-types) is -/// useful. Note that the two approaches are not mutually exclusive; you can use -/// whichever is easier for your problem domain or both! -/// -/// ## Implementation Contract -/// -/// The original, unmodified input is given in `data[..size]`. -/// -/// You must modify the data in place and return the new size. -/// -/// The new size should not be greater than `max_size`. If this is not the case, -/// then the `data` will be truncated to fit within `max_size`. Note that -/// `max_size < size` is possible when shrinking test cases. -/// -/// You must produce the same mutation given the same `seed`. Generally, when -/// choosing what kind of mutation to make or where to mutate, you should start -/// by creating a random number generator (RNG) that is seeded with the given -/// `seed` and then consult the RNG whenever making a decision: -/// -/// ```no_run -/// #![no_main] -/// -/// use rand::{rngs::StdRng, Rng, SeedableRng}; -/// -/// libfuzzer_sys::fuzz_mutator!(|data: &mut [u8], size: usize, max_size: usize, seed: u32| { -/// let mut rng = StdRng::seed_from_u64(seed as u64); -/// -/// # let first_mutation = |_, _, _, _| todo!(); -/// # let second_mutation = |_, _, _, _| todo!(); -/// # let third_mutation = |_, _, _, _| todo!(); -/// # let fourth_mutation = |_, _, _, _| todo!(); -/// // Choose which of our four supported kinds of mutations we want to make. -/// match rng.gen_range(0..4) { -/// 0 => first_mutation(rng, data, size, max_size), -/// 1 => second_mutation(rng, data, size, max_size), -/// 2 => third_mutation(rng, data, size, max_size), -/// 3 => fourth_mutation(rng, data, size, max_size), -/// _ => unreachable!() -/// } -/// }); -/// ``` -/// -/// ## Example: Compression -/// -/// Consider a simple fuzz target that takes compressed data as input, -/// decompresses it, and then asserts that the decompressed data doesn't begin -/// with "boom". It is difficult for `libFuzzer` (or any other fuzzer) to crash -/// this fuzz target because nearly all mutations it makes will invalidate the -/// compression format. Therefore, we use a custom mutator that decompresses the -/// raw input, mutates the decompressed data, and then recompresses it. This -/// allows `libFuzzer` to quickly discover crashing inputs. -/// -/// ```no_run -/// #![no_main] -/// -/// use flate2::{read::GzDecoder, write::GzEncoder, Compression}; -/// use libfuzzer_sys::{fuzz_mutator, fuzz_target}; -/// use std::io::{Read, Write}; -/// -/// fuzz_target!(|data: &[u8]| { -/// // Decompress the input data and crash if it starts with "boom". -/// if let Some(data) = decompress(data) { -/// if data.starts_with(b"boom") { -/// panic!(); -/// } -/// } -/// }); -/// -/// fuzz_mutator!( -/// |data: &mut [u8], size: usize, max_size: usize, _seed: u32| { -/// // Decompress the input data. If that fails, use a dummy value. -/// let mut decompressed = decompress(&data[..size]).unwrap_or_else(|| b"hi".to_vec()); -/// -/// // Mutate the decompressed data with `libFuzzer`'s default mutator. Make -/// // the `decompressed` vec's extra capacity available for insertion -/// // mutations via `resize`. -/// let len = decompressed.len(); -/// let cap = decompressed.capacity(); -/// decompressed.resize(cap, 0); -/// let new_decompressed_size = libfuzzer_sys::fuzzer_mutate(&mut decompressed, len, cap); -/// -/// // Recompress the mutated data. -/// let compressed = compress(&decompressed[..new_decompressed_size]); -/// -/// // Copy the recompressed mutated data into `data` and return the new size. -/// let new_size = std::cmp::min(max_size, compressed.len()); -/// data[..new_size].copy_from_slice(&compressed[..new_size]); -/// new_size -/// } -/// ); -/// -/// fn decompress(compressed_data: &[u8]) -> Option<Vec<u8>> { -/// let mut decoder = GzDecoder::new(compressed_data); -/// let mut decompressed = Vec::new(); -/// if decoder.read_to_end(&mut decompressed).is_ok() { -/// Some(decompressed) -/// } else { -/// None -/// } -/// } -/// -/// fn compress(data: &[u8]) -> Vec<u8> { -/// let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); -/// encoder -/// .write_all(data) -/// .expect("writing into a vec is infallible"); -/// encoder.finish().expect("writing into a vec is infallible") -/// } -/// ``` -/// -/// This example is inspired by [a similar example from the official `libFuzzer` -/// docs](https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md#example-compression). -/// -/// ## More Example Ideas -/// -/// * A PNG custom mutator that decodes a PNG, mutates the image, and then -/// re-encodes the mutated image as a new PNG. -/// -/// * A [`serde`](https://serde.rs/) custom mutator that deserializes your -/// structure, mutates it, and then reserializes it. -/// -/// * A Wasm binary custom mutator that inserts, replaces, and removes a -/// bytecode instruction in a function's body. -/// -/// * An HTTP request custom mutator that inserts, replaces, and removes a -/// header from an HTTP request. -#[macro_export] -macro_rules! fuzz_mutator { - ( - | - $data:ident : &mut [u8] , - $size:ident : usize , - $max_size:ident : usize , - $seed:ident : u32 $(,)* - | - $body:block - ) => { - /// Auto-generated function. - #[export_name = "LLVMFuzzerCustomMutator"] - pub fn rust_fuzzer_custom_mutator( - $data: *mut u8, - $size: usize, - $max_size: usize, - $seed: std::os::raw::c_uint, - ) -> usize { - // Depending on if we are growing or shrinking the test case, `size` - // might be larger or smaller than `max_size`. The `data`'s capacity - // is the maximum of the two. - let len = std::cmp::max($max_size, $size); - let $data: &mut [u8] = unsafe { std::slice::from_raw_parts_mut($data, len) }; - - // `unsigned int` is generally a `u32`, but not on all targets. Do - // an infallible (and potentially lossy, but that's okay because it - // preserves determinism) conversion. - let $seed = $seed as u32; - - // Truncate the new size if it is larger than the max. - let new_size = { $body }; - std::cmp::min(new_size, $max_size) - } - }; -} - -/// The default `libFuzzer` mutator. -/// -/// You generally don't have to use this at all unless you're defining a -/// custom mutator with [the `fuzz_mutator!` macro][crate::fuzz_mutator]. -/// -/// Mutates `data[..size]` in place such that the mutated data is no larger than -/// `max_size` and returns the new size of the mutated data. -/// -/// To only allow shrinking mutations, make `max_size < size`. -/// -/// To additionally allow mutations that grow the size of the data, make -/// `max_size > size`. -/// -/// Both `size` and `max_size` must be less than or equal to `data.len()`. -/// -/// # Example -/// -/// ```no_run -/// // Create some data in a buffer. -/// let mut data = vec![0; 128]; -/// data[..b"hello".len()].copy_from_slice(b"hello"); -/// -/// // Ask `libFuzzer` to mutate the data. By setting `max_size` to our buffer's -/// // full length, we are allowing `libFuzzer` to perform mutations that grow -/// // the size of the data, such as insertions. -/// let size = b"hello".len(); -/// let max_size = data.len(); -/// let new_size = libfuzzer_sys::fuzzer_mutate(&mut data, size, max_size); -/// -/// // Get the mutated data out of the buffer. -/// let mutated_data = &data[..new_size]; -/// ``` -pub fn fuzzer_mutate(data: &mut [u8], size: usize, max_size: usize) -> usize { - assert!(size <= data.len()); - assert!(max_size <= data.len()); - let new_size = unsafe { LLVMFuzzerMutate(data.as_mut_ptr(), size, max_size) }; - assert!(new_size <= data.len()); - new_size -} |