From: Louis Dionne Date: Fri, 16 Oct 2020 14:33:18 +0000 (-0400) Subject: [libc++] Refactor the fuzzing tests X-Git-Tag: llvmorg-13-init~8866 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b4bd194378851c2f421477d4147019d10f2420ac;p=platform%2Fupstream%2Fllvm.git [libc++] Refactor the fuzzing tests Define all the fuzzing tests in libcxx/test/libcxx/fuzzing, and get rid of the ad-hoc libcxx/fuzzing directory, which wasn't properly integrated with the build system or test suite. As a fly-by change, this also reduces the dependencies of fuzzing tests on large library components like , to make them work on more platforms. --- diff --git a/libcxx/fuzzing/RoutineNames.txt b/libcxx/fuzzing/RoutineNames.txt deleted file mode 100644 index a853c87..0000000 --- a/libcxx/fuzzing/RoutineNames.txt +++ /dev/null @@ -1,40 +0,0 @@ -sort -stable_sort -partition -partition_copy -stable_partition -unique -unique_copy -nth_element -partial_sort -partial_sort_copy -make_heap -push_heap -pop_heap -regex_ECMAScript -regex_POSIX -regex_extended -regex_awk -regex_grep -regex_egrep -search -uniform_int_distribution -uniform_real_distribution -bernoulli_distribution -poisson_distribution -geometric_distribution -binomial_distribution -negative_binomial_distribution -exponential_distribution -gamma_distribution -weibull_distribution -extreme_value_distribution -normal_distribution -lognormal_distribution -chi_squared_distribution -cauchy_distribution -fisher_f_distribution -student_t_distribution -discrete_distribution -piecewise_constant_distribution -piecewise_linear_distribution diff --git a/libcxx/fuzzing/fuzz_test.cpp b/libcxx/fuzzing/fuzz_test.cpp deleted file mode 100644 index 508c3b6..0000000 --- a/libcxx/fuzzing/fuzz_test.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// -*- C++ -*- -//===------------------------- fuzz_test.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// A simple program for running regressions on the fuzzing routines. -// This code is not part of any shipping product. -// -// To build: -// clang++ -std=c++11 fuzz_test.cpp fuzzing.cpp -// -// To use: -// fuzz_test -r partial_sort [-v] files... -// -// Each file should contain a test case. - -// TODO: should add some memory tracking, too. - - -#include -#include -#include -#include -#include -#include - -#include "fuzzing.h" - -// ==== Count memory allocations ==== - -struct MemoryCounters { - size_t totalAllocationCount; - size_t netAllocationCount; - size_t totalBytesAllocated; - }; - -MemoryCounters gMemoryCounters; - -void ZeroMemoryCounters() { - gMemoryCounters.totalAllocationCount = 0; - gMemoryCounters.netAllocationCount = 0; - gMemoryCounters.totalBytesAllocated = 0; -} - -void* operator new(std::size_t size) -{ - if (size == 0) size = 1; - void *p = ::malloc(size); - if (p == NULL) - throw std::bad_alloc(); - gMemoryCounters.totalAllocationCount += 1; - gMemoryCounters.netAllocationCount += 1; - gMemoryCounters.totalBytesAllocated += size; - return p; -} - -void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - try { return operator new(size); } - catch (const std::bad_alloc &) {} - return nullptr; -} - -void* operator new[](std::size_t size) -{ - return ::operator new(size); -} - -void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - try { return operator new(size); } - catch (const std::bad_alloc &) {} - return nullptr; -} - -void operator delete(void* ptr) noexcept -{ - if (ptr) - ::free(ptr); - gMemoryCounters.netAllocationCount -= 1; -} - -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - ::operator delete(ptr); -} - -void operator delete[](void* ptr) noexcept -{ - ::operator delete(ptr); -} - -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - ::operator delete(ptr); -} - -// ==== End count memory allocations ==== - - -typedef int (*FuzzProc) (const uint8_t *data, size_t size); - -const std::map procs = { - {"sort", fuzzing::sort}, - {"stable_sort", fuzzing::stable_sort}, - {"partition", fuzzing::partition}, - {"partition_copy", fuzzing::partition_copy}, - {"stable_partition", fuzzing::stable_partition}, - {"unique", fuzzing::unique}, - {"unique_copy", fuzzing::unique_copy}, - {"nth_element", fuzzing::nth_element}, - {"partial_sort", fuzzing::partial_sort}, - {"partial_sort_copy", fuzzing::partial_sort_copy}, - {"make_heap", fuzzing::make_heap}, - {"push_heap", fuzzing::push_heap}, - {"pop_heap", fuzzing::pop_heap}, - {"regex_ECMAScript", fuzzing::regex_ECMAScript}, - {"regex_POSIX", fuzzing::regex_POSIX}, - {"regex_extended", fuzzing::regex_extended}, - {"regex_awk", fuzzing::regex_awk}, - {"regex_grep", fuzzing::regex_grep}, - {"regex_egrep", fuzzing::regex_egrep}, - {"search", fuzzing::search} -}; - - - -bool verbose = false; - -void test_one(const char *filename, FuzzProc fp) -{ - std::vector v; - std::ifstream f (filename, std::ios::binary); - if (!f.is_open()) - std::cerr << "## Can't open '" << filename << "'" << std::endl; - else - { - typedef std::istream_iterator Iter; - std::copy(Iter(f), Iter(), std::back_inserter(v)); - if (verbose) - std::cout << "File '" << filename << "' contains " << v.size() << " entries" << std::endl; - ZeroMemoryCounters(); - const auto start_time = std::chrono::high_resolution_clock::now(); - int ret = fp (v.data(), v.size()); - const auto finish_time = std::chrono::high_resolution_clock::now(); - MemoryCounters mc = gMemoryCounters; - if (ret != 0) - std::cerr << "## Failure code: " << ret << std::endl; - if (verbose) - { - std::cout << "Execution time: " - << std::chrono::duration_cast(finish_time - start_time).count() - << " milliseconds" << std::endl; - std::cout << "Memory: " - << mc.totalBytesAllocated << " bytes allocated (" - << mc.totalAllocationCount << " allocations); " - << mc.netAllocationCount << " allocations remain" << std::endl; - } - } -} - -void usage (const char *name) -{ - std::cout << "Usage: " << name << " -r proc [-v] files..." << std::endl; - std::cout << "Supported routines:" << std::endl; - for (const auto &p : procs) - std::cout << " " << p.first << std::endl; - std::cout << std::endl; -} - -// Poor man's command-line options -const std::string dashR("-r"); -const std::string dashV("-v"); - -int main(int argc, char *argv[]) -{ - if (argc < 4 || dashR != argv[1] || procs.find(argv[2]) == procs.end()) - usage(argv[0]); - else { - FuzzProc fp = procs.find(argv[2])->second; - int firstFile = 3; - if (dashV == argv[firstFile]) - { - verbose = true; - ++firstFile; - } - for (int i = firstFile; i < argc; ++i) - test_one(argv[i], fp); - } -} diff --git a/libcxx/fuzzing/fuzz_test_template.cpp b/libcxx/fuzzing/fuzz_test_template.cpp deleted file mode 100644 index ba44eef..0000000 --- a/libcxx/fuzzing/fuzz_test_template.cpp +++ /dev/null @@ -1,22 +0,0 @@ -//===------------------------- fuzz_test.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "fuzzing/fuzzing.h" -#ifdef NDEBUG -#undef NDEBUG -#endif -#include - -#ifndef TEST_FUNCTION -#error TEST_FUNCTION must be defined -#endif - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - int result = fuzzing::TEST_FUNCTION(data, size); - assert(result == 0); return 0; -} diff --git a/libcxx/fuzzing/fuzzing.cpp b/libcxx/fuzzing/fuzzing.cpp deleted file mode 100644 index 1840c26..0000000 --- a/libcxx/fuzzing/fuzzing.cpp +++ /dev/null @@ -1,846 +0,0 @@ -// -*- C++ -*- -//===------------------------- fuzzing.cpp -------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// A set of routines to use when fuzzing the algorithms in libc++ -// Each one tests a single algorithm. -// -// They all have the form of: -// int `algorithm`(const uint8_t *data, size_t size); -// -// They perform the operation, and then check to see if the results are correct. -// If so, they return zero, and non-zero otherwise. -// -// For example, sort calls std::sort, then checks two things: -// (1) The resulting vector is sorted -// (2) The resulting vector contains the same elements as the original data. - - - -#include "fuzzing.h" -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef NDEBUG -#undef NDEBUG -#endif -#include -// If we had C++14, we could use the four iterator version of is_permutation and equal - -#ifndef _LIBCPP_VERSION -#error These test should be built with libc++ only. -#endif - -namespace fuzzing { - -// This is a struct we can use to test the stable_XXX algorithms. -// perform the operation on the key, then check the order of the payload. - -struct stable_test { - uint8_t key; - size_t payload; - - stable_test(uint8_t k) : key(k), payload(0) {} - stable_test(uint8_t k, size_t p) : key(k), payload(p) {} - }; - -void swap(stable_test &lhs, stable_test &rhs) -{ - using std::swap; - swap(lhs.key, rhs.key); - swap(lhs.payload, rhs.payload); -} - -struct key_less -{ - bool operator () (const stable_test &lhs, const stable_test &rhs) const - { - return lhs.key < rhs.key; - } -}; - -struct payload_less -{ - bool operator () (const stable_test &lhs, const stable_test &rhs) const - { - return lhs.payload < rhs.payload; - } -}; - -struct total_less -{ - bool operator () (const stable_test &lhs, const stable_test &rhs) const - { - return lhs.key == rhs.key ? lhs.payload < rhs.payload : lhs.key < rhs.key; - } -}; - -bool operator==(const stable_test &lhs, const stable_test &rhs) -{ - return lhs.key == rhs.key && lhs.payload == rhs.payload; -} - - -template -struct is_even -{ - bool operator () (const T &t) const - { - return t % 2 == 0; - } -}; - - -template<> -struct is_even -{ - bool operator () (const stable_test &t) const - { - return t.key % 2 == 0; - } -}; - -typedef std::vector Vec; -typedef std::vector StableVec; -typedef StableVec::const_iterator SVIter; - -// Cheap version of is_permutation -// Builds a set of buckets for each of the key values. -// Sums all the payloads. -// Not 100% perfect, but _way_ faster -bool is_permutation(SVIter first1, SVIter last1, SVIter first2) -{ - size_t xBuckets[256] = {0}; - size_t xPayloads[256] = {0}; - size_t yBuckets[256] = {0}; - size_t yPayloads[256] = {0}; - - for (; first1 != last1; ++first1, ++first2) - { - xBuckets [first1->key]++; - xPayloads[first1->key] += first1->payload; - - yBuckets [first2->key]++; - yPayloads[first2->key] += first2->payload; - } - - for (size_t i = 0; i < 256; ++i) - { - if (xBuckets[i] != yBuckets[i]) - return false; - if (xPayloads[i] != yPayloads[i]) - return false; - } - - return true; -} - -template -bool is_permutation(Iter1 first1, Iter1 last1, Iter2 first2) -{ - static_assert((std::is_same::value_type, uint8_t>::value), ""); - static_assert((std::is_same::value_type, uint8_t>::value), ""); - - size_t xBuckets[256] = {0}; - size_t yBuckets[256] = {0}; - - for (; first1 != last1; ++first1, ++first2) - { - xBuckets [*first1]++; - yBuckets [*first2]++; - } - - for (size_t i = 0; i < 256; ++i) - if (xBuckets[i] != yBuckets[i]) - return false; - - return true; -} - -// == sort == -int sort(const uint8_t *data, size_t size) -{ - Vec working(data, data + size); - std::sort(working.begin(), working.end()); - - if (!std::is_sorted(working.begin(), working.end())) return 1; - if (!fuzzing::is_permutation(data, data + size, working.cbegin())) return 99; - return 0; -} - - -// == stable_sort == -int stable_sort(const uint8_t *data, size_t size) -{ - StableVec input; - for (size_t i = 0; i < size; ++i) - input.push_back(stable_test(data[i], i)); - StableVec working = input; - std::stable_sort(working.begin(), working.end(), key_less()); - - if (!std::is_sorted(working.begin(), working.end(), key_less())) return 1; - auto iter = working.begin(); - while (iter != working.end()) - { - auto range = std::equal_range(iter, working.end(), *iter, key_less()); - if (!std::is_sorted(range.first, range.second, total_less())) return 2; - iter = range.second; - } - if (!fuzzing::is_permutation(input.cbegin(), input.cend(), working.cbegin())) return 99; - return 0; -} - -// == partition == -int partition(const uint8_t *data, size_t size) -{ - Vec working(data, data + size); - auto iter = std::partition(working.begin(), working.end(), is_even()); - - if (!std::all_of (working.begin(), iter, is_even())) return 1; - if (!std::none_of(iter, working.end(), is_even())) return 2; - if (!fuzzing::is_permutation(data, data + size, working.cbegin())) return 99; - return 0; -} - - -// == partition_copy == -int partition_copy(const uint8_t *data, size_t size) -{ - Vec v1, v2; - auto iter = std::partition_copy(data, data + size, - std::back_inserter(v1), std::back_inserter(v2), - is_even()); - ((void)iter); -// The two vectors should add up to the original size - if (v1.size() + v2.size() != size) return 1; - -// All of the even values should be in the first vector, and none in the second - if (!std::all_of (v1.begin(), v1.end(), is_even())) return 2; - if (!std::none_of(v2.begin(), v2.end(), is_even())) return 3; - -// Every value in both vectors has to be in the original - -// Make a copy of the input, and sort it - Vec v0{data, data + size}; - std::sort(v0.begin(), v0.end()); - -// Sort each vector and ensure that all of the elements appear in the original input - std::sort(v1.begin(), v1.end()); - if (!std::includes(v0.begin(), v0.end(), v1.begin(), v1.end())) return 4; - - std::sort(v2.begin(), v2.end()); - if (!std::includes(v0.begin(), v0.end(), v2.begin(), v2.end())) return 5; - -// This, while simple, is really slow - 20 seconds on a 500K element input. -// for (auto v: v1) -// if (std::find(data, data + size, v) == data + size) return 4; -// -// for (auto v: v2) -// if (std::find(data, data + size, v) == data + size) return 5; - - return 0; -} - -// == stable_partition == -int stable_partition (const uint8_t *data, size_t size) -{ - StableVec input; - for (size_t i = 0; i < size; ++i) - input.push_back(stable_test(data[i], i)); - StableVec working = input; - auto iter = std::stable_partition(working.begin(), working.end(), is_even()); - - if (!std::all_of (working.begin(), iter, is_even())) return 1; - if (!std::none_of(iter, working.end(), is_even())) return 2; - if (!std::is_sorted(working.begin(), iter, payload_less())) return 3; - if (!std::is_sorted(iter, working.end(), payload_less())) return 4; - if (!fuzzing::is_permutation(input.cbegin(), input.cend(), working.cbegin())) return 99; - return 0; -} - -// == nth_element == -// use the first element as a position into the data -int nth_element (const uint8_t *data, size_t size) -{ - if (size <= 1) return 0; - const size_t partition_point = data[0] % size; - Vec working(data + 1, data + size); - const auto partition_iter = working.begin() + partition_point; - std::nth_element(working.begin(), partition_iter, working.end()); - -// nth may be the end iterator, in this case nth_element has no effect. - if (partition_iter == working.end()) - { - if (!std::equal(data + 1, data + size, working.begin())) return 98; - } - else - { - const uint8_t nth = *partition_iter; - if (!std::all_of(working.begin(), partition_iter, [=](uint8_t v) { return v <= nth; })) - return 1; - if (!std::all_of(partition_iter, working.end(), [=](uint8_t v) { return v >= nth; })) - return 2; - if (!fuzzing::is_permutation(data + 1, data + size, working.cbegin())) return 99; - } - - return 0; -} - -// == partial_sort == -// use the first element as a position into the data -int partial_sort (const uint8_t *data, size_t size) -{ - if (size <= 1) return 0; - const size_t sort_point = data[0] % size; - Vec working(data + 1, data + size); - const auto sort_iter = working.begin() + sort_point; - std::partial_sort(working.begin(), sort_iter, working.end()); - - if (sort_iter != working.end()) - { - const uint8_t nth = *std::min_element(sort_iter, working.end()); - if (!std::all_of(working.begin(), sort_iter, [=](uint8_t v) { return v <= nth; })) - return 1; - if (!std::all_of(sort_iter, working.end(), [=](uint8_t v) { return v >= nth; })) - return 2; - } - if (!std::is_sorted(working.begin(), sort_iter)) return 3; - if (!fuzzing::is_permutation(data + 1, data + size, working.cbegin())) return 99; - - return 0; -} - - -// == partial_sort_copy == -// use the first element as a count -int partial_sort_copy (const uint8_t *data, size_t size) -{ - if (size <= 1) return 0; - const size_t num_results = data[0] % size; - Vec results(num_results); - (void) std::partial_sort_copy(data + 1, data + size, results.begin(), results.end()); - -// The results have to be sorted - if (!std::is_sorted(results.begin(), results.end())) return 1; -// All the values in results have to be in the original data - for (auto v: results) - if (std::find(data + 1, data + size, v) == data + size) return 2; - -// The things in results have to be the smallest N in the original data - Vec sorted(data + 1, data + size); - std::sort(sorted.begin(), sorted.end()); - if (!std::equal(results.begin(), results.end(), sorted.begin())) return 3; - return 0; -} - -// The second sequence has been "uniqued" -template -static bool compare_unique(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) -{ - assert(first1 != last1 && first2 != last2); - if (*first1 != *first2) return false; - - uint8_t last_value = *first1; - ++first1; ++first2; - while(first1 != last1 && first2 != last2) - { - // Skip over dups in the first sequence - while (*first1 == last_value) - if (++first1 == last1) return false; - if (*first1 != *first2) return false; - last_value = *first1; - ++first1; ++first2; - } - -// Still stuff left in the 'uniqued' sequence - oops - if (first1 == last1 && first2 != last2) return false; - -// Still stuff left in the original sequence - better be all the same - while (first1 != last1) - { - if (*first1 != last_value) return false; - ++first1; - } - return true; -} - -// == unique == -int unique (const uint8_t *data, size_t size) -{ - Vec working(data, data + size); - std::sort(working.begin(), working.end()); - Vec results = working; - Vec::iterator new_end = std::unique(results.begin(), results.end()); - Vec::iterator it; // scratch iterator - -// Check the size of the unique'd sequence. -// it should only be zero if the input sequence was empty. - if (results.begin() == new_end) - return working.size() == 0 ? 0 : 1; - -// 'results' is sorted - if (!std::is_sorted(results.begin(), new_end)) return 2; - -// All the elements in 'results' must be different - it = results.begin(); - uint8_t prev_value = *it++; - for (; it != new_end; ++it) - { - if (*it == prev_value) return 3; - prev_value = *it; - } - -// Every element in 'results' must be in 'working' - for (it = results.begin(); it != new_end; ++it) - if (std::find(working.begin(), working.end(), *it) == working.end()) - return 4; - -// Every element in 'working' must be in 'results' - for (auto v : working) - if (std::find(results.begin(), new_end, v) == new_end) - return 5; - - return 0; -} - -// == unique_copy == -int unique_copy (const uint8_t *data, size_t size) -{ - Vec working(data, data + size); - std::sort(working.begin(), working.end()); - Vec results; - (void) std::unique_copy(working.begin(), working.end(), - std::back_inserter(results)); - Vec::iterator it; // scratch iterator - -// Check the size of the unique'd sequence. -// it should only be zero if the input sequence was empty. - if (results.size() == 0) - return working.size() == 0 ? 0 : 1; - -// 'results' is sorted - if (!std::is_sorted(results.begin(), results.end())) return 2; - -// All the elements in 'results' must be different - it = results.begin(); - uint8_t prev_value = *it++; - for (; it != results.end(); ++it) - { - if (*it == prev_value) return 3; - prev_value = *it; - } - -// Every element in 'results' must be in 'working' - for (auto v : results) - if (std::find(working.begin(), working.end(), v) == working.end()) - return 4; - -// Every element in 'working' must be in 'results' - for (auto v : working) - if (std::find(results.begin(), results.end(), v) == results.end()) - return 5; - - return 0; -} - - -// -- regex fuzzers -static int regex_helper(const uint8_t *data, size_t size, std::regex::flag_type flag) -{ - if (size > 0) - { -#ifndef _LIBCPP_NO_EXCEPTIONS - try - { - std::string s((const char *)data, size); - std::regex re(s, flag); - return std::regex_match(s, re) ? 1 : 0; - } - catch (std::regex_error &ex) {} -#else - ((void)data); - ((void)size); - ((void)flag); -#endif - } - return 0; -} - - -int regex_ECMAScript (const uint8_t *data, size_t size) -{ - (void) regex_helper(data, size, std::regex_constants::ECMAScript); - return 0; -} - -int regex_POSIX (const uint8_t *data, size_t size) -{ - (void) regex_helper(data, size, std::regex_constants::basic); - return 0; -} - -int regex_extended (const uint8_t *data, size_t size) -{ - (void) regex_helper(data, size, std::regex_constants::extended); - return 0; -} - -int regex_awk (const uint8_t *data, size_t size) -{ - (void) regex_helper(data, size, std::regex_constants::awk); - return 0; -} - -int regex_grep (const uint8_t *data, size_t size) -{ - (void) regex_helper(data, size, std::regex_constants::grep); - return 0; -} - -int regex_egrep (const uint8_t *data, size_t size) -{ - (void) regex_helper(data, size, std::regex_constants::egrep); - return 0; -} - -// -- heap fuzzers -int make_heap (const uint8_t *data, size_t size) -{ - Vec working(data, data + size); - std::make_heap(working.begin(), working.end()); - - if (!std::is_heap(working.begin(), working.end())) return 1; - if (!fuzzing::is_permutation(data, data + size, working.cbegin())) return 99; - return 0; -} - -int push_heap (const uint8_t *data, size_t size) -{ - if (size < 2) return 0; - -// Make a heap from the first half of the data - Vec working(data, data + size); - auto iter = working.begin() + (size / 2); - std::make_heap(working.begin(), iter); - if (!std::is_heap(working.begin(), iter)) return 1; - -// Now push the rest onto the heap, one at a time - ++iter; - for (; iter != working.end(); ++iter) { - std::push_heap(working.begin(), iter); - if (!std::is_heap(working.begin(), iter)) return 2; - } - - if (!fuzzing::is_permutation(data, data + size, working.cbegin())) return 99; - return 0; -} - -int pop_heap (const uint8_t *data, size_t size) -{ - if (size < 2) return 0; - Vec working(data, data + size); - std::make_heap(working.begin(), working.end()); - -// Pop things off, one at a time - auto iter = --working.end(); - while (iter != working.begin()) { - std::pop_heap(working.begin(), iter); - if (!std::is_heap(working.begin(), --iter)) return 2; - } - - return 0; -} - - -// -- search fuzzers -int search (const uint8_t *data, size_t size) -{ - if (size < 2) return 0; - - const size_t pat_size = data[0] * (size - 1) / std::numeric_limits::max(); - assert(pat_size <= size - 1); - const uint8_t *pat_begin = data + 1; - const uint8_t *pat_end = pat_begin + pat_size; - const uint8_t *data_end = data + size; - assert(pat_end <= data_end); -// std::cerr << "data[0] = " << size_t(data[0]) << " "; -// std::cerr << "Pattern size = " << pat_size << "; corpus is " << size - 1 << std::endl; - auto it = std::search(pat_end, data_end, pat_begin, pat_end); - if (it != data_end) // not found - if (!std::equal(pat_begin, pat_end, it)) - return 1; - return 0; -} - -template -static int search_helper (const uint8_t *data, size_t size) -{ - if (size < 2) return 0; - - const size_t pat_size = data[0] * (size - 1) / std::numeric_limits::max(); - const uint8_t *pat_begin = data + 1; - const uint8_t *pat_end = pat_begin + pat_size; - const uint8_t *data_end = data + size; - - auto it = std::search(pat_end, data_end, S(pat_begin, pat_end)); - if (it != data_end) // not found - if (!std::equal(pat_begin, pat_end, it)) - return 1; - return 0; -} - -// These are still in std::experimental -// int search_boyer_moore (const uint8_t *data, size_t size) -// { -// return search_helper>(data, size); -// } -// -// int search_boyer_moore_horspool (const uint8_t *data, size_t size) -// { -// return search_helper>(data, size); -// } - - -// -- set operation fuzzers -template -static void set_helper (const uint8_t *data, size_t size, Vec &v1, Vec &v2) -{ - assert(size > 1); - - const size_t pat_size = data[0] * (size - 1) / std::numeric_limits::max(); - const uint8_t *pat_begin = data + 1; - const uint8_t *pat_end = pat_begin + pat_size; - const uint8_t *data_end = data + size; - v1.assign(pat_begin, pat_end); - v2.assign(pat_end, data_end); - - std::sort(v1.begin(), v1.end()); - std::sort(v2.begin(), v2.end()); -} - -enum class ParamKind { - OneValue, - TwoValues, - PointerRange -}; - -template -std::vector GetValues(const uint8_t *data, size_t size) { - std::vector result; - while (size >= sizeof(IntT)) { - IntT tmp; - memcpy(&tmp, data, sizeof(IntT)); - size -= sizeof(IntT); - data += sizeof(IntT); - result.push_back(tmp); - } - return result; -} - -enum InitKind { - Default, - DoubleOnly, - VectorDouble, - VectorResultType -}; - - - -template -struct ParamTypeHelper { - using ParamT = typename Dist::param_type; - using ResultT = typename Dist::result_type; - static_assert(std::is_same::value, ""); - static ParamT Create(const uint8_t* data, size_t size, bool &OK) { - - constexpr bool select_vector_result = std::is_constructible::value; - constexpr bool select_vector_double = std::is_constructible::value; - constexpr int selector = select_vector_result ? 0 : (select_vector_double ? 1 : 2); - return DispatchAndCreate(std::integral_constant{}, data, size, OK); - - } - - static ParamT DispatchAndCreate(std::integral_constant, const uint8_t *data, size_t size, bool &OK) { - return CreateVectorResult(data, size, OK); - } - static ParamT DispatchAndCreate(std::integral_constant, const uint8_t *data, size_t size, bool &OK) { - return CreateVectorDouble(data, size, OK); - } - static ParamT DispatchAndCreate(std::integral_constant, const uint8_t *data, size_t size, bool &OK) { - return CreateDefault(data, size, OK); - } - -static ParamT -CreateVectorResult(const uint8_t *data, size_t size, bool &OK) { - auto Input = GetValues(data, size); - OK = false; - if (Input.size() < 10) - return ParamT{}; - OK = true; - auto Beg = Input.begin(); - auto End = Input.end(); - auto Mid = Beg + ((End - Beg) / 2); - - assert(Mid - Beg <= (End - Mid)); - ParamT p(Beg, Mid, Mid); - return p; -} - - static ParamT - CreateVectorDouble(const uint8_t *data, size_t size, bool &OK) { - auto Input = GetValues(data, size); - - OK = true; - auto Beg = Input.begin(); - auto End = Input.end(); - - ParamT p(Beg, End); - return p; - } - - - static ParamT - CreateDefault(const uint8_t *data, size_t size, bool &OK) { - OK = false; - if (size < sizeof(ParamT)) - return ParamT{}; - OK = true; - ParamT input; - memcpy(&input, data, sizeof(ParamT)); - return input; - } - -}; - - - - -template -struct ParamTypeHelper> { - using Dist = std::poisson_distribution; - using ParamT = typename Dist::param_type; - using ResultT = typename Dist::result_type; - - static ParamT Create(const uint8_t *data, size_t size, bool& OK) { - OK = false; - auto vals = GetValues(data, size); - if (vals.empty() || std::isnan(vals[0]) || std::isnan(std::abs(vals[0])) || vals[0] < 0 ) - return ParamT{}; - OK = true; - //std::cerr << "Value: " << vals[0] << std::endl; - return ParamT{vals[0]}; - } -}; - - -template -struct ParamTypeHelper> { - using Dist = std::geometric_distribution; - using ParamT = typename Dist::param_type; - using ResultT = typename Dist::result_type; - - static ParamT Create(const uint8_t *data, size_t size, bool& OK) { - OK = false; - auto vals = GetValues(data, size); - if (vals.empty() || std::isnan(vals[0]) || vals[0] < 0 ) - return ParamT{}; - OK = true; - // std::cerr << "Value: " << vals[0] << std::endl; - return ParamT{vals[0]}; - } -}; - - -template -struct ParamTypeHelper> { - using Dist = std::lognormal_distribution; - using ParamT = typename Dist::param_type; - using ResultT = typename Dist::result_type; - - static ParamT Create(const uint8_t *data, size_t size, bool& OK) { - OK = false; - auto vals = GetValues(data, size); - if (vals.size() < 2 ) - return ParamT{}; - OK = true; - return ParamT{vals[0], vals[1]}; - } -}; - - -template <> -struct ParamTypeHelper { - using Dist = std::bernoulli_distribution; - using ParamT = typename Dist::param_type; - using ResultT = typename Dist::result_type; - - static ParamT Create(const uint8_t *data, size_t size, bool& OK) { - OK = false; - auto vals = GetValues(data, size); - if (vals.empty()) - return ParamT{}; - OK = true; - return ParamT{vals[0]}; - } -}; - -template -int random_distribution_helper(const uint8_t *data, size_t size) { - - std::mt19937 engine; - using ParamT = typename Distribution::param_type; - bool OK; - ParamT p = ParamTypeHelper::Create(data, size, OK); - if (!OK) - return 0; - Distribution d(p); - volatile auto res = d(engine); - if (std::isnan(res)) { - // FIXME(llvm.org/PR44289): - // Investigate why these distributions are returning NaN and decide - // if that's what we want them to be doing. - // - // Make this assert false (or return non-zero). - return 0; - } - return 0; -} - -#define DEFINE_RANDOM_TEST(name, ...) \ -int name(const uint8_t *data, size_t size) { \ - return random_distribution_helper< std::name __VA_ARGS__ >(data, size); \ -} -DEFINE_RANDOM_TEST(uniform_int_distribution,) -DEFINE_RANDOM_TEST(uniform_real_distribution,) -DEFINE_RANDOM_TEST(bernoulli_distribution) -DEFINE_RANDOM_TEST(poisson_distribution,) -DEFINE_RANDOM_TEST(geometric_distribution,) -DEFINE_RANDOM_TEST(binomial_distribution, ) -DEFINE_RANDOM_TEST(negative_binomial_distribution, ) -DEFINE_RANDOM_TEST(exponential_distribution, ) -DEFINE_RANDOM_TEST(gamma_distribution, ) -DEFINE_RANDOM_TEST(weibull_distribution, ) -DEFINE_RANDOM_TEST(extreme_value_distribution, ) -DEFINE_RANDOM_TEST(normal_distribution, ) -DEFINE_RANDOM_TEST(lognormal_distribution, ) -DEFINE_RANDOM_TEST(chi_squared_distribution, ) -DEFINE_RANDOM_TEST(cauchy_distribution, ) -DEFINE_RANDOM_TEST(fisher_f_distribution, ) -DEFINE_RANDOM_TEST(student_t_distribution, ) -DEFINE_RANDOM_TEST(discrete_distribution, ) -DEFINE_RANDOM_TEST(piecewise_constant_distribution, ) -DEFINE_RANDOM_TEST(piecewise_linear_distribution, ) - -} // namespace fuzzing diff --git a/libcxx/fuzzing/fuzzing.h b/libcxx/fuzzing/fuzzing.h deleted file mode 100644 index 99a3aa1..0000000 --- a/libcxx/fuzzing/fuzzing.h +++ /dev/null @@ -1,83 +0,0 @@ -// -*- C++ -*- -//===-------------------------- fuzzing.h --------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef _LIBCPP_FUZZING -#define _LIBCPP_FUZZING - -#include // for size_t -#include // for uint8_t - -namespace fuzzing { - -// These all return 0 on success; != 0 on failure - int sort (const uint8_t *data, size_t size); - int stable_sort (const uint8_t *data, size_t size); - int partition (const uint8_t *data, size_t size); - int partition_copy (const uint8_t *data, size_t size); - int stable_partition (const uint8_t *data, size_t size); - int unique (const uint8_t *data, size_t size); - int unique_copy (const uint8_t *data, size_t size); - -// partition and stable_partition take Bi-Di iterators. -// Should test those, too - int nth_element (const uint8_t *data, size_t size); - int partial_sort (const uint8_t *data, size_t size); - int partial_sort_copy (const uint8_t *data, size_t size); - -// Heap operations - int make_heap (const uint8_t *data, size_t size); - int push_heap (const uint8_t *data, size_t size); - int pop_heap (const uint8_t *data, size_t size); - -// Various flavors of regex - int regex_ECMAScript (const uint8_t *data, size_t size); - int regex_POSIX (const uint8_t *data, size_t size); - int regex_extended (const uint8_t *data, size_t size); - int regex_awk (const uint8_t *data, size_t size); - int regex_grep (const uint8_t *data, size_t size); - int regex_egrep (const uint8_t *data, size_t size); - -// Searching - int search (const uint8_t *data, size_t size); -// int search_boyer_moore (const uint8_t *data, size_t size); -// int search_boyer_moore_horspool (const uint8_t *data, size_t size); - -// Set operations -// int includes (const uint8_t *data, size_t size); -// int set_union (const uint8_t *data, size_t size); -// int set_intersection (const uint8_t *data, size_t size); -// int set_difference (const uint8_t *data, size_t size); -// int set_symmetric_difference (const uint8_t *data, size_t size); -// int merge (const uint8_t *data, size_t size); - -// Random numbers - int uniform_int_distribution(const uint8_t*, size_t); - int uniform_real_distribution(const uint8_t*, size_t); - int bernoulli_distribution(const uint8_t*, size_t); - int poisson_distribution(const uint8_t*, size_t); - int geometric_distribution(const uint8_t*, size_t); - int binomial_distribution(const uint8_t*, size_t); - int negative_binomial_distribution(const uint8_t*, size_t); - int exponential_distribution(const uint8_t*, size_t); - int gamma_distribution(const uint8_t*, size_t); - int weibull_distribution(const uint8_t*, size_t); - int extreme_value_distribution(const uint8_t*, size_t); - int normal_distribution(const uint8_t*, size_t); - int lognormal_distribution(const uint8_t*, size_t); - int chi_squared_distribution(const uint8_t*, size_t); - int cauchy_distribution(const uint8_t*, size_t); - int fisher_f_distribution(const uint8_t*, size_t); - int student_t_distribution(const uint8_t*, size_t); - int discrete_distribution(const uint8_t*, size_t); - int piecewise_constant_distribution(const uint8_t*, size_t); - int piecewise_linear_distribution(const uint8_t*, size_t); - -} // namespace fuzzing - -#endif // _LIBCPP_FUZZING diff --git a/libcxx/test/libcxx/fuzzing/fuzz.h b/libcxx/test/libcxx/fuzzing/fuzz.h new file mode 100644 index 0000000..1281879 --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/fuzz.h @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_LIBCXX_FUZZING_FUZZ_H +#define TEST_LIBCXX_FUZZING_FUZZ_H + +#include +#include +#include +#include // std::strlen +#include +#include // std::swap + + +// This is a struct we can use to test the stable_XXX algorithms. +// Perform the operation on the key, then check the order of the payload. +struct ByteWithPayload { + std::uint8_t key; + std::size_t payload; + + ByteWithPayload(std::uint8_t k) : key(k), payload(0) { } + ByteWithPayload(std::uint8_t k, std::size_t p) : key(k), payload(p) { } + + friend bool operator==(ByteWithPayload const& x, ByteWithPayload const& y) { + return x.key == y.key && x.payload == y.payload; + } + + friend bool operator!=(ByteWithPayload const& x, ByteWithPayload const& y) { + return !(x == y); + } + + struct key_less { + bool operator()(ByteWithPayload const& x, ByteWithPayload const& y) const + { return x.key < y.key; } + }; + + struct payload_less { + bool operator()(ByteWithPayload const& x, ByteWithPayload const& y) const + { return x.payload < y.payload; } + }; + + struct total_less { + bool operator()(ByteWithPayload const& x, ByteWithPayload const& y) const { + return x.key == y.key ? x.payload < y.payload : x.key < y.key; + } + }; + + friend void swap(ByteWithPayload& lhs, ByteWithPayload& rhs) { + std::swap(lhs.key, rhs.key); + std::swap(lhs.payload, rhs.payload); + } +}; + +// Faster version of std::is_permutation +// +// Builds a set of buckets for each of the key values, and sums all the payloads. +// Not 100% perfect, but _way_ faster. +template ::value_type, ByteWithPayload>::value && + std::is_same::value_type, ByteWithPayload>::value +>::type> +bool fast_is_permutation(Iter1 first1, Iter1 last1, Iter2 first2) { + std::size_t xBuckets[256] = {0}; + std::size_t xPayloads[256] = {0}; + std::size_t yBuckets[256] = {0}; + std::size_t yPayloads[256] = {0}; + + for (; first1 != last1; ++first1, ++first2) { + xBuckets[first1->key]++; + xPayloads[first1->key] += first1->payload; + + yBuckets[first2->key]++; + yPayloads[first2->key] += first2->payload; + } + + for (std::size_t i = 0; i < 256; ++i) { + if (xBuckets[i] != yBuckets[i]) + return false; + if (xPayloads[i] != yPayloads[i]) + return false; + } + + return true; +} + +template ::value_type, std::uint8_t>::value && + std::is_same::value_type, std::uint8_t>::value +>::type> +bool fast_is_permutation(Iter1 first1, Iter1 last1, Iter2 first2) { + std::size_t xBuckets[256] = {0}; + std::size_t yBuckets[256] = {0}; + + for (; first1 != last1; ++first1, ++first2) { + xBuckets[*first1]++; + yBuckets[*first2]++; + } + + for (std::size_t i = 0; i < 256; ++i) + if (xBuckets[i] != yBuckets[i]) + return false; + + return true; +} + +// When running inside OSS-Fuzz, we link against a fuzzing library that defines +// main() and calls LLVMFuzzerTestOneInput. +// +// Otherwise, when e.g. running the Lit tests, we define main() to run fuzzing +// tests on a few inputs. +#if !defined(LIBCPP_OSS_FUZZ) +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t*, std::size_t); + +int main(int, char**) { + const char* test_cases[] = { + "", + "s", + "bac", + "bacasf", + "lkajseravea", + "adsfkajdsfjkas;lnc441324513,34535r34525234", + "b*c", + "ba?sf", + "lka*ea", + "adsf*kas;lnc441[0-9]1r34525234" + }; + + for (const char* tc : test_cases) { + const std::size_t size = std::strlen(tc); + const std::uint8_t* data = reinterpret_cast(tc); + int result = LLVMFuzzerTestOneInput(data, size); + assert(result == 0); + } + + return 0; +} +#endif // !LIBCPP_OSS_FUZZ + +#endif // TEST_LIBCXX_FUZZING_FUZZ_H diff --git a/libcxx/test/libcxx/fuzzing/fuzzer_test.h b/libcxx/test/libcxx/fuzzing/fuzzer_test.h deleted file mode 100644 index 1fafd23..0000000 --- a/libcxx/test/libcxx/fuzzing/fuzzer_test.h +++ /dev/null @@ -1,46 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef TEST_LIBCXX_FUZZER_TEST_H -#define TEST_LIBCXX_FUZZER_TEST_H - -#include -#include - -#include "../../../fuzzing/fuzzing.h" -#include "../../../fuzzing/fuzzing.cpp" - -const char* TestCaseSetOne[] = {"", "s", "bac", - "bacasf", - "lkajseravea", - "adsfkajdsfjkas;lnc441324513,34535r34525234", - "b*c", - "ba?sf", - "lka*ea", - "adsf*kas;lnc441[0-9]1r34525234"}; - -using FuzzerFuncType = int(const uint8_t*, size_t); - -template -inline void RunFuzzingTest(FuzzerFuncType *to_test, const char* (&test_cases)[NumCases]) { - for (const char* TC : test_cases) { - const size_t size = std::strlen(TC); - const uint8_t* data = (const uint8_t*)TC; - int result = to_test(data, size); - assert(result == 0); - } -} - -#define FUZZER_TEST(FuncName) \ -int main() { \ - RunFuzzingTest(FuncName, TestCaseSetOne); \ -} \ -extern int require_semi - -#endif // TEST_LIBCXX_FUZZER_TEST_H diff --git a/libcxx/test/libcxx/fuzzing/geometric_distribution.pass.cpp b/libcxx/test/libcxx/fuzzing/geometric_distribution.pass.cpp deleted file mode 100644 index 5a5839a..0000000 --- a/libcxx/test/libcxx/fuzzing/geometric_distribution.pass.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// -*- C++ -*- -//===------------------------ unique_copy.cpp -----------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include -#include - -#include "fuzzer_test.h" - -template -int random_distribution_helper(const uint8_t *data, size_t size) { - std::mt19937 engine; - using ParamT = typename Distribution::param_type; - if (size < sizeof(double)) - return 0; - double Arg; - memcpy(&Arg, data, sizeof(double)); - ParamT p(Arg); - Distribution d(p); - for (int I=0; I < 1000; ++I) { - volatile auto res = d(engine); - ((void)res); - } - return 0; -} - -int FuzzRandom(const uint8_t *Data, size_t Size) { - return random_distribution_helper>(Data, Size); -} -FUZZER_TEST(FuzzRandom); - - diff --git a/libcxx/test/libcxx/fuzzing/make_heap.pass.cpp b/libcxx/test/libcxx/fuzzing/make_heap.pass.cpp new file mode 100644 index 0000000..ede82c2 --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/make_heap.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + std::vector working(data, data + size); + std::make_heap(working.begin(), working.end()); + + if (!std::is_heap(working.begin(), working.end())) + return 1; + if (!fast_is_permutation(data, data + size, working.cbegin())) + return 99; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/nth_element.pass.cpp b/libcxx/test/libcxx/fuzzing/nth_element.pass.cpp index e3ad5bb..5e491e7 100644 --- a/libcxx/test/libcxx/fuzzing/nth_element.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/nth_element.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===----------------------- nth_element.cpp ------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,37 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +// Use the first element as a position into the data +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + if (size <= 1) return 0; + const size_t partition_point = data[0] % size; + std::vector working(data + 1, data + size); + const auto partition_iter = working.begin() + partition_point; + std::nth_element(working.begin(), partition_iter, working.end()); + + // nth may be the end iterator, in this case nth_element has no effect. + if (partition_iter == working.end()) { + if (!std::equal(data + 1, data + size, working.begin())) + return 98; + } + else { + const std::uint8_t nth = *partition_iter; + if (!std::all_of(working.begin(), partition_iter, [=](std::uint8_t v) { return v <= nth; })) + return 1; + if (!std::all_of(partition_iter, working.end(), [=](std::uint8_t v) { return v >= nth; })) + return 2; + if (!fast_is_permutation(data + 1, data + size, working.cbegin())) + return 99; + } -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::nth_element); + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/partial_sort.pass.cpp b/libcxx/test/libcxx/fuzzing/partial_sort.pass.cpp index 3f6fef1..c179846 100644 --- a/libcxx/test/libcxx/fuzzing/partial_sort.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/partial_sort.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===-------------------------- partial_sort.cpp --------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,26 +6,35 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 -#include -#include // for strlen +#include +#include +#include +#include -#include "../../../fuzzing/fuzzing.h" -#include "../../../fuzzing/fuzzing.cpp" +#include "fuzz.h" -const char* test_cases[] = {"", "s", "bac", - "bacasf", - "lkajseravea", - "adsfkajdsfjkas;lnc441324513,34535r34525234"}; +// Use the first element as a position into the data +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + if (size <= 1) + return 0; + const std::size_t sort_point = data[0] % size; + std::vector working(data + 1, data + size); + const auto sort_iter = working.begin() + sort_point; + std::partial_sort(working.begin(), sort_iter, working.end()); -const size_t k_num_tests = sizeof(test_cases) / sizeof(test_cases[0]); + if (sort_iter != working.end()) { + const std::uint8_t nth = *std::min_element(sort_iter, working.end()); + if (!std::all_of(working.begin(), sort_iter, [=](std::uint8_t v) { return v <= nth; })) + return 1; + if (!std::all_of(sort_iter, working.end(), [=](std::uint8_t v) { return v >= nth; })) + return 2; + } + if (!std::is_sorted(working.begin(), sort_iter)) + return 3; + if (!fast_is_permutation(data + 1, data + size, working.cbegin())) + return 99; -int main(int, char**) { - for (size_t i = 0; i < k_num_tests; ++i) { - const size_t size = std::strlen(test_cases[i]); - const uint8_t* data = (const uint8_t*)test_cases[i]; - assert(0 == fuzzing::partial_sort(data, size)); - } - return 0; + return 0; } diff --git a/libcxx/test/libcxx/fuzzing/partial_sort_copy.pass.cpp b/libcxx/test/libcxx/fuzzing/partial_sort_copy.pass.cpp index b5d47c5..530ab0d 100644 --- a/libcxx/test/libcxx/fuzzing/partial_sort_copy.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/partial_sort_copy.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===----------------------- partial_sort_copy.cpp ------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,36 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +// Use the first element as a count +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + if (size <= 1) + return 0; + const std::size_t num_results = data[0] % size; + std::vector results(num_results); + (void)std::partial_sort_copy(data + 1, data + size, results.begin(), results.end()); + + // The results have to be sorted + if (!std::is_sorted(results.begin(), results.end())) + return 1; + // All the values in results have to be in the original data + for (auto v: results) + if (std::find(data + 1, data + size, v) == data + size) + return 2; + + // The things in results have to be the smallest N in the original data + std::vector sorted(data + 1, data + size); + std::sort(sorted.begin(), sorted.end()); + if (!std::equal(results.begin(), results.end(), sorted.begin())) + return 3; -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::partial_sort_copy); + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/partition.pass.cpp b/libcxx/test/libcxx/fuzzing/partition.pass.cpp index dda506d..203e45b 100644 --- a/libcxx/test/libcxx/fuzzing/partition.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/partition.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===--------------------------- partition.cpp ----------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,25 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + auto is_even = [](auto x) { return x % 2 == 0; }; + std::vector working(data, data + size); + auto iter = std::partition(working.begin(), working.end(), is_even); -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::partition); + if (!std::all_of(working.begin(), iter, is_even)) + return 1; + if (!std::none_of(iter, working.end(), is_even)) + return 2; + if (!fast_is_permutation(data, data + size, working.cbegin())) + return 99; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/partition_copy.pass.cpp b/libcxx/test/libcxx/fuzzing/partition_copy.pass.cpp index 383b4cf..ac37c2e 100644 --- a/libcxx/test/libcxx/fuzzing/partition_copy.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/partition_copy.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===------------------------ partition_copy.cpp --------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,60 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + auto is_even = [](auto t) { + return t % 2 == 0; + }; + + std::vector v1, v2; + auto iter = std::partition_copy(data, data + size, + std::back_inserter>(v1), + std::back_inserter>(v2), + is_even); + ((void)iter); + // The two vectors should add up to the original size + if (v1.size() + v2.size() != size) + return 1; + + // All of the even values should be in the first vector, and none in the second + if (!std::all_of(v1.begin(), v1.end(), is_even)) + return 2; + if (!std::none_of(v2.begin(), v2.end(), is_even)) + return 3; + + // Every value in both vectors has to be in the original + + // Make a copy of the input, and sort it + std::vector v0{data, data + size}; + std::sort(v0.begin(), v0.end()); + + // Sort each vector and ensure that all of the elements appear in the original input + std::sort(v1.begin(), v1.end()); + if (!std::includes(v0.begin(), v0.end(), v1.begin(), v1.end())) + return 4; + + std::sort(v2.begin(), v2.end()); + if (!std::includes(v0.begin(), v0.end(), v2.begin(), v2.end())) + return 5; + + // This, while simple, is really slow - 20 seconds on a 500K element input. + // for (auto v: v1) + // if (std::find(data, data + size, v) == data + size) + // return 4; + // + // for (auto v: v2) + // if (std::find(data, data + size, v) == data + size) + // return 5; -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::partition_copy); + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/pop_heap.pass.cpp b/libcxx/test/libcxx/fuzzing/pop_heap.pass.cpp new file mode 100644 index 0000000..e54eede --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/pop_heap.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + if (size < 2) + return 0; + std::vector working(data, data + size); + std::make_heap(working.begin(), working.end()); + + // Pop things off, one at a time + auto iter = --working.end(); + while (iter != working.begin()) { + std::pop_heap(working.begin(), iter); + if (!std::is_heap(working.begin(), --iter)) + return 2; + } + + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/push_heap.pass.cpp b/libcxx/test/libcxx/fuzzing/push_heap.pass.cpp new file mode 100644 index 0000000..ce67176 --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/push_heap.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + if (size < 2) + return 0; + + // Make a heap from the first half of the data + std::vector working(data, data + size); + auto iter = working.begin() + (size / 2); + std::make_heap(working.begin(), iter); + if (!std::is_heap(working.begin(), iter)) + return 1; + + // Now push the rest onto the heap, one at a time + ++iter; + for (; iter != working.end(); ++iter) { + std::push_heap(working.begin(), iter); + if (!std::is_heap(working.begin(), iter)) + return 2; + } + + if (!fast_is_permutation(data, data + size, working.cbegin())) + return 99; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/random.pass.cpp b/libcxx/test/libcxx/fuzzing/random.pass.cpp new file mode 100644 index 0000000..e7545ff --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/random.pass.cpp @@ -0,0 +1,194 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fuzz.h" + +template +std::vector GetValues(const std::uint8_t *data, std::size_t size) { + std::vector result; + while (size >= sizeof(IntT)) { + IntT tmp; + std::memcpy(&tmp, data, sizeof(IntT)); + size -= sizeof(IntT); + data += sizeof(IntT); + result.push_back(tmp); + } + return result; +} + +template +struct ParamTypeHelper { + using ParamT = typename Dist::param_type; + using ResultT = typename Dist::result_type; + static_assert(std::is_same::value, ""); + + static ParamT Create(const uint8_t* data, std::size_t size, bool &OK) { + constexpr bool select_vector_result = std::is_constructible::value; + constexpr bool select_vector_double = std::is_constructible::value; + constexpr int selector = select_vector_result ? 0 : (select_vector_double ? 1 : 2); + return DispatchAndCreate(std::integral_constant{}, data, size, OK); + } + + // Vector result + static ParamT DispatchAndCreate(std::integral_constant, const std::uint8_t *data, std::size_t size, bool &OK) { + auto Input = GetValues(data, size); + OK = false; + if (Input.size() < 10) + return ParamT{}; + OK = true; + auto Beg = Input.begin(); + auto End = Input.end(); + auto Mid = Beg + ((End - Beg) / 2); + + assert(Mid - Beg <= (End - Mid)); + ParamT p(Beg, Mid, Mid); + return p; + } + + // Vector double + static ParamT DispatchAndCreate(std::integral_constant, const std::uint8_t *data, std::size_t size, bool &OK) { + auto Input = GetValues(data, size); + + OK = true; + auto Beg = Input.begin(); + auto End = Input.end(); + + ParamT p(Beg, End); + return p; + } + + // Default + static ParamT DispatchAndCreate(std::integral_constant, const std::uint8_t *data, std::size_t size, bool &OK) { + OK = false; + if (size < sizeof(ParamT)) + return ParamT{}; + OK = true; + ParamT input; + std::memcpy(&input, data, sizeof(ParamT)); + return input; + } +}; + +template +struct ParamTypeHelper> { + using Dist = std::poisson_distribution; + using ParamT = typename Dist::param_type; + using ResultT = typename Dist::result_type; + + static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { + OK = false; + auto vals = GetValues(data, size); + if (vals.empty() || std::isnan(vals[0]) || std::isnan(std::abs(vals[0])) || vals[0] < 0) + return ParamT{}; + OK = true; + return ParamT{vals[0]}; + } +}; + +template +struct ParamTypeHelper> { + using Dist = std::geometric_distribution; + using ParamT = typename Dist::param_type; + using ResultT = typename Dist::result_type; + + static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { + OK = false; + auto vals = GetValues(data, size); + if (vals.empty() || std::isnan(vals[0]) || vals[0] < 0 ) + return ParamT{}; + OK = true; + return ParamT{vals[0]}; + } +}; + +template +struct ParamTypeHelper> { + using Dist = std::lognormal_distribution; + using ParamT = typename Dist::param_type; + using ResultT = typename Dist::result_type; + + static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { + OK = false; + auto vals = GetValues(data, size); + if (vals.size() < 2 ) + return ParamT{}; + OK = true; + return ParamT{vals[0], vals[1]}; + } +}; + +template <> +struct ParamTypeHelper { + using Dist = std::bernoulli_distribution; + using ParamT = Dist::param_type; + using ResultT = Dist::result_type; + + static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { + OK = false; + auto vals = GetValues(data, size); + if (vals.empty()) + return ParamT{}; + OK = true; + return ParamT{vals[0]}; + } +}; + +template +int helper(const std::uint8_t *data, std::size_t size) { + std::mt19937 engine; + using ParamT = typename Distribution::param_type; + bool OK; + ParamT p = ParamTypeHelper::Create(data, size, OK); + if (!OK) + return 0; + Distribution d(p); + volatile auto res = d(engine); + if (std::isnan(res)) { + // FIXME(llvm.org/PR44289): + // Investigate why these distributions are returning NaN and decide + // if that's what we want them to be doing. + // + // Make this assert false (or return non-zero). + return 0; + } + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + return helper>(data, size) || + helper>(data, size) || + helper(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size) || + helper>(data, size); +} diff --git a/libcxx/test/libcxx/fuzzing/regex.pass.cpp b/libcxx/test/libcxx/fuzzing/regex.pass.cpp new file mode 100644 index 0000000..b027dd0 --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/regex.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 +// UNSUPPORTED: no-exceptions + +#include +#include +#include +#include + +#include "fuzz.h" + +template +static int regex_test(const std::uint8_t *data, std::size_t size) { + if (size == 0) + return 0; + + std::string s((const char *)data, size); + std::regex re; + try { + re.assign(s, Syntax); + } catch (std::regex_error &) { + // the data represents an invalid regex, ignore this test case + return 0; + } + + auto match = std::regex_match(s, re); + (void)match; + return 0; // always pretend we succeeded -- we're only looking for crashes +} + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + return regex_test(data, size) || + regex_test(data, size) || + regex_test(data, size) || + regex_test(data, size) || + regex_test(data, size) || + regex_test(data, size); +} diff --git a/libcxx/test/libcxx/fuzzing/regex_ECMAScript.pass.cpp b/libcxx/test/libcxx/fuzzing/regex_ECMAScript.pass.cpp deleted file mode 100644 index 7945494..0000000 --- a/libcxx/test/libcxx/fuzzing/regex_ECMAScript.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -*- C++ -*- -//===--------------------- regex_ECMAScript.cpp ---------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::regex_ECMAScript); diff --git a/libcxx/test/libcxx/fuzzing/regex_POSIX.pass.cpp b/libcxx/test/libcxx/fuzzing/regex_POSIX.pass.cpp deleted file mode 100644 index af94fb7..0000000 --- a/libcxx/test/libcxx/fuzzing/regex_POSIX.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -*- C++ -*- -//===----------------------- regex_POSIX.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::regex_POSIX); diff --git a/libcxx/test/libcxx/fuzzing/regex_awk.pass.cpp b/libcxx/test/libcxx/fuzzing/regex_awk.pass.cpp deleted file mode 100644 index 8040d8b..0000000 --- a/libcxx/test/libcxx/fuzzing/regex_awk.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -*- C++ -*- -//===------------------------- regex_awk.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::regex_awk); diff --git a/libcxx/test/libcxx/fuzzing/regex_egrep.pass.cpp b/libcxx/test/libcxx/fuzzing/regex_egrep.pass.cpp deleted file mode 100644 index 1c7076b..0000000 --- a/libcxx/test/libcxx/fuzzing/regex_egrep.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -*- C++ -*- -//===------------------------ regex_egrep.cpp -----------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::regex_egrep); diff --git a/libcxx/test/libcxx/fuzzing/regex_extended.pass.cpp b/libcxx/test/libcxx/fuzzing/regex_extended.pass.cpp deleted file mode 100644 index d981c8bd..0000000 --- a/libcxx/test/libcxx/fuzzing/regex_extended.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -*- C++ -*- -//===---------------------- regex_extended.cpp ----------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::regex_extended); diff --git a/libcxx/test/libcxx/fuzzing/regex_grep.pass.cpp b/libcxx/test/libcxx/fuzzing/regex_grep.pass.cpp deleted file mode 100644 index 4c0eceb..0000000 --- a/libcxx/test/libcxx/fuzzing/regex_grep.pass.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -*- C++ -*- -//===------------------------ regex_grep.cpp ------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03 - -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::regex_grep); diff --git a/libcxx/test/libcxx/fuzzing/search.pass.cpp b/libcxx/test/libcxx/fuzzing/search.pass.cpp new file mode 100644 index 0000000..e20fad2 --- /dev/null +++ b/libcxx/test/libcxx/fuzzing/search.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + if (size < 2) + return 0; + + const std::size_t pat_size = data[0] * (size - 1) / std::numeric_limits::max(); + assert(pat_size <= size - 1); + const std::uint8_t *pat_begin = data + 1; + const std::uint8_t *pat_end = pat_begin + pat_size; + const std::uint8_t *data_end = data + size; + assert(pat_end <= data_end); + + auto it = std::search(pat_end, data_end, pat_begin, pat_end); + if (it != data_end) // not found + if (!std::equal(pat_begin, pat_end, it)) + return 1; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/sort.pass.cpp b/libcxx/test/libcxx/fuzzing/sort.pass.cpp index c9fcf65..e118897 100644 --- a/libcxx/test/libcxx/fuzzing/sort.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/sort.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===--------------------------- sort.cpp ---------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,22 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + std::vector working(data, data + size); + std::sort(working.begin(), working.end()); -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::sort); + if (!std::is_sorted(working.begin(), working.end())) + return 1; + if (!fast_is_permutation(data, data + size, working.cbegin())) + return 99; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/stable_partition.pass.cpp b/libcxx/test/libcxx/fuzzing/stable_partition.pass.cpp index d8eb359..2a1799dc3 100644 --- a/libcxx/test/libcxx/fuzzing/stable_partition.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/stable_partition.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===--------------------- stable_partition.cpp ---------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,33 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + auto is_even = [](auto b) { return b.key % 2 == 0; }; + + std::vector input; + for (std::size_t i = 0; i < size; ++i) + input.push_back(ByteWithPayload(data[i], i)); + std::vector working = input; + auto iter = std::stable_partition(working.begin(), working.end(), is_even); -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::stable_partition); + if (!std::all_of(working.begin(), iter, is_even)) + return 1; + if (!std::none_of(iter, working.end(), is_even)) + return 2; + if (!std::is_sorted(working.begin(), iter, ByteWithPayload::payload_less())) + return 3; + if (!std::is_sorted(iter, working.end(), ByteWithPayload::payload_less())) + return 4; + if (!fast_is_permutation(input.cbegin(), input.cend(), working.cbegin())) + return 99; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/stable_sort.pass.cpp b/libcxx/test/libcxx/fuzzing/stable_sort.pass.cpp index 666e48b..b493691 100644 --- a/libcxx/test/libcxx/fuzzing/stable_sort.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/stable_sort.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===------------------------ stable_sort.cpp ----------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,34 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + std::vector input; + for (std::size_t i = 0; i < size; ++i) + input.push_back(ByteWithPayload(data[i], i)); + + std::vector working = input; + std::stable_sort(working.begin(), working.end(), ByteWithPayload::key_less()); + + if (!std::is_sorted(working.begin(), working.end(), ByteWithPayload::key_less())) + return 1; -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::stable_sort); + auto iter = working.begin(); + while (iter != working.end()) { + auto range = std::equal_range(iter, working.end(), *iter, ByteWithPayload::key_less()); + if (!std::is_sorted(range.first, range.second, ByteWithPayload::total_less())) + return 2; + iter = range.second; + } + if (!fast_is_permutation(input.cbegin(), input.cend(), working.cbegin())) + return 99; + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/unique.pass.cpp b/libcxx/test/libcxx/fuzzing/unique.pass.cpp index 6ffbd5c..e95c617 100644 --- a/libcxx/test/libcxx/fuzzing/unique.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/unique.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===--------------------------- unique.cpp -------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,49 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + std::vector working(data, data + size); + std::sort(working.begin(), working.end()); + std::vector results = working; + std::vector::iterator new_end = std::unique(results.begin(), results.end()); + std::vector::iterator it; // scratch iterator + + // Check the size of the unique'd sequence. + // it should only be zero if the input sequence was empty. + if (results.begin() == new_end) + return working.size() == 0 ? 0 : 1; + + // 'results' is sorted + if (!std::is_sorted(results.begin(), new_end)) + return 2; + + // All the elements in 'results' must be different + it = results.begin(); + std::uint8_t prev_value = *it++; + for (; it != new_end; ++it) { + if (*it == prev_value) + return 3; + prev_value = *it; + } + + // Every element in 'results' must be in 'working' + for (it = results.begin(); it != new_end; ++it) + if (std::find(working.begin(), working.end(), *it) == working.end()) + return 4; + + // Every element in 'working' must be in 'results' + for (auto v : working) + if (std::find(results.begin(), new_end, v) == new_end) + return 5; -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::unique); + return 0; +} diff --git a/libcxx/test/libcxx/fuzzing/unique_copy.pass.cpp b/libcxx/test/libcxx/fuzzing/unique_copy.pass.cpp index 47a4f95..dfaaa19 100644 --- a/libcxx/test/libcxx/fuzzing/unique_copy.pass.cpp +++ b/libcxx/test/libcxx/fuzzing/unique_copy.pass.cpp @@ -1,5 +1,4 @@ -// -*- C++ -*- -//===------------------------ unique_copy.cpp -----------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,7 +6,51 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03 +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include +#include +#include + +#include "fuzz.h" + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + std::vector working(data, data + size); + std::sort(working.begin(), working.end()); + std::vector results; + (void)std::unique_copy(working.begin(), working.end(), + std::back_inserter>(results)); + std::vector::iterator it; // scratch iterator + + // Check the size of the unique'd sequence. + // it should only be zero if the input sequence was empty. + if (results.size() == 0) + return working.size() == 0 ? 0 : 1; + + // 'results' is sorted + if (!std::is_sorted(results.begin(), results.end())) + return 2; + + // All the elements in 'results' must be different + it = results.begin(); + std::uint8_t prev_value = *it++; + for (; it != results.end(); ++it) { + if (*it == prev_value) + return 3; + prev_value = *it; + } + + // Every element in 'results' must be in 'working' + for (auto v : results) + if (std::find(working.begin(), working.end(), v) == working.end()) + return 4; + + // Every element in 'working' must be in 'results' + for (auto v : working) + if (std::find(results.begin(), results.end(), v) == results.end()) + return 5; -#include "fuzzer_test.h" -FUZZER_TEST(fuzzing::unique_copy); + return 0; +} diff --git a/libcxx/utils/ci/oss-fuzz.sh b/libcxx/utils/ci/oss-fuzz.sh index eac1a27..8a9421a 100755 --- a/libcxx/utils/ci/oss-fuzz.sh +++ b/libcxx/utils/ci/oss-fuzz.sh @@ -4,20 +4,16 @@ # This script runs the continuous fuzzing tests on OSS-Fuzz. # -if [[ $SANITIZER = *undefined* ]]; then - CXXFLAGS="$CXXFLAGS -fsanitize=unsigned-integer-overflow -fsanitize-trap=unsigned-integer-overflow" +if [[ ${SANITIZER} = *undefined* ]]; then + CXXFLAGS="${CXXFLAGS} -fsanitize=unsigned-integer-overflow -fsanitize-trap=unsigned-integer-overflow" fi -for f in $(grep -v "#" libcxx/fuzzing/RoutineNames.txt); do - cat > ${f}_fuzzer.cc < -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - int result = fuzzing::$f(data, size); - assert(result == 0); return 0; -} -EOF - $CXX $CXXFLAGS -std=c++11 ${f}_fuzzer.cc ./libcxx/fuzzing/fuzzing.cpp \ - -nostdinc++ -cxx-isystem ./libcxx/include -iquote ./libcxx \ - -o $OUT/$f $LIB_FUZZING_ENGINE +for test in libcxx/test/libcxx/fuzzing/*.pass.cpp; do + ${CXX} ${CXXFLAGS} \ + -std=c++14 \ + -DLIBCPP_OSS_FUZZ \ + -nostdinc++ -cxx-isystem libcxx/include \ + -o "${OUT}/$(basename ${test})" \ + ${test} \ + ${LIB_FUZZING_ENGINE} done