From dd3f4f9b9200547458f7fb8740fe5c5e9e602d3f Mon Sep 17 00:00:00 2001 From: Bosswestfalen Date: Mon, 12 Dec 2016 19:26:45 +0100 Subject: [PATCH 01/71] Replaced class iterator and class const_iterator with aliases of template iter_impl. iter_impl has operator const_iterator to create an const_iterator from an iterator. --- src/json.hpp | 245 ++++++++++++--------------------------------------- 1 file changed, 55 insertions(+), 190 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 04ea79a2..e95acd95 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -261,10 +261,12 @@ class basic_json /// the type of an element const pointer using const_pointer = typename std::allocator_traits::const_pointer; + // forward declaration for iterators + template class iter_impl; /// an iterator for a basic_json container - class iterator; + using iterator = iter_impl; /// a const iterator for a basic_json container - class const_iterator; + using const_iterator = iter_impl; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container @@ -8204,10 +8206,10 @@ class basic_json public: /*! - @brief a const random access iterator for the @ref basic_json class + @brief a template for a random access iterator for the @ref basic_json class - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. + This class implements a both iterators (iterator and const_iterator) + for the @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the @@ -8222,25 +8224,35 @@ class basic_json @since version 1.0.0 */ - class const_iterator : public std::iterator + template + class iter_impl : public std::iterator { /// allow basic_json to access private members friend class basic_json; + // make sure U is basic_json or const basic_json + static_assert(std::is_same::value + or std::is_same::value, + "iter_impl only accepts (const) basic_json"); + public: /// the type of the values when the iterator is dereferenced using value_type = typename basic_json::value_type; /// a type to represent differences between iterators using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; + using pointer = typename std::conditional::value, + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; + using reference = typename std::conditional::value, + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor - const_iterator() = default; + iter_impl() = default; /*! @brief constructor for a given JSON instance @@ -8248,7 +8260,7 @@ class basic_json @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ - explicit const_iterator(pointer object) noexcept + explicit iter_impl(pointer object) noexcept : m_object(object) { assert(m_object != nullptr); @@ -8275,37 +8287,25 @@ class basic_json } } - /*! - @brief copy constructor given a non-const iterator - @param[in] other iterator to copy from - @note It is not checked whether @a other is initialized. + /* + Use operator const_iterator instead of + const_iterator(const iterator& other) noexcept + to avoid two class definitions for iterator and const_iterator. + + This function is only called if this class is an iterator. + If this class is a const_iterator this function is not called. */ - explicit const_iterator(const iterator& other) noexcept - : m_object(other.m_object) - { - if (m_object != nullptr) - { - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } + operator const_iterator() const + { + const_iterator ret; - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } - } + if (m_object) + { + ret.m_object = m_object; + ret.m_it = m_it; } + + return ret; } /*! @@ -8313,7 +8313,7 @@ class basic_json @param[in] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator(const const_iterator& other) noexcept + iter_impl(const iter_impl& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} @@ -8322,7 +8322,7 @@ class basic_json @param[in,out] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator& operator=(const_iterator other) noexcept( + iter_impl& operator=(iter_impl other) noexcept( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and @@ -8484,7 +8484,7 @@ class basic_json @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator++(int) + iter_impl operator++(int) { auto result = *this; ++(*this); @@ -8495,7 +8495,7 @@ class basic_json @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator++() + iter_impl& operator++() { assert(m_object != nullptr); @@ -8527,7 +8527,7 @@ class basic_json @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator--(int) + iter_impl operator--(int) { auto result = *this; --(*this); @@ -8538,7 +8538,7 @@ class basic_json @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator--() + iter_impl& operator--() { assert(m_object != nullptr); @@ -8570,7 +8570,7 @@ class basic_json @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const const_iterator& other) const + bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -8603,7 +8603,7 @@ class basic_json @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const const_iterator& other) const + bool operator!=(const iter_impl& other) const { return not operator==(other); } @@ -8612,7 +8612,7 @@ class basic_json @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<(const const_iterator& other) const + bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -8645,7 +8645,7 @@ class basic_json @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<=(const const_iterator& other) const + bool operator<=(const iter_impl& other) const { return not other.operator < (*this); } @@ -8654,7 +8654,7 @@ class basic_json @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>(const const_iterator& other) const + bool operator>(const iter_impl& other) const { return not operator<=(other); } @@ -8663,7 +8663,7 @@ class basic_json @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>=(const const_iterator& other) const + bool operator>=(const iter_impl& other) const { return not operator<(other); } @@ -8672,7 +8672,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator+=(difference_type i) + iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); @@ -8703,7 +8703,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator-=(difference_type i) + iter_impl& operator-=(difference_type i) { return operator+=(-i); } @@ -8712,7 +8712,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator+(difference_type i) + iter_impl operator+(difference_type i) { auto result = *this; result += i; @@ -8723,7 +8723,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator-(difference_type i) + iter_impl operator-(difference_type i) { auto result = *this; result -= i; @@ -8734,7 +8734,7 @@ class basic_json @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - difference_type operator-(const const_iterator& other) const + difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); @@ -8830,141 +8830,6 @@ class basic_json internal_iterator m_it = internal_iterator(); }; - /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - explicit iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - return const_cast(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() const - { - return const_cast(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast(base_iterator::value()); - } - }; - /*! @brief a template for a reverse iterator class From 1e981115c962b9fa3e750967bd994020f0610664 Mon Sep 17 00:00:00 2001 From: Yixin Zhang Date: Fri, 9 Dec 2016 21:31:57 -0500 Subject: [PATCH 02/71] Fix issue #380: Signed integer overflow check Instead of checking something like `x * y + z > max` where `x * y` can overflow, check for `x > (max - z) / y` instead. --- src/json.hpp | 1350 +++++++++++++++------------------------------ src/json.hpp.re2c | 12 +- 2 files changed, 442 insertions(+), 920 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 0dfc11fd..2eaf9eb8 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9311,915 +9311,437 @@ class basic_json m_start = m_cursor; assert(m_start != nullptr); - - { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 160, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((m_limit - m_cursor) < 5) - { - fill_line_buffer(5); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - if (yych <= '[') - { - if (yych <= '-') - { - if (yych <= '"') - { - if (yych <= 0x00) - { - goto basic_json_parser_2; - } - if (yych <= '!') - { - goto basic_json_parser_4; - } - goto basic_json_parser_9; - } - else - { - if (yych <= '+') - { - goto basic_json_parser_4; - } - if (yych <= ',') - { - goto basic_json_parser_10; - } - goto basic_json_parser_12; - } - } - else - { - if (yych <= '9') - { - if (yych <= '/') - { - goto basic_json_parser_4; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - goto basic_json_parser_15; - } - else - { - if (yych <= ':') - { - goto basic_json_parser_17; - } - if (yych <= 'Z') - { - goto basic_json_parser_4; - } - goto basic_json_parser_19; - } - } - } - else - { - if (yych <= 'n') - { - if (yych <= 'e') - { - if (yych == ']') - { - goto basic_json_parser_21; - } - goto basic_json_parser_4; - } - else - { - if (yych <= 'f') - { - goto basic_json_parser_23; - } - if (yych <= 'm') - { - goto basic_json_parser_4; - } - goto basic_json_parser_24; - } - } - else - { - if (yych <= 'z') - { - if (yych == 't') - { - goto basic_json_parser_25; - } - goto basic_json_parser_4; - } - else - { - if (yych <= '{') - { - goto basic_json_parser_26; - } - if (yych == '}') - { - goto basic_json_parser_28; - } - goto basic_json_parser_4; - } - } - } -basic_json_parser_2: - ++m_cursor; - { - last_token_type = token_type::end_of_input; - break; - } -basic_json_parser_4: - ++m_cursor; -basic_json_parser_5: - { - last_token_type = token_type::parse_error; - break; - } -basic_json_parser_6: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_6; - } - { - continue; - } -basic_json_parser_9: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x1F) - { - goto basic_json_parser_5; - } - if (yych <= 0x7F) - { - goto basic_json_parser_31; - } - if (yych <= 0xC1) - { - goto basic_json_parser_5; - } - if (yych <= 0xF4) - { - goto basic_json_parser_31; - } - goto basic_json_parser_5; -basic_json_parser_10: - ++m_cursor; - { - last_token_type = token_type::value_separator; - break; - } -basic_json_parser_12: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_5; - } - if (yych <= '0') - { - goto basic_json_parser_13; - } - if (yych <= '9') - { - goto basic_json_parser_15; - } - goto basic_json_parser_5; -basic_json_parser_13: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_43; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - } -basic_json_parser_14: - { - last_token_type = token_type::value_number; - break; - } -basic_json_parser_15: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - fill_line_buffer(3); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yybm[0 + yych] & 64) - { - goto basic_json_parser_15; - } - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_43; - } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_14; - } -basic_json_parser_17: - ++m_cursor; - { - last_token_type = token_type::name_separator; - break; - } -basic_json_parser_19: - ++m_cursor; - { - last_token_type = token_type::begin_array; - break; - } -basic_json_parser_21: - ++m_cursor; - { - last_token_type = token_type::end_array; - break; - } -basic_json_parser_23: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_45; - } - goto basic_json_parser_5; -basic_json_parser_24: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') - { - goto basic_json_parser_46; - } - goto basic_json_parser_5; -basic_json_parser_25: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') - { - goto basic_json_parser_47; - } - goto basic_json_parser_5; -basic_json_parser_26: - ++m_cursor; - { - last_token_type = token_type::begin_object; - break; - } -basic_json_parser_28: - ++m_cursor; - { - last_token_type = token_type::end_object; - break; - } -basic_json_parser_30: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; -basic_json_parser_31: - if (yybm[0 + yych] & 128) - { - goto basic_json_parser_30; - } - if (yych <= 0xE0) - { - if (yych <= '\\') - { - if (yych <= 0x1F) - { - goto basic_json_parser_32; - } - if (yych <= '"') - { - goto basic_json_parser_33; - } - goto basic_json_parser_35; - } - else - { - if (yych <= 0xC1) - { - goto basic_json_parser_32; - } - if (yych <= 0xDF) - { - goto basic_json_parser_36; - } - goto basic_json_parser_37; - } - } - else - { - if (yych <= 0xEF) - { - if (yych == 0xED) - { - goto basic_json_parser_39; - } - goto basic_json_parser_38; - } - else - { - if (yych <= 0xF0) - { - goto basic_json_parser_40; - } - if (yych <= 0xF3) - { - goto basic_json_parser_41; - } - if (yych <= 0xF4) - { - goto basic_json_parser_42; - } - } - } -basic_json_parser_32: - m_cursor = m_marker; - if (yyaccept == 0) - { - goto basic_json_parser_5; - } - else - { - goto basic_json_parser_14; - } -basic_json_parser_33: - ++m_cursor; - { - last_token_type = token_type::value_string; - break; - } -basic_json_parser_35: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 'e') - { - if (yych <= '/') - { - if (yych == '"') - { - goto basic_json_parser_30; - } - if (yych <= '.') - { - goto basic_json_parser_32; - } - goto basic_json_parser_30; - } - else - { - if (yych <= '\\') - { - if (yych <= '[') - { - goto basic_json_parser_32; - } - goto basic_json_parser_30; - } - else - { - if (yych == 'b') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } - } - } - else - { - if (yych <= 'q') - { - if (yych <= 'f') - { - goto basic_json_parser_30; - } - if (yych == 'n') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 's') - { - if (yych <= 'r') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 't') - { - goto basic_json_parser_30; - } - if (yych <= 'u') - { - goto basic_json_parser_48; - } - goto basic_json_parser_32; - } - } - } -basic_json_parser_36: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x7F) - { - goto basic_json_parser_32; - } - if (yych <= 0xBF) - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; -basic_json_parser_37: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x9F) - { - goto basic_json_parser_32; - } - if (yych <= 0xBF) - { - goto basic_json_parser_36; - } - goto basic_json_parser_32; -basic_json_parser_38: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x7F) - { - goto basic_json_parser_32; - } - if (yych <= 0xBF) - { - goto basic_json_parser_36; - } - goto basic_json_parser_32; -basic_json_parser_39: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x7F) - { - goto basic_json_parser_32; - } - if (yych <= 0x9F) - { - goto basic_json_parser_36; - } - goto basic_json_parser_32; -basic_json_parser_40: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x8F) - { - goto basic_json_parser_32; - } - if (yych <= 0xBF) - { - goto basic_json_parser_38; - } - goto basic_json_parser_32; -basic_json_parser_41: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x7F) - { - goto basic_json_parser_32; - } - if (yych <= 0xBF) - { - goto basic_json_parser_38; - } - goto basic_json_parser_32; -basic_json_parser_42: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 0x7F) - { - goto basic_json_parser_32; - } - if (yych <= 0x8F) - { - goto basic_json_parser_38; - } - goto basic_json_parser_32; -basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_49; - } - goto basic_json_parser_32; -basic_json_parser_44: - yych = *++m_cursor; - if (yych <= ',') - { - if (yych == '+') - { - goto basic_json_parser_51; - } - goto basic_json_parser_32; - } - else - { - if (yych <= '-') - { - goto basic_json_parser_51; - } - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_52; - } - goto basic_json_parser_32; - } -basic_json_parser_45: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_54; - } - goto basic_json_parser_32; -basic_json_parser_46: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_55; - } - goto basic_json_parser_32; -basic_json_parser_47: - yych = *++m_cursor; - if (yych == 'u') - { - goto basic_json_parser_56; - } - goto basic_json_parser_32; -basic_json_parser_48: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_57; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_57; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_57; - } - goto basic_json_parser_32; - } -basic_json_parser_49: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - fill_line_buffer(3); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= 'D') - { - if (yych <= '/') - { - goto basic_json_parser_14; - } - if (yych <= '9') - { - goto basic_json_parser_49; - } - goto basic_json_parser_14; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_14; - } -basic_json_parser_51: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych >= ':') - { - goto basic_json_parser_32; - } -basic_json_parser_52: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_14; - } - if (yych <= '9') - { - goto basic_json_parser_52; - } - goto basic_json_parser_14; -basic_json_parser_54: - yych = *++m_cursor; - if (yych == 's') - { - goto basic_json_parser_58; - } - goto basic_json_parser_32; -basic_json_parser_55: - yych = *++m_cursor; - if (yych == 'l') - { - goto basic_json_parser_59; - } - goto basic_json_parser_32; -basic_json_parser_56: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_61; - } - goto basic_json_parser_32; -basic_json_parser_57: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_63; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_63; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_63; - } - goto basic_json_parser_32; - } -basic_json_parser_58: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_64; - } - goto basic_json_parser_32; -basic_json_parser_59: - ++m_cursor; - { - last_token_type = token_type::literal_null; - break; - } -basic_json_parser_61: - ++m_cursor; - { - last_token_type = token_type::literal_true; - break; - } -basic_json_parser_63: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_66; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_66; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_66; - } - goto basic_json_parser_32; - } -basic_json_parser_64: - ++m_cursor; - { - last_token_type = token_type::literal_false; - break; - } -basic_json_parser_66: - ++m_cursor; - if (m_limit <= m_cursor) - { - fill_line_buffer(1); // LCOV_EXCL_LINE - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_32; - } - if (yych <= '9') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_30; - } - if (yych <= '`') - { - goto basic_json_parser_32; - } - if (yych <= 'f') - { - goto basic_json_parser_30; - } - goto basic_json_parser_32; - } + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + if ((m_limit - m_cursor) < 5) fill_line_buffer(5); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yybm[0+yych] & 32) { + goto basic_json_parser_6; + } + if (yych <= '[') { + if (yych <= '-') { + if (yych <= '"') { + if (yych <= 0x00) goto basic_json_parser_2; + if (yych <= '!') goto basic_json_parser_4; + goto basic_json_parser_9; + } else { + if (yych <= '+') goto basic_json_parser_4; + if (yych <= ',') goto basic_json_parser_10; + goto basic_json_parser_12; } + } else { + if (yych <= '9') { + if (yych <= '/') goto basic_json_parser_4; + if (yych <= '0') goto basic_json_parser_13; + goto basic_json_parser_15; + } else { + if (yych <= ':') goto basic_json_parser_17; + if (yych <= 'Z') goto basic_json_parser_4; + goto basic_json_parser_19; + } + } + } else { + if (yych <= 'n') { + if (yych <= 'e') { + if (yych == ']') goto basic_json_parser_21; + goto basic_json_parser_4; + } else { + if (yych <= 'f') goto basic_json_parser_23; + if (yych <= 'm') goto basic_json_parser_4; + goto basic_json_parser_24; + } + } else { + if (yych <= 'z') { + if (yych == 't') goto basic_json_parser_25; + goto basic_json_parser_4; + } else { + if (yych <= '{') goto basic_json_parser_26; + if (yych == '}') goto basic_json_parser_28; + goto basic_json_parser_4; + } + } + } +basic_json_parser_2: + ++m_cursor; + { last_token_type = token_type::end_of_input; break; } +basic_json_parser_4: + ++m_cursor; +basic_json_parser_5: + { last_token_type = token_type::parse_error; break; } +basic_json_parser_6: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yybm[0+yych] & 32) { + goto basic_json_parser_6; + } + { continue; } +basic_json_parser_9: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) goto basic_json_parser_5; + if (yych <= 0x7F) goto basic_json_parser_31; + if (yych <= 0xC1) goto basic_json_parser_5; + if (yych <= 0xF4) goto basic_json_parser_31; + goto basic_json_parser_5; +basic_json_parser_10: + ++m_cursor; + { last_token_type = token_type::value_separator; break; } +basic_json_parser_12: + yych = *++m_cursor; + if (yych <= '/') goto basic_json_parser_5; + if (yych <= '0') goto basic_json_parser_13; + if (yych <= '9') goto basic_json_parser_15; + goto basic_json_parser_5; +basic_json_parser_13: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') { + if (yych == '.') goto basic_json_parser_43; + } else { + if (yych <= 'E') goto basic_json_parser_44; + if (yych == 'e') goto basic_json_parser_44; + } +basic_json_parser_14: + { last_token_type = token_type::value_number; break; } +basic_json_parser_15: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) fill_line_buffer(3); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yybm[0+yych] & 64) { + goto basic_json_parser_15; + } + if (yych <= 'D') { + if (yych == '.') goto basic_json_parser_43; + goto basic_json_parser_14; + } else { + if (yych <= 'E') goto basic_json_parser_44; + if (yych == 'e') goto basic_json_parser_44; + goto basic_json_parser_14; + } +basic_json_parser_17: + ++m_cursor; + { last_token_type = token_type::name_separator; break; } +basic_json_parser_19: + ++m_cursor; + { last_token_type = token_type::begin_array; break; } +basic_json_parser_21: + ++m_cursor; + { last_token_type = token_type::end_array; break; } +basic_json_parser_23: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') goto basic_json_parser_45; + goto basic_json_parser_5; +basic_json_parser_24: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') goto basic_json_parser_46; + goto basic_json_parser_5; +basic_json_parser_25: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') goto basic_json_parser_47; + goto basic_json_parser_5; +basic_json_parser_26: + ++m_cursor; + { last_token_type = token_type::begin_object; break; } +basic_json_parser_28: + ++m_cursor; + { last_token_type = token_type::end_object; break; } +basic_json_parser_30: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; +basic_json_parser_31: + if (yybm[0+yych] & 128) { + goto basic_json_parser_30; + } + if (yych <= 0xE0) { + if (yych <= '\\') { + if (yych <= 0x1F) goto basic_json_parser_32; + if (yych <= '"') goto basic_json_parser_33; + goto basic_json_parser_35; + } else { + if (yych <= 0xC1) goto basic_json_parser_32; + if (yych <= 0xDF) goto basic_json_parser_36; + goto basic_json_parser_37; + } + } else { + if (yych <= 0xEF) { + if (yych == 0xED) goto basic_json_parser_39; + goto basic_json_parser_38; + } else { + if (yych <= 0xF0) goto basic_json_parser_40; + if (yych <= 0xF3) goto basic_json_parser_41; + if (yych <= 0xF4) goto basic_json_parser_42; + } + } +basic_json_parser_32: + m_cursor = m_marker; + if (yyaccept == 0) { + goto basic_json_parser_5; + } else { + goto basic_json_parser_14; + } +basic_json_parser_33: + ++m_cursor; + { last_token_type = token_type::value_string; break; } +basic_json_parser_35: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 'e') { + if (yych <= '/') { + if (yych == '"') goto basic_json_parser_30; + if (yych <= '.') goto basic_json_parser_32; + goto basic_json_parser_30; + } else { + if (yych <= '\\') { + if (yych <= '[') goto basic_json_parser_32; + goto basic_json_parser_30; + } else { + if (yych == 'b') goto basic_json_parser_30; + goto basic_json_parser_32; + } + } + } else { + if (yych <= 'q') { + if (yych <= 'f') goto basic_json_parser_30; + if (yych == 'n') goto basic_json_parser_30; + goto basic_json_parser_32; + } else { + if (yych <= 's') { + if (yych <= 'r') goto basic_json_parser_30; + goto basic_json_parser_32; + } else { + if (yych <= 't') goto basic_json_parser_30; + if (yych <= 'u') goto basic_json_parser_48; + goto basic_json_parser_32; + } + } + } +basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x7F) goto basic_json_parser_32; + if (yych <= 0xBF) goto basic_json_parser_30; + goto basic_json_parser_32; +basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x9F) goto basic_json_parser_32; + if (yych <= 0xBF) goto basic_json_parser_36; + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x7F) goto basic_json_parser_32; + if (yych <= 0xBF) goto basic_json_parser_36; + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x7F) goto basic_json_parser_32; + if (yych <= 0x9F) goto basic_json_parser_36; + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x8F) goto basic_json_parser_32; + if (yych <= 0xBF) goto basic_json_parser_38; + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x7F) goto basic_json_parser_32; + if (yych <= 0xBF) goto basic_json_parser_38; + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 0x7F) goto basic_json_parser_32; + if (yych <= 0x8F) goto basic_json_parser_38; + goto basic_json_parser_32; +basic_json_parser_43: + yych = *++m_cursor; + if (yych <= '/') goto basic_json_parser_32; + if (yych <= '9') goto basic_json_parser_49; + goto basic_json_parser_32; +basic_json_parser_44: + yych = *++m_cursor; + if (yych <= ',') { + if (yych == '+') goto basic_json_parser_51; + goto basic_json_parser_32; + } else { + if (yych <= '-') goto basic_json_parser_51; + if (yych <= '/') goto basic_json_parser_32; + if (yych <= '9') goto basic_json_parser_52; + goto basic_json_parser_32; + } +basic_json_parser_45: + yych = *++m_cursor; + if (yych == 'l') goto basic_json_parser_54; + goto basic_json_parser_32; +basic_json_parser_46: + yych = *++m_cursor; + if (yych == 'l') goto basic_json_parser_55; + goto basic_json_parser_32; +basic_json_parser_47: + yych = *++m_cursor; + if (yych == 'u') goto basic_json_parser_56; + goto basic_json_parser_32; +basic_json_parser_48: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') goto basic_json_parser_32; + if (yych <= '9') goto basic_json_parser_57; + goto basic_json_parser_32; + } else { + if (yych <= 'F') goto basic_json_parser_57; + if (yych <= '`') goto basic_json_parser_32; + if (yych <= 'f') goto basic_json_parser_57; + goto basic_json_parser_32; + } +basic_json_parser_49: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) fill_line_buffer(3); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= 'D') { + if (yych <= '/') goto basic_json_parser_14; + if (yych <= '9') goto basic_json_parser_49; + goto basic_json_parser_14; + } else { + if (yych <= 'E') goto basic_json_parser_44; + if (yych == 'e') goto basic_json_parser_44; + goto basic_json_parser_14; + } +basic_json_parser_51: + yych = *++m_cursor; + if (yych <= '/') goto basic_json_parser_32; + if (yych >= ':') goto basic_json_parser_32; +basic_json_parser_52: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= '/') goto basic_json_parser_14; + if (yych <= '9') goto basic_json_parser_52; + goto basic_json_parser_14; +basic_json_parser_54: + yych = *++m_cursor; + if (yych == 's') goto basic_json_parser_58; + goto basic_json_parser_32; +basic_json_parser_55: + yych = *++m_cursor; + if (yych == 'l') goto basic_json_parser_59; + goto basic_json_parser_32; +basic_json_parser_56: + yych = *++m_cursor; + if (yych == 'e') goto basic_json_parser_61; + goto basic_json_parser_32; +basic_json_parser_57: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') goto basic_json_parser_32; + if (yych <= '9') goto basic_json_parser_63; + goto basic_json_parser_32; + } else { + if (yych <= 'F') goto basic_json_parser_63; + if (yych <= '`') goto basic_json_parser_32; + if (yych <= 'f') goto basic_json_parser_63; + goto basic_json_parser_32; + } +basic_json_parser_58: + yych = *++m_cursor; + if (yych == 'e') goto basic_json_parser_64; + goto basic_json_parser_32; +basic_json_parser_59: + ++m_cursor; + { last_token_type = token_type::literal_null; break; } +basic_json_parser_61: + ++m_cursor; + { last_token_type = token_type::literal_true; break; } +basic_json_parser_63: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') goto basic_json_parser_32; + if (yych <= '9') goto basic_json_parser_66; + goto basic_json_parser_32; + } else { + if (yych <= 'F') goto basic_json_parser_66; + if (yych <= '`') goto basic_json_parser_32; + if (yych <= 'f') goto basic_json_parser_66; + goto basic_json_parser_32; + } +basic_json_parser_64: + ++m_cursor; + { last_token_type = token_type::literal_false; break; } +basic_json_parser_66: + ++m_cursor; + if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE + yych = *m_cursor; + if (yych <= '@') { + if (yych <= '/') goto basic_json_parser_32; + if (yych <= '9') goto basic_json_parser_30; + goto basic_json_parser_32; + } else { + if (yych <= 'F') goto basic_json_parser_30; + if (yych <= '`') goto basic_json_parser_32; + if (yych <= 'f') goto basic_json_parser_30; + goto basic_json_parser_32; + } + } } @@ -10619,19 +10141,19 @@ basic_json_parser_66: // skip if definitely not an integer if (type != value_t::number_float) { - // multiply last value by ten and add the new digit - auto temp = value * 10 + *curptr - '0'; + auto digit = *curptr - '0'; - // test for overflow - if (temp < value || temp > max) + // overflow if value * 10 + digit > max, move terms around + // to avoid overflow in intermediate values + if (value > (max - digit) / 10) { // overflow type = value_t::number_float; } else { - // no overflow - save it - value = temp; + // no overflow + value = value * 10 + digit; } } } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 72fd0474..57c502d3 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -9769,19 +9769,19 @@ class basic_json // skip if definitely not an integer if (type != value_t::number_float) { - // multiply last value by ten and add the new digit - auto temp = value * 10 + *curptr - '0'; + auto digit = *curptr - '0'; - // test for overflow - if (temp < value || temp > max) + // overflow if value * 10 + digit > max, move terms around + // to avoid overflow in intermediate values + if (value > (max - digit) / 10) { // overflow type = value_t::number_float; } else { - // no overflow - save it - value = temp; + // no overflow + value = value * 10 + digit; } } } From bd6422f5835beca46e0a3652fb6b5d93312fa442 Mon Sep 17 00:00:00 2001 From: Yixin Zhang Date: Sat, 10 Dec 2016 14:21:13 -0500 Subject: [PATCH 03/71] Ran `make pretty` --- src/json.hpp | 1242 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 860 insertions(+), 382 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 2eaf9eb8..e64484e3 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9311,437 +9311,915 @@ class basic_json m_start = m_cursor; assert(m_start != nullptr); - - { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 160, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((m_limit - m_cursor) < 5) fill_line_buffer(5); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yybm[0+yych] & 32) { - goto basic_json_parser_6; - } - if (yych <= '[') { - if (yych <= '-') { - if (yych <= '"') { - if (yych <= 0x00) goto basic_json_parser_2; - if (yych <= '!') goto basic_json_parser_4; - goto basic_json_parser_9; - } else { - if (yych <= '+') goto basic_json_parser_4; - if (yych <= ',') goto basic_json_parser_10; - goto basic_json_parser_12; - } - } else { - if (yych <= '9') { - if (yych <= '/') goto basic_json_parser_4; - if (yych <= '0') goto basic_json_parser_13; - goto basic_json_parser_15; - } else { - if (yych <= ':') goto basic_json_parser_17; - if (yych <= 'Z') goto basic_json_parser_4; - goto basic_json_parser_19; - } - } - } else { - if (yych <= 'n') { - if (yych <= 'e') { - if (yych == ']') goto basic_json_parser_21; - goto basic_json_parser_4; - } else { - if (yych <= 'f') goto basic_json_parser_23; - if (yych <= 'm') goto basic_json_parser_4; - goto basic_json_parser_24; - } - } else { - if (yych <= 'z') { - if (yych == 't') goto basic_json_parser_25; - goto basic_json_parser_4; - } else { - if (yych <= '{') goto basic_json_parser_26; - if (yych == '}') goto basic_json_parser_28; - goto basic_json_parser_4; - } - } - } + + { + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + if ((m_limit - m_cursor) < 5) + { + fill_line_buffer(5); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + if (yych <= '[') + { + if (yych <= '-') + { + if (yych <= '"') + { + if (yych <= 0x00) + { + goto basic_json_parser_2; + } + if (yych <= '!') + { + goto basic_json_parser_4; + } + goto basic_json_parser_9; + } + else + { + if (yych <= '+') + { + goto basic_json_parser_4; + } + if (yych <= ',') + { + goto basic_json_parser_10; + } + goto basic_json_parser_12; + } + } + else + { + if (yych <= '9') + { + if (yych <= '/') + { + goto basic_json_parser_4; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + goto basic_json_parser_15; + } + else + { + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych <= 'Z') + { + goto basic_json_parser_4; + } + goto basic_json_parser_19; + } + } + } + else + { + if (yych <= 'n') + { + if (yych <= 'e') + { + if (yych == ']') + { + goto basic_json_parser_21; + } + goto basic_json_parser_4; + } + else + { + if (yych <= 'f') + { + goto basic_json_parser_23; + } + if (yych <= 'm') + { + goto basic_json_parser_4; + } + goto basic_json_parser_24; + } + } + else + { + if (yych <= 'z') + { + if (yych == 't') + { + goto basic_json_parser_25; + } + goto basic_json_parser_4; + } + else + { + if (yych <= '{') + { + goto basic_json_parser_26; + } + if (yych == '}') + { + goto basic_json_parser_28; + } + goto basic_json_parser_4; + } + } + } basic_json_parser_2: - ++m_cursor; - { last_token_type = token_type::end_of_input; break; } + ++m_cursor; + { + last_token_type = token_type::end_of_input; + break; + } basic_json_parser_4: - ++m_cursor; + ++m_cursor; basic_json_parser_5: - { last_token_type = token_type::parse_error; break; } + { + last_token_type = token_type::parse_error; + break; + } basic_json_parser_6: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yybm[0+yych] & 32) { - goto basic_json_parser_6; - } - { continue; } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + { + continue; + } basic_json_parser_9: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x1F) goto basic_json_parser_5; - if (yych <= 0x7F) goto basic_json_parser_31; - if (yych <= 0xC1) goto basic_json_parser_5; - if (yych <= 0xF4) goto basic_json_parser_31; - goto basic_json_parser_5; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) + { + goto basic_json_parser_5; + } + if (yych <= 0x7F) + { + goto basic_json_parser_31; + } + if (yych <= 0xC1) + { + goto basic_json_parser_5; + } + if (yych <= 0xF4) + { + goto basic_json_parser_31; + } + goto basic_json_parser_5; basic_json_parser_10: - ++m_cursor; - { last_token_type = token_type::value_separator; break; } + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } basic_json_parser_12: - yych = *++m_cursor; - if (yych <= '/') goto basic_json_parser_5; - if (yych <= '0') goto basic_json_parser_13; - if (yych <= '9') goto basic_json_parser_15; - goto basic_json_parser_5; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_5; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + if (yych <= '9') + { + goto basic_json_parser_15; + } + goto basic_json_parser_5; basic_json_parser_13: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') { - if (yych == '.') goto basic_json_parser_43; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - } + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + } basic_json_parser_14: - { last_token_type = token_type::value_number; break; } + { + last_token_type = token_type::value_number; + break; + } basic_json_parser_15: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) fill_line_buffer(3); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yybm[0+yych] & 64) { - goto basic_json_parser_15; - } - if (yych <= 'D') { - if (yych == '.') goto basic_json_parser_43; - goto basic_json_parser_14; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - goto basic_json_parser_14; - } + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 64) + { + goto basic_json_parser_15; + } + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_43; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } basic_json_parser_17: - ++m_cursor; - { last_token_type = token_type::name_separator; break; } + ++m_cursor; + { + last_token_type = token_type::name_separator; + break; + } basic_json_parser_19: - ++m_cursor; - { last_token_type = token_type::begin_array; break; } + ++m_cursor; + { + last_token_type = token_type::begin_array; + break; + } basic_json_parser_21: - ++m_cursor; - { last_token_type = token_type::end_array; break; } + ++m_cursor; + { + last_token_type = token_type::end_array; + break; + } basic_json_parser_23: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') goto basic_json_parser_45; - goto basic_json_parser_5; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_45; + } + goto basic_json_parser_5; basic_json_parser_24: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') goto basic_json_parser_46; - goto basic_json_parser_5; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_46; + } + goto basic_json_parser_5; basic_json_parser_25: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') goto basic_json_parser_47; - goto basic_json_parser_5; + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_47; + } + goto basic_json_parser_5; basic_json_parser_26: - ++m_cursor; - { last_token_type = token_type::begin_object; break; } + ++m_cursor; + { + last_token_type = token_type::begin_object; + break; + } basic_json_parser_28: - ++m_cursor; - { last_token_type = token_type::end_object; break; } + ++m_cursor; + { + last_token_type = token_type::end_object; + break; + } basic_json_parser_30: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; basic_json_parser_31: - if (yybm[0+yych] & 128) { - goto basic_json_parser_30; - } - if (yych <= 0xE0) { - if (yych <= '\\') { - if (yych <= 0x1F) goto basic_json_parser_32; - if (yych <= '"') goto basic_json_parser_33; - goto basic_json_parser_35; - } else { - if (yych <= 0xC1) goto basic_json_parser_32; - if (yych <= 0xDF) goto basic_json_parser_36; - goto basic_json_parser_37; - } - } else { - if (yych <= 0xEF) { - if (yych == 0xED) goto basic_json_parser_39; - goto basic_json_parser_38; - } else { - if (yych <= 0xF0) goto basic_json_parser_40; - if (yych <= 0xF3) goto basic_json_parser_41; - if (yych <= 0xF4) goto basic_json_parser_42; - } - } + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_30; + } + if (yych <= 0xE0) + { + if (yych <= '\\') + { + if (yych <= 0x1F) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_33; + } + goto basic_json_parser_35; + } + else + { + if (yych <= 0xC1) + { + goto basic_json_parser_32; + } + if (yych <= 0xDF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_37; + } + } + else + { + if (yych <= 0xEF) + { + if (yych == 0xED) + { + goto basic_json_parser_39; + } + goto basic_json_parser_38; + } + else + { + if (yych <= 0xF0) + { + goto basic_json_parser_40; + } + if (yych <= 0xF3) + { + goto basic_json_parser_41; + } + if (yych <= 0xF4) + { + goto basic_json_parser_42; + } + } + } basic_json_parser_32: - m_cursor = m_marker; - if (yyaccept == 0) { - goto basic_json_parser_5; - } else { - goto basic_json_parser_14; - } + m_cursor = m_marker; + if (yyaccept == 0) + { + goto basic_json_parser_5; + } + else + { + goto basic_json_parser_14; + } basic_json_parser_33: - ++m_cursor; - { last_token_type = token_type::value_string; break; } + ++m_cursor; + { + last_token_type = token_type::value_string; + break; + } basic_json_parser_35: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 'e') { - if (yych <= '/') { - if (yych == '"') goto basic_json_parser_30; - if (yych <= '.') goto basic_json_parser_32; - goto basic_json_parser_30; - } else { - if (yych <= '\\') { - if (yych <= '[') goto basic_json_parser_32; - goto basic_json_parser_30; - } else { - if (yych == 'b') goto basic_json_parser_30; - goto basic_json_parser_32; - } - } - } else { - if (yych <= 'q') { - if (yych <= 'f') goto basic_json_parser_30; - if (yych == 'n') goto basic_json_parser_30; - goto basic_json_parser_32; - } else { - if (yych <= 's') { - if (yych <= 'r') goto basic_json_parser_30; - goto basic_json_parser_32; - } else { - if (yych <= 't') goto basic_json_parser_30; - if (yych <= 'u') goto basic_json_parser_48; - goto basic_json_parser_32; - } - } - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'e') + { + if (yych <= '/') + { + if (yych == '"') + { + goto basic_json_parser_30; + } + if (yych <= '.') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych <= '\\') + { + if (yych <= '[') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych == 'b') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } + } + else + { + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_30; + } + if (yych == 'n') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 't') + { + goto basic_json_parser_30; + } + if (yych <= 'u') + { + goto basic_json_parser_48; + } + goto basic_json_parser_32; + } + } + } basic_json_parser_36: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_30; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; basic_json_parser_37: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x9F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_36; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; basic_json_parser_38: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_36; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; basic_json_parser_39: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0x9F) goto basic_json_parser_36; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; basic_json_parser_40: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x8F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_38; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; basic_json_parser_41: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0xBF) goto basic_json_parser_38; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; basic_json_parser_42: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 0x7F) goto basic_json_parser_32; - if (yych <= 0x8F) goto basic_json_parser_38; - goto basic_json_parser_32; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_49; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_32; basic_json_parser_44: - yych = *++m_cursor; - if (yych <= ',') { - if (yych == '+') goto basic_json_parser_51; - goto basic_json_parser_32; - } else { - if (yych <= '-') goto basic_json_parser_51; - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_52; - goto basic_json_parser_32; - } + yych = *++m_cursor; + if (yych <= ',') + { + if (yych == '+') + { + goto basic_json_parser_51; + } + goto basic_json_parser_32; + } + else + { + if (yych <= '-') + { + goto basic_json_parser_51; + } + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_52; + } + goto basic_json_parser_32; + } basic_json_parser_45: - yych = *++m_cursor; - if (yych == 'l') goto basic_json_parser_54; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_54; + } + goto basic_json_parser_32; basic_json_parser_46: - yych = *++m_cursor; - if (yych == 'l') goto basic_json_parser_55; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_55; + } + goto basic_json_parser_32; basic_json_parser_47: - yych = *++m_cursor; - if (yych == 'u') goto basic_json_parser_56; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 'u') + { + goto basic_json_parser_56; + } + goto basic_json_parser_32; basic_json_parser_48: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_57; - goto basic_json_parser_32; - } else { - if (yych <= 'F') goto basic_json_parser_57; - if (yych <= '`') goto basic_json_parser_32; - if (yych <= 'f') goto basic_json_parser_57; - goto basic_json_parser_32; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_57; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_57; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_57; + } + goto basic_json_parser_32; + } basic_json_parser_49: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) fill_line_buffer(3); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= 'D') { - if (yych <= '/') goto basic_json_parser_14; - if (yych <= '9') goto basic_json_parser_49; - goto basic_json_parser_14; - } else { - if (yych <= 'E') goto basic_json_parser_44; - if (yych == 'e') goto basic_json_parser_44; - goto basic_json_parser_14; - } + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_49; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_44; + } + if (yych == 'e') + { + goto basic_json_parser_44; + } + goto basic_json_parser_14; + } basic_json_parser_51: - yych = *++m_cursor; - if (yych <= '/') goto basic_json_parser_32; - if (yych >= ':') goto basic_json_parser_32; + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } basic_json_parser_52: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= '/') goto basic_json_parser_14; - if (yych <= '9') goto basic_json_parser_52; - goto basic_json_parser_14; + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_14; + } + if (yych <= '9') + { + goto basic_json_parser_52; + } + goto basic_json_parser_14; basic_json_parser_54: - yych = *++m_cursor; - if (yych == 's') goto basic_json_parser_58; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 's') + { + goto basic_json_parser_58; + } + goto basic_json_parser_32; basic_json_parser_55: - yych = *++m_cursor; - if (yych == 'l') goto basic_json_parser_59; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_59; + } + goto basic_json_parser_32; basic_json_parser_56: - yych = *++m_cursor; - if (yych == 'e') goto basic_json_parser_61; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_61; + } + goto basic_json_parser_32; basic_json_parser_57: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_63; - goto basic_json_parser_32; - } else { - if (yych <= 'F') goto basic_json_parser_63; - if (yych <= '`') goto basic_json_parser_32; - if (yych <= 'f') goto basic_json_parser_63; - goto basic_json_parser_32; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_63; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; + } basic_json_parser_58: - yych = *++m_cursor; - if (yych == 'e') goto basic_json_parser_64; - goto basic_json_parser_32; + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_64; + } + goto basic_json_parser_32; basic_json_parser_59: - ++m_cursor; - { last_token_type = token_type::literal_null; break; } + ++m_cursor; + { + last_token_type = token_type::literal_null; + break; + } basic_json_parser_61: - ++m_cursor; - { last_token_type = token_type::literal_true; break; } + ++m_cursor; + { + last_token_type = token_type::literal_true; + break; + } basic_json_parser_63: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_66; - goto basic_json_parser_32; - } else { - if (yych <= 'F') goto basic_json_parser_66; - if (yych <= '`') goto basic_json_parser_32; - if (yych <= 'f') goto basic_json_parser_66; - goto basic_json_parser_32; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_66; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; + } basic_json_parser_64: - ++m_cursor; - { last_token_type = token_type::literal_false; break; } + ++m_cursor; + { + last_token_type = token_type::literal_false; + break; + } basic_json_parser_66: - ++m_cursor; - if (m_limit <= m_cursor) fill_line_buffer(1); // LCOV_EXCL_LINE - yych = *m_cursor; - if (yych <= '@') { - if (yych <= '/') goto basic_json_parser_32; - if (yych <= '9') goto basic_json_parser_30; - goto basic_json_parser_32; - } else { - if (yych <= 'F') goto basic_json_parser_30; - if (yych <= '`') goto basic_json_parser_32; - if (yych <= 'f') goto basic_json_parser_30; - goto basic_json_parser_32; - } - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_30; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } } From 703d4baf8b7589a033b36eb4d394533eebc62552 Mon Sep 17 00:00:00 2001 From: Yixin Zhang Date: Mon, 12 Dec 2016 20:59:43 -0500 Subject: [PATCH 04/71] Fixed conversion warnings Use static_cast on digit. --- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index e64484e3..ff409559 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -10619,7 +10619,7 @@ basic_json_parser_66: // skip if definitely not an integer if (type != value_t::number_float) { - auto digit = *curptr - '0'; + auto digit = static_cast(*curptr - '0'); // overflow if value * 10 + digit > max, move terms around // to avoid overflow in intermediate values diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 57c502d3..4366fe6d 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -9769,7 +9769,7 @@ class basic_json // skip if definitely not an integer if (type != value_t::number_float) { - auto digit = *curptr - '0'; + auto digit = static_cast(*curptr - '0'); // overflow if value * 10 + digit > max, move terms around // to avoid overflow in intermediate values From f5d4a9c4eb5ded73dfe3307342592337f3c8dde0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 13 Dec 2016 17:51:46 +0100 Subject: [PATCH 05/71] :construction_worker: added Clang 3.9.0 and sanitizer build #394 --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index bb0e504d..fb8f44bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,15 @@ matrix: after_success: - make check TEST_PREFIX="valgrind --error-exitcode=1 --leak-check=full " TEST_PATTERN="" + # CLang sanitizers + + - os: linux + env: LLVM_VERSION=3.9.0 + compiler: clang + after_success: + - make clean + - CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" make check + # cppcheck - os: linux @@ -190,6 +199,7 @@ cache: - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.1 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.0 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.1 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.9.0 install: From 9b5411db57b13ac8900d57905fc9b52b3be0e71c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 13 Dec 2016 17:55:41 +0100 Subject: [PATCH 06/71] :construction_worker: clang 3.9.0 is not working #394 --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fb8f44bd..b87008a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,9 @@ matrix: # CLang sanitizers - os: linux - env: LLVM_VERSION=3.9.0 + env: + - LLVM_VERSION=3.8.1 + - SPECIAL=sanitizers compiler: clang after_success: - make clean @@ -199,7 +201,6 @@ cache: - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.1 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.0 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.1 - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.9.0 install: From 3f089cab2bf21b2cca172f2662ce25e8c8ba2535 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 13 Dec 2016 18:24:16 +0100 Subject: [PATCH 07/71] :construction_worker: moved instructed build to "before_script" #394 --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b87008a4..c07b610a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,16 +41,15 @@ matrix: after_success: - make check TEST_PREFIX="valgrind --error-exitcode=1 --leak-check=full " TEST_PATTERN="" - # CLang sanitizers + # cLang sanitizer - os: linux env: - LLVM_VERSION=3.8.1 - - SPECIAL=sanitizers + - SPECIAL=sanitizer compiler: clang - after_success: - - make clean - - CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" make check + before_script: + - CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" make # cppcheck From c3fac19692fa4946da8b79931d43914c6075f766 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 13 Dec 2016 19:41:36 +0100 Subject: [PATCH 08/71] :construction_worker: another try for sanitizers #394 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c07b610a..c7edd7e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,7 @@ matrix: - SPECIAL=sanitizer compiler: clang before_script: - - CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" make + - make clang_sanitize # cppcheck From 92e28c31530a2a2411fe6711691aa8e2757c79b0 Mon Sep 17 00:00:00 2001 From: Bosswestfalen Date: Tue, 13 Dec 2016 20:30:56 +0100 Subject: [PATCH 09/71] added missing changes in json.hpp.re2c --- src/json.hpp.re2c | 239 ++++++++++------------------------------------ 1 file changed, 51 insertions(+), 188 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 76084140..520d2204 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8204,10 +8204,10 @@ class basic_json public: /*! - @brief a const random access iterator for the @ref basic_json class + @brief a template for a random access iterator for the @ref basic_json class - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. + This class implements a both iterators (iterator and const_iterator) + for the @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the @@ -8222,25 +8222,35 @@ class basic_json @since version 1.0.0 */ - class const_iterator : public std::iterator + template + class iter_impl : public std::iterator { /// allow basic_json to access private members friend class basic_json; + // make sure U is basic_json or const basic_json + static_assert(std::is_same::value + or std::is_same::value, + "iter_impl only accepts (const) basic_json"); + public: /// the type of the values when the iterator is dereferenced using value_type = typename basic_json::value_type; /// a type to represent differences between iterators using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; + using pointer = typename std::conditional::value, + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; + using reference = typename std::conditional::value, + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor - const_iterator() = default; + iter_impl() = default; /*! @brief constructor for a given JSON instance @@ -8248,7 +8258,7 @@ class basic_json @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ - explicit const_iterator(pointer object) noexcept + explicit iter_impl(pointer object) noexcept : m_object(object) { assert(m_object != nullptr); @@ -8275,37 +8285,25 @@ class basic_json } } - /*! - @brief copy constructor given a non-const iterator - @param[in] other iterator to copy from - @note It is not checked whether @a other is initialized. + /* + Use operator const_iterator instead of + const_iterator(const iterator& other) noexcept + to avoid two class definitions for iterator and const_iterator. + + This function is only called if this class is an iterator. + If this class is a const_iterator this function is not called. */ - explicit const_iterator(const iterator& other) noexcept - : m_object(other.m_object) - { - if (m_object != nullptr) - { - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } + operator const_iterator() const + { + const_iterator ret; - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } - } + if (m_object) + { + ret.m_object = m_object; + ret.m_it = m_it; } + + return ret; } /*! @@ -8313,7 +8311,7 @@ class basic_json @param[in] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator(const const_iterator& other) noexcept + iter_impl(const iter_impl& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} @@ -8322,7 +8320,7 @@ class basic_json @param[in,out] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator& operator=(const_iterator other) noexcept( + iter_impl& operator=(iter_impl other) noexcept( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and @@ -8484,7 +8482,7 @@ class basic_json @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator++(int) + iter_impl operator++(int) { auto result = *this; ++(*this); @@ -8495,7 +8493,7 @@ class basic_json @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator++() + iter_impl& operator++() { assert(m_object != nullptr); @@ -8527,7 +8525,7 @@ class basic_json @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator--(int) + iter_impl operator--(int) { auto result = *this; --(*this); @@ -8538,7 +8536,7 @@ class basic_json @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator--() + iter_impl& operator--() { assert(m_object != nullptr); @@ -8570,7 +8568,7 @@ class basic_json @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const const_iterator& other) const + bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -8603,7 +8601,7 @@ class basic_json @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const const_iterator& other) const + bool operator!=(const iter_impl& other) const { return not operator==(other); } @@ -8612,7 +8610,7 @@ class basic_json @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<(const const_iterator& other) const + bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -8645,7 +8643,7 @@ class basic_json @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<=(const const_iterator& other) const + bool operator<=(const iter_impl& other) const { return not other.operator < (*this); } @@ -8654,7 +8652,7 @@ class basic_json @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>(const const_iterator& other) const + bool operator>(const iter_impl& other) const { return not operator<=(other); } @@ -8663,7 +8661,7 @@ class basic_json @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>=(const const_iterator& other) const + bool operator>=(const iter_impl& other) const { return not operator<(other); } @@ -8672,7 +8670,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator+=(difference_type i) + iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); @@ -8703,7 +8701,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator-=(difference_type i) + iter_impl& operator-=(difference_type i) { return operator+=(-i); } @@ -8712,7 +8710,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator+(difference_type i) + iter_impl operator+(difference_type i) { auto result = *this; result += i; @@ -8723,7 +8721,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator-(difference_type i) + iter_impl operator-(difference_type i) { auto result = *this; result -= i; @@ -8734,7 +8732,7 @@ class basic_json @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - difference_type operator-(const const_iterator& other) const + difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); @@ -8830,141 +8828,6 @@ class basic_json internal_iterator m_it = internal_iterator(); }; - /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - explicit iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - return const_cast(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() const - { - return const_cast(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast(base_iterator::value()); - } - }; - /*! @brief a template for a reverse iterator class From dfafd2c259ae65ac135b12f03c4fd5bb32670456 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 13 Dec 2016 21:23:59 +0100 Subject: [PATCH 10/71] :construction_worker: can't get clang sanitizer to work #394 See https://travis-ci.org/nlohmann/json/jobs/183684093 for a failing build. --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7edd7e0..0f9010b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,13 +43,13 @@ matrix: # cLang sanitizer - - os: linux - env: - - LLVM_VERSION=3.8.1 - - SPECIAL=sanitizer - compiler: clang - before_script: - - make clang_sanitize + #- os: linux + # env: + # - LLVM_VERSION=3.8.1 + # - SPECIAL=sanitizer + # compiler: clang + # before_script: + # - make clang_sanitize # cppcheck From 2f94c30baddaa6267961b3d61a86c438c3e9ac2c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 13 Dec 2016 21:46:07 +0100 Subject: [PATCH 11/71] :white_check_mark: added a regression test for #380 / #390 --- README.md | 1 + test/src/unit-regression.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 04bdf192..fa045f7f 100644 --- a/README.md +++ b/README.md @@ -583,6 +583,7 @@ I deeply appreciate the help of the following people. - [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser and improved the benchmarking code. - [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan. - [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. +- [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. Thanks a lot for helping out! diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index f6d05eff..3125c44e 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -533,4 +533,11 @@ TEST_CASE("regression tests") json j3 = json::parse("-9223372036854775809"); CHECK(j3.is_number_float()); } + + SECTION("issue #380 - bug in overflow detection when parsing integers") + { + json j = json::parse("166020696663385964490"); + CHECK(j.is_number_float()); + CHECK(j.dump() == "1.66020696663386e+20"); + } } From f24e4f680ebeff7fdc7a824fa83fff2c504d6638 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 14 Dec 2016 22:30:09 +0100 Subject: [PATCH 12/71] :art: cleanup after PR #395 --- README.md | 1 + src/json.hpp | 35 +++++++++++++++++------------------ src/json.hpp.re2c | 37 +++++++++++++++++++------------------ 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index fa045f7f..644b37f2 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,7 @@ I deeply appreciate the help of the following people. - [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan. - [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. - [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. +- [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. Thanks a lot for helping out! diff --git a/src/json.hpp b/src/json.hpp index aed03fa3..d066bd2c 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -228,6 +228,7 @@ class basic_json public: // forward declarations + template class iter_impl; template class json_reverse_iterator; class json_pointer; @@ -261,8 +262,6 @@ class basic_json /// the type of an element const pointer using const_pointer = typename std::allocator_traits::const_pointer; - // forward declaration for iterators - template class iter_impl; /// an iterator for a basic_json container using iterator = iter_impl; /// a const iterator for a basic_json container @@ -8208,8 +8207,8 @@ class basic_json /*! @brief a template for a random access iterator for the @ref basic_json class - This class implements a both iterators (iterator and const_iterator) - for the @ref basic_json class. + This class implements a both iterators (iterator and const_iterator) for the + @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the @@ -8222,9 +8221,9 @@ class basic_json The iterator that can be moved to point (forward and backward) to any element in constant time. - @since version 1.0.0 + @since version 1.0.0, simplified in version 2.0.9 */ - template + template class iter_impl : public std::iterator { /// allow basic_json to access private members @@ -8242,12 +8241,12 @@ class basic_json using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = typename std::conditional::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) using reference = typename std::conditional::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; @@ -8288,19 +8287,19 @@ class basic_json } /* - Use operator const_iterator instead of - const_iterator(const iterator& other) noexcept - to avoid two class definitions for iterator and const_iterator. + Use operator `const_iterator` instead of `const_iterator(const iterator& + other) noexcept` to avoid two class definitions for @ref iterator and + @ref const_iterator. - This function is only called if this class is an iterator. - If this class is a const_iterator this function is not called. + This function is only called if this class is an @ref iterator. If this + class is a @ref const_iterator this function is not called. */ operator const_iterator() const - { - const_iterator ret; + { + const_iterator ret; if (m_object) - { + { ret.m_object = m_object; ret.m_it = m_it; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 17813c79..ded56b9f 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -228,6 +228,7 @@ class basic_json public: // forward declarations + template class iter_impl; template class json_reverse_iterator; class json_pointer; @@ -262,9 +263,9 @@ class basic_json using const_pointer = typename std::allocator_traits::const_pointer; /// an iterator for a basic_json container - class iterator; + using iterator = iter_impl; /// a const iterator for a basic_json container - class const_iterator; + using const_iterator = iter_impl; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container @@ -8206,8 +8207,8 @@ class basic_json /*! @brief a template for a random access iterator for the @ref basic_json class - This class implements a both iterators (iterator and const_iterator) - for the @ref basic_json class. + This class implements a both iterators (iterator and const_iterator) for the + @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the @@ -8220,9 +8221,9 @@ class basic_json The iterator that can be moved to point (forward and backward) to any element in constant time. - @since version 1.0.0 + @since version 1.0.0, simplified in version 2.0.9 */ - template + template class iter_impl : public std::iterator { /// allow basic_json to access private members @@ -8240,12 +8241,12 @@ class basic_json using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = typename std::conditional::value, - typename basic_json::const_pointer, - typename basic_json::pointer>::type; + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) using reference = typename std::conditional::value, - typename basic_json::const_reference, - typename basic_json::reference>::type; + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; @@ -8286,19 +8287,19 @@ class basic_json } /* - Use operator const_iterator instead of - const_iterator(const iterator& other) noexcept - to avoid two class definitions for iterator and const_iterator. + Use operator `const_iterator` instead of `const_iterator(const iterator& + other) noexcept` to avoid two class definitions for @ref iterator and + @ref const_iterator. - This function is only called if this class is an iterator. - If this class is a const_iterator this function is not called. + This function is only called if this class is an @ref iterator. If this + class is a @ref const_iterator this function is not called. */ operator const_iterator() const - { - const_iterator ret; + { + const_iterator ret; if (m_object) - { + { ret.m_object = m_object; ret.m_it = m_it; } From f6f7fed99a89d99b43ec20e65e08f706a00fa69a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Dec 2016 18:57:19 +0100 Subject: [PATCH 13/71] :rotating_light: removed some warnings --- src/json.hpp | 50 +++++++++++++++++++++++------------------------ src/json.hpp.re2c | 50 +++++++++++++++++++++++------------------------ 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index d066bd2c..d9760cfc 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6982,7 +6982,7 @@ class basic_json case 0xd9: // str 8 { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -6990,7 +6990,7 @@ class basic_json case 0xda: // str 16 { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -6998,7 +6998,7 @@ class basic_json case 0xdb: // str 32 { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7007,7 +7007,7 @@ class basic_json case 0xdc: // array 16 { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { @@ -7019,7 +7019,7 @@ class basic_json case 0xdd: // array 32 { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7031,7 +7031,7 @@ class basic_json case 0xde: // map 16 { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { @@ -7044,7 +7044,7 @@ class basic_json case 0xdf: // map 32 { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7056,7 +7056,7 @@ class basic_json default: { - throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx)); + throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx]))); } } } @@ -7216,7 +7216,7 @@ class basic_json case 0x76: case 0x77: { - const size_t len = v[current_idx] - 0x60; + const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7224,7 +7224,7 @@ class basic_json case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7232,7 +7232,7 @@ class basic_json case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7240,7 +7240,7 @@ class basic_json case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7248,7 +7248,7 @@ class basic_json case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7294,7 +7294,7 @@ class basic_json case 0x97: { basic_json result = value_t::array; - const size_t len = v[current_idx] - 0x80; + const auto len = static_cast(v[current_idx] - 0x80); for (size_t i = 0; i < len; ++i) { result.push_back(from_cbor_internal(v, idx)); @@ -7305,7 +7305,7 @@ class basic_json case 0x98: // array (one-byte uint8_t for n follows) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 1; // skip 1 size byte for (size_t i = 0; i < len; ++i) { @@ -7317,7 +7317,7 @@ class basic_json case 0x99: // array (two-byte uint16_t for n follow) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7329,7 +7329,7 @@ class basic_json case 0x9a: // array (four-byte uint32_t for n follow) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7341,7 +7341,7 @@ class basic_json case 0x9b: // array (eight-byte uint64_t for n follow) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 8; // skip 8 size bytes for (size_t i = 0; i < len; ++i) { @@ -7389,7 +7389,7 @@ class basic_json case 0xb7: { basic_json result = value_t::object; - const size_t len = v[current_idx] - 0xa0; + const auto len = static_cast(v[current_idx] - 0xa0); for (size_t i = 0; i < len; ++i) { std::string key = from_cbor_internal(v, idx); @@ -7401,7 +7401,7 @@ class basic_json case 0xb8: // map (one-byte uint8_t for n follows) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 1; // skip 1 size byte for (size_t i = 0; i < len; ++i) { @@ -7414,7 +7414,7 @@ class basic_json case 0xb9: // map (two-byte uint16_t for n follow) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { @@ -7427,7 +7427,7 @@ class basic_json case 0xba: // map (four-byte uint32_t for n follow) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7440,7 +7440,7 @@ class basic_json case 0xbb: // map (eight-byte uint64_t for n follow) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 8; // skip 8 size bytes for (size_t i = 0; i < len; ++i) { @@ -7534,7 +7534,7 @@ class basic_json default: // anything else (0xFF is handled inside the other types) { - throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(v[current_idx])); + throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx]))); } } } @@ -10516,7 +10516,7 @@ basic_json_parser_66: { // we cannot simply negate value (== max == -INT64_MIN), // see https://github.com/nlohmann/json/issues/389 - result.m_value.number_integer = INT64_MIN; + result.m_value.number_integer = static_cast(INT64_MIN); } else { diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ded56b9f..f2e5d991 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6982,7 +6982,7 @@ class basic_json case 0xd9: // str 8 { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -6990,7 +6990,7 @@ class basic_json case 0xda: // str 16 { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -6998,7 +6998,7 @@ class basic_json case 0xdb: // str 32 { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7007,7 +7007,7 @@ class basic_json case 0xdc: // array 16 { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { @@ -7019,7 +7019,7 @@ class basic_json case 0xdd: // array 32 { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7031,7 +7031,7 @@ class basic_json case 0xde: // map 16 { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { @@ -7044,7 +7044,7 @@ class basic_json case 0xdf: // map 32 { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7056,7 +7056,7 @@ class basic_json default: { - throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx)); + throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx]))); } } } @@ -7216,7 +7216,7 @@ class basic_json case 0x76: case 0x77: { - const size_t len = v[current_idx] - 0x60; + const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7224,7 +7224,7 @@ class basic_json case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7232,7 +7232,7 @@ class basic_json case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7240,7 +7240,7 @@ class basic_json case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7248,7 +7248,7 @@ class basic_json case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) { - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes return std::string(reinterpret_cast(v.data()) + offset, len); @@ -7294,7 +7294,7 @@ class basic_json case 0x97: { basic_json result = value_t::array; - const size_t len = v[current_idx] - 0x80; + const auto len = static_cast(v[current_idx] - 0x80); for (size_t i = 0; i < len; ++i) { result.push_back(from_cbor_internal(v, idx)); @@ -7305,7 +7305,7 @@ class basic_json case 0x98: // array (one-byte uint8_t for n follows) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 1; // skip 1 size byte for (size_t i = 0; i < len; ++i) { @@ -7317,7 +7317,7 @@ class basic_json case 0x99: // array (two-byte uint16_t for n follow) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7329,7 +7329,7 @@ class basic_json case 0x9a: // array (four-byte uint32_t for n follow) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7341,7 +7341,7 @@ class basic_json case 0x9b: // array (eight-byte uint64_t for n follow) { basic_json result = value_t::array; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 8; // skip 8 size bytes for (size_t i = 0; i < len; ++i) { @@ -7389,7 +7389,7 @@ class basic_json case 0xb7: { basic_json result = value_t::object; - const size_t len = v[current_idx] - 0xa0; + const auto len = static_cast(v[current_idx] - 0xa0); for (size_t i = 0; i < len; ++i) { std::string key = from_cbor_internal(v, idx); @@ -7401,7 +7401,7 @@ class basic_json case 0xb8: // map (one-byte uint8_t for n follows) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 1; // skip 1 size byte for (size_t i = 0; i < len; ++i) { @@ -7414,7 +7414,7 @@ class basic_json case 0xb9: // map (two-byte uint16_t for n follow) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 2; // skip 2 size bytes for (size_t i = 0; i < len; ++i) { @@ -7427,7 +7427,7 @@ class basic_json case 0xba: // map (four-byte uint32_t for n follow) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 4; // skip 4 size bytes for (size_t i = 0; i < len; ++i) { @@ -7440,7 +7440,7 @@ class basic_json case 0xbb: // map (eight-byte uint64_t for n follow) { basic_json result = value_t::object; - const auto len = get_from_vector(v, current_idx); + const auto len = static_cast(get_from_vector(v, current_idx)); idx += 8; // skip 8 size bytes for (size_t i = 0; i < len; ++i) { @@ -7534,7 +7534,7 @@ class basic_json default: // anything else (0xFF is handled inside the other types) { - throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(v[current_idx])); + throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx]))); } } } @@ -9666,7 +9666,7 @@ class basic_json { // we cannot simply negate value (== max == -INT64_MIN), // see https://github.com/nlohmann/json/issues/389 - result.m_value.number_integer = INT64_MIN; + result.m_value.number_integer = static_cast(INT64_MIN); } else { From 303e873ae8c4b3a5324eb37558e94644455cc9d3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Dec 2016 20:45:46 +0100 Subject: [PATCH 14/71] :bookmark: bumped version to 2.0.9 --- CMakeLists.txt | 2 +- doc/Doxyfile | 2 +- doc/index.md | 2 +- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- test/src/fuzz.cpp | 2 +- test/src/unit-algorithms.cpp | 2 +- test/src/unit-allocator.cpp | 2 +- test/src/unit-capacity.cpp | 2 +- test/src/unit-cbor.cpp | 2 +- test/src/unit-class_const_iterator.cpp | 2 +- test/src/unit-class_iterator.cpp | 2 +- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 2 +- test/src/unit-comparison.cpp | 2 +- test/src/unit-concepts.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- test/src/unit-constructor2.cpp | 2 +- test/src/unit-convenience.cpp | 2 +- test/src/unit-conversions.cpp | 2 +- test/src/unit-deserialization.cpp | 2 +- test/src/unit-element_access1.cpp | 2 +- test/src/unit-element_access2.cpp | 2 +- test/src/unit-inspection.cpp | 2 +- test/src/unit-iterator_wrapper.cpp | 2 +- test/src/unit-iterators1.cpp | 2 +- test/src/unit-iterators2.cpp | 2 +- test/src/unit-json_patch.cpp | 2 +- test/src/unit-json_pointer.cpp | 2 +- test/src/unit-modifiers.cpp | 2 +- test/src/unit-msgpack.cpp | 2 +- test/src/unit-pointer_access.cpp | 2 +- test/src/unit-readme.cpp | 2 +- test/src/unit-reference_access.cpp | 2 +- test/src/unit-regression.cpp | 2 +- test/src/unit-serialization.cpp | 2 +- test/src/unit-testsuites.cpp | 2 +- test/src/unit-unicode.cpp | 2 +- test/src/unit.cpp | 2 +- 39 files changed, 39 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efda6e92..80770367 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) # define the project -project(nlohmann_json VERSION 2.0.8 LANGUAGES CXX) +project(nlohmann_json VERSION 2.0.9 LANGUAGES CXX) enable_testing() diff --git a/doc/Doxyfile b/doc/Doxyfile index 625f9c11..17c81d8b 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "JSON for Modern C++" -PROJECT_NUMBER = 2.0.8 +PROJECT_NUMBER = 2.0.9 PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = . diff --git a/doc/index.md b/doc/index.md index 0b608eb5..8292fe65 100644 --- a/doc/index.md +++ b/doc/index.md @@ -277,4 +277,4 @@ The container functions known from STL have been extended to support the differe @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code -@version 2.0.8 +@version 2.0.9 diff --git a/src/json.hpp b/src/json.hpp index d9760cfc..23058bee 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index f2e5d991..ac9a3315 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp index 880dc1ca..ef403ea8 100644 --- a/test/src/fuzz.cpp +++ b/test/src/fuzz.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Run "make fuzz_testing" and follow the instructions. diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 5197f803..31be6556 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index f6cecc87..25fd3349 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 97c19cf2..7fc3d49c 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 604d7437..14944cff 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.7 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 2ef62441..2970b865 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 3d82fce4..14c6828b 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 22a04b05..6dec3f82 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index dad6b1ed..7bf23d50 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 582146a1..73c31abc 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index 002ccdfb..dea6b510 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 6804a3a9..23c0db38 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index 1edad6ae..0bbd13cf 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 8791caf2..94341be1 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 1c49a93b..72c3bf60 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 969a7753..605c596b 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index d3b9ed4e..0a1715e7 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index ed1348f8..29be17a1 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index d373c103..1660c719 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index af1fa08e..96928eb4 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index d51c4541..60f7c2fa 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index f231c8ec..da0fc937 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 9a40ce79..880bc46d 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 35558d27..91d003ab 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 08a6dd34..02ffa6a5 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 7498940a..46997e27 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.7 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index 5d5eb163..e1c9caab 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index 07f36363..92b13222 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index a036d2f8..4a8047f8 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 3125c44e..1e720ddb 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 0c800382..8385a4a4 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index c41c4788..92b6e5aa 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 0e49757b..4cb51e21 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit.cpp b/test/src/unit.cpp index 7b93c387..1a278868 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.8 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . From 706be50596984e956f00f54655dcddc49d9acff4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Dec 2016 20:54:43 +0100 Subject: [PATCH 15/71] :memo: updated number of tests --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 644b37f2..97fb41da 100644 --- a/README.md +++ b/README.md @@ -608,7 +608,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (11201886 assertions in 43 test cases) +All tests passed (11201893 assertions in 43 test cases) ``` Alternatively, you can use [CMake](https://cmake.org) and run From 36dc7861069842fd742882864eb4ff7a099bfc12 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Dec 2016 21:09:14 +0100 Subject: [PATCH 16/71] :bookmark: updated Changelog --- ChangeLog.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 287420c6..92545768 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,29 @@ # Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [v2.0.9](https://github.com/nlohmann/json/releases/tag/v2.0.9) (2016-12-16) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.8...v2.0.9) + +- \#pragma GCC diagnostic ignored "-Wdocumentation" [\#393](https://github.com/nlohmann/json/issues/393) +- How to parse this json file and write separate sub object as json files? [\#392](https://github.com/nlohmann/json/issues/392) +- Integer-overflow \(OSS-Fuzz issue 267\) [\#389](https://github.com/nlohmann/json/issues/389) +- Implement indefinite-length types from RFC 7049 [\#387](https://github.com/nlohmann/json/issues/387) +- template parameter "T" is not used in declaring the parameter types of function template [\#386](https://github.com/nlohmann/json/issues/386) +- Serializing json instances containing already serialized string values without escaping [\#385](https://github.com/nlohmann/json/issues/385) +- Add test cases from RFC 7049 [\#384](https://github.com/nlohmann/json/issues/384) +- Add a table of contents to the README file [\#383](https://github.com/nlohmann/json/issues/383) +- Update FAQ section in the guidelines for contributing [\#382](https://github.com/nlohmann/json/issues/382) +- Allow for forward declaring nlohmann::json [\#381](https://github.com/nlohmann/json/issues/381) +- Bug in overflow detection when parsing integers [\#380](https://github.com/nlohmann/json/issues/380) +- A unique name to mention the library? [\#377](https://github.com/nlohmann/json/issues/377) +- Support for comments. [\#376](https://github.com/nlohmann/json/issues/376) +- Non-unique keys in objects. [\#375](https://github.com/nlohmann/json/issues/375) +- Request: binary serialization/deserialization [\#358](https://github.com/nlohmann/json/issues/358) + +- Replace class iterator and const\_iterator by using a single template class to reduce code. [\#395](https://github.com/nlohmann/json/pull/395) ([Bosswestfalen](https://github.com/Bosswestfalen)) +- Clang: quiet a warning [\#391](https://github.com/nlohmann/json/pull/391) ([jaredgrubb](https://github.com/jaredgrubb)) +- Fix issue \#380: Signed integer overflow check [\#390](https://github.com/nlohmann/json/pull/390) ([qwename](https://github.com/qwename)) + ## [v2.0.8](https://github.com/nlohmann/json/releases/tag/v2.0.8) (2016-12-02) [Full Changelog](https://github.com/nlohmann/json/compare/v2.0.7...v2.0.8) @@ -8,7 +31,6 @@ All notable changes to this project will be documented in this file. This projec - Compiler warnings? [\#372](https://github.com/nlohmann/json/issues/372) - docs: how to release a json object in memory? [\#371](https://github.com/nlohmann/json/issues/371) - crash in dump [\#370](https://github.com/nlohmann/json/issues/370) -- Conversion operators not considered [\#369](https://github.com/nlohmann/json/issues/369) - Coverity issue \(FORWARD\_NULL\) in lexer\(std::istream& s\) [\#368](https://github.com/nlohmann/json/issues/368) - json::parse on failed stream gets stuck [\#366](https://github.com/nlohmann/json/issues/366) - Performance improvements [\#365](https://github.com/nlohmann/json/issues/365) From a507b9b46a35542aeb693ad87d947e7426e0ab98 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Dec 2016 21:25:20 +0100 Subject: [PATCH 17/71] :bookmark: updated documentation --- README.md | 2 +- doc/examples/README.link | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 97fb41da..97ae6854 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/fsf5FqYe6GoX68W6) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) diff --git a/doc/examples/README.link b/doc/examples/README.link index 48a66aa0..128ab1dc 100644 --- a/doc/examples/README.link +++ b/doc/examples/README.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file From 57afd293aa9d26f5760415b45a6ed82b8bd60a2c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 16 Dec 2016 21:34:37 +0100 Subject: [PATCH 18/71] :bookmark: updated documentation --- doc/json.gif | Bin 1218691 -> 1325973 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/json.gif b/doc/json.gif index a5f118785512478a3ae129f355e934dd2a6ab758..7d3bf81645496c9a72e4f187e21a3ce2ba9223ef 100644 GIT binary patch delta 269401 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⁢{Q;*1=f5>62BjIrXa9 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%0Ai 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%0Dx^YVxBFClGx$?-;Xw=`H* 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℘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 u@zc;J)7<~2`3zy^I#<7W^Nj|6U6K From 9952a3c456e4af1bffb4e5c85968fdf32a122e08 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 18 Dec 2016 18:17:26 +0100 Subject: [PATCH 19/71] :construction_worker: trying to fix coverity task --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f9010b7..006647ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,9 +98,6 @@ matrix: compiler: clang before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['valgrind'] coverity_scan: project: name: "nlohmann/json" From 0fffbb8488bb88a938e2303fbab7874cbbb797fd Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 20 Dec 2016 19:06:15 +0100 Subject: [PATCH 20/71] :construction_worker: trying Doozer --- .doozer.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .doozer.json diff --git a/.doozer.json b/.doozer.json new file mode 100644 index 00000000..67012d8d --- /dev/null +++ b/.doozer.json @@ -0,0 +1,9 @@ +{ + "targets": { + "xenial-amd64": { + "buildenv": "xenial-amd64", + "builddeps": ["build-essential"], + "buildcmd": ["make check"] + } + } +} From 1178a8152a7bab880c466d1d6be411ce66f59e90 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 20 Dec 2016 19:13:19 +0100 Subject: [PATCH 21/71] :construction_worker: using cmake with Doozer --- .doozer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.doozer.json b/.doozer.json index 67012d8d..97f52598 100644 --- a/.doozer.json +++ b/.doozer.json @@ -2,8 +2,8 @@ "targets": { "xenial-amd64": { "buildenv": "xenial-amd64", - "builddeps": ["build-essential"], - "buildcmd": ["make check"] + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm ; cd cm ; cmake .. ; cbuild . ; ctest"] } } } From 73b28e8f922e539a6317b887d4ece8a3add05638 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 20 Dec 2016 19:16:52 +0100 Subject: [PATCH 22/71] :construction_worker: fixing build command --- .doozer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.doozer.json b/.doozer.json index 97f52598..89386481 100644 --- a/.doozer.json +++ b/.doozer.json @@ -3,7 +3,7 @@ "xenial-amd64": { "buildenv": "xenial-amd64", "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm ; cd cm ; cmake .. ; cbuild . ; ctest"] + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] } } } From 57b32d97736d204e303be26f44ea6159c3041ad9 Mon Sep 17 00:00:00 2001 From: Andreas Smas Date: Tue, 20 Dec 2016 21:38:01 +0100 Subject: [PATCH 23/71] Add Doozer build badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 97ae6854..16c7eabf 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) +[![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK) From 38597ac62826d1acb8fc557e4127f3c62f8f4109 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 20 Dec 2016 23:04:07 +0100 Subject: [PATCH 24/71] :construction_worker: added more builders --- .doozer.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.doozer.json b/.doozer.json index 89386481..eaea3b02 100644 --- a/.doozer.json +++ b/.doozer.json @@ -4,6 +4,16 @@ "buildenv": "xenial-amd64", "builddeps": ["build-essential", "cmake"], "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] + }, + "xenial-i386": { + "buildenv": "xenial-i386", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] + }, + "osx": { + "buildenv": "osx", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] } } } From c6fbd305b04866632c8a2c5cf501a77f94efc57d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 09:17:17 +0100 Subject: [PATCH 25/71] :construction_worker: trying more doozer workers --- .doozer.json | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/.doozer.json b/.doozer.json index eaea3b02..3781b86d 100644 --- a/.doozer.json +++ b/.doozer.json @@ -1,19 +1,54 @@ { "targets": { - "xenial-amd64": { - "buildenv": "xenial-amd64", + "jessie-i386": { + "buildenv": "jessie-i386", "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "precise-i386": { + "buildenv": "precise-i386", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "precise-amd64": { + "buildenv": "precise-amd64", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "trusty-i386": { + "buildenv": "trusty-i386", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "trusty-amd64": { + "buildenv": "trusty-amd64", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] }, "xenial-i386": { "buildenv": "xenial-i386", "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "xenial-amd64": { + "buildenv": "xenial-amd64", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "fedora24-x86_64": { + "buildenv": "fedora24-x86_64", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + }, + "centos7-x86_64": { + "buildenv": "centos7-x86_64", + "builddeps": ["build-essential", "cmake"], + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] }, "osx": { "buildenv": "osx", "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest"] + "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] } } } From 2e5208d5cdf735bb7f2588992a2ceb0a63abecdd Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 09:19:53 +0100 Subject: [PATCH 26/71] :construction_worker: there is no cmake in the osx image --- .doozer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.doozer.json b/.doozer.json index 3781b86d..072270d5 100644 --- a/.doozer.json +++ b/.doozer.json @@ -47,8 +47,8 @@ }, "osx": { "buildenv": "osx", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + "builddeps": ["build-essential"], + "buildcmd": ["make check"] } } } From f78671de8e8fe94e88d674c78428a71e72d89d89 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 09:33:16 +0100 Subject: [PATCH 27/71] :construction_worker: Fedora has no package "build-essential" --- .doozer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.doozer.json b/.doozer.json index 072270d5..fad1a6a0 100644 --- a/.doozer.json +++ b/.doozer.json @@ -37,7 +37,7 @@ }, "fedora24-x86_64": { "buildenv": "fedora24-x86_64", - "builddeps": ["build-essential", "cmake"], + "builddeps": ["cmake"], "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] }, "centos7-x86_64": { From 91e45a8dd0b560ac01d3ad14c0b45bbfbebea094 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 09:47:31 +0100 Subject: [PATCH 28/71] :construction_worker: the cmake of trusty is too old --- .doozer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.doozer.json b/.doozer.json index fad1a6a0..808dbf21 100644 --- a/.doozer.json +++ b/.doozer.json @@ -18,12 +18,12 @@ "trusty-i386": { "buildenv": "trusty-i386", "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + "buildcmd": ["make check"] }, "trusty-amd64": { "buildenv": "trusty-amd64", "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + "buildcmd": ["make check"] }, "xenial-i386": { "buildenv": "xenial-i386", From 937cca762fe0e5080a6851ed922deb8d4dd7a0ff Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 09:54:11 +0100 Subject: [PATCH 29/71] :construction_worker: forgot to install make --- .doozer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.doozer.json b/.doozer.json index 808dbf21..2e1f73d5 100644 --- a/.doozer.json +++ b/.doozer.json @@ -37,7 +37,7 @@ }, "fedora24-x86_64": { "buildenv": "fedora24-x86_64", - "builddeps": ["cmake"], + "builddeps": ["cmake", "make"], "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] }, "centos7-x86_64": { From 2cf0f299ade6c778d7c38c06d2fe2559d75ba683 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 10:13:59 +0100 Subject: [PATCH 30/71] :construction_worker: using clang where GCC is too old --- .doozer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.doozer.json b/.doozer.json index 2e1f73d5..67aaa8d7 100644 --- a/.doozer.json +++ b/.doozer.json @@ -2,8 +2,8 @@ "targets": { "jessie-i386": { "buildenv": "jessie-i386", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + "builddeps": ["build-essential", "cmake", "clang"], + "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"] }, "precise-i386": { "buildenv": "precise-i386", @@ -17,13 +17,13 @@ }, "trusty-i386": { "buildenv": "trusty-i386", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["make check"] + "builddeps": ["build-essential", "cmake", "clang"], + "buildcmd": ["make check CXX=clang++"] }, "trusty-amd64": { "buildenv": "trusty-amd64", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["make check"] + "builddeps": ["build-essential", "cmake", "clang"], + "buildcmd": ["make check CXX=clang++"] }, "xenial-i386": { "buildenv": "xenial-i386", From 7107072f71e1e80362a1fe4e6812e381efade7d5 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 21 Dec 2016 10:33:36 +0100 Subject: [PATCH 31/71] :construction_worker: clang for fedora --- .doozer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.doozer.json b/.doozer.json index 67aaa8d7..589dccc3 100644 --- a/.doozer.json +++ b/.doozer.json @@ -37,8 +37,8 @@ }, "fedora24-x86_64": { "buildenv": "fedora24-x86_64", - "builddeps": ["cmake", "make"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] + "builddeps": ["cmake", "make", "clang"], + "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"] }, "centos7-x86_64": { "buildenv": "centos7-x86_64", From 69ed19e483681c2a64944f3b9b9351326872d350 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:08:50 +0100 Subject: [PATCH 32/71] :heavy_plus_sign: added libFuzzer --- .gitignore | 1 + test/fuzz_test/Fuzzer/CMakeLists.txt | 45 + test/fuzz_test/Fuzzer/FuzzerCorpus.h | 217 +++++ test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp | 52 ++ test/fuzz_test/Fuzzer/FuzzerDefs.h | 89 ++ test/fuzz_test/Fuzzer/FuzzerDictionary.h | 124 +++ test/fuzz_test/Fuzzer/FuzzerDriver.cpp | 545 ++++++++++++ test/fuzz_test/Fuzzer/FuzzerExtFunctions.def | 50 ++ test/fuzz_test/Fuzzer/FuzzerExtFunctions.h | 35 + .../Fuzzer/FuzzerExtFunctionsDlsym.cpp | 52 ++ .../Fuzzer/FuzzerExtFunctionsWeak.cpp | 53 ++ .../Fuzzer/FuzzerExtFunctionsWeakAlias.cpp | 56 ++ test/fuzz_test/Fuzzer/FuzzerFlags.def | 115 +++ test/fuzz_test/Fuzzer/FuzzerIO.cpp | 117 +++ test/fuzz_test/Fuzzer/FuzzerIO.h | 64 ++ test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp | 88 ++ test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp | 282 +++++++ test/fuzz_test/Fuzzer/FuzzerInterface.h | 67 ++ test/fuzz_test/Fuzzer/FuzzerInternal.h | 182 ++++ test/fuzz_test/Fuzzer/FuzzerLoop.cpp | 792 ++++++++++++++++++ test/fuzz_test/Fuzzer/FuzzerMain.cpp | 21 + test/fuzz_test/Fuzzer/FuzzerMerge.cpp | 261 ++++++ test/fuzz_test/Fuzzer/FuzzerMerge.h | 70 ++ test/fuzz_test/Fuzzer/FuzzerMutate.cpp | 527 ++++++++++++ test/fuzz_test/Fuzzer/FuzzerMutate.h | 145 ++++ test/fuzz_test/Fuzzer/FuzzerOptions.h | 68 ++ test/fuzz_test/Fuzzer/FuzzerRandom.h | 36 + test/fuzz_test/Fuzzer/FuzzerSHA1.cpp | 222 +++++ test/fuzz_test/Fuzzer/FuzzerSHA1.h | 33 + test/fuzz_test/Fuzzer/FuzzerTracePC.cpp | 339 ++++++++ test/fuzz_test/Fuzzer/FuzzerTracePC.h | 158 ++++ test/fuzz_test/Fuzzer/FuzzerTraceState.cpp | 325 +++++++ test/fuzz_test/Fuzzer/FuzzerUtil.cpp | 218 +++++ test/fuzz_test/Fuzzer/FuzzerUtil.h | 72 ++ test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp | 152 ++++ test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp | 24 + test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp | 117 +++ test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp | 182 ++++ test/fuzz_test/Fuzzer/FuzzerValueBitMap.h | 87 ++ test/fuzz_test/Fuzzer/README.txt | 2 + test/fuzz_test/Fuzzer/afl/afl_driver.cpp | 295 +++++++ test/fuzz_test/Fuzzer/build.sh | 10 + test/fuzz_test/Fuzzer/cxx.dict | 122 +++ .../standalone/StandaloneFuzzTargetMain.c | 41 + test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp | 22 + .../Fuzzer/test/AbsNegAndConstant64Test.cpp | 23 + .../Fuzzer/test/AbsNegAndConstantTest.cpp | 23 + .../Fuzzer/test/AccumulateAllocationsTest.cpp | 17 + .../Fuzzer/test/BufferOverflowOnInput.cpp | 23 + test/fuzz_test/Fuzzer/test/CMakeLists.txt | 217 +++++ .../Fuzzer/test/CallerCalleeTest.cpp | 59 ++ test/fuzz_test/Fuzzer/test/CounterTest.cpp | 18 + .../Fuzzer/test/CustomCrossOverTest.cpp | 63 ++ .../Fuzzer/test/CustomMutatorTest.cpp | 38 + test/fuzz_test/Fuzzer/test/DSO1.cpp | 12 + test/fuzz_test/Fuzzer/test/DSO2.cpp | 12 + test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp | 11 + test/fuzz_test/Fuzzer/test/DSOTestMain.cpp | 31 + test/fuzz_test/Fuzzer/test/DivTest.cpp | 20 + test/fuzz_test/Fuzzer/test/EmptyTest.cpp | 11 + .../test/FourIndependentBranchesTest.cpp | 22 + .../Fuzzer/test/FullCoverageSetTest.cpp | 24 + test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp | 738 ++++++++++++++++ test/fuzz_test/Fuzzer/test/InitializeTest.cpp | 28 + test/fuzz_test/Fuzzer/test/LeakTest.cpp | 17 + .../fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp | 17 + test/fuzz_test/Fuzzer/test/LoadTest.cpp | 22 + test/fuzz_test/Fuzzer/test/MemcmpTest.cpp | 31 + .../fuzz_test/Fuzzer/test/NthRunCrashTest.cpp | 18 + .../Fuzzer/test/NullDerefOnEmptyTest.cpp | 19 + test/fuzz_test/Fuzzer/test/NullDerefTest.cpp | 26 + .../Fuzzer/test/OneHugeAllocTest.cpp | 28 + .../test/OutOfMemorySingleLargeMallocTest.cpp | 27 + .../fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp | 31 + .../Fuzzer/test/RepeatedBytesTest.cpp | 29 + test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp | 22 + .../Fuzzer/test/ShrinkControlFlowTest.cpp | 28 + .../Fuzzer/test/ShrinkValueProfileTest.cpp | 22 + .../Fuzzer/test/SignedIntOverflowTest.cpp | 28 + test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp | 46 + .../Fuzzer/test/SimpleDictionaryTest.cpp | 29 + test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp | 40 + test/fuzz_test/Fuzzer/test/SimpleTest.cpp | 27 + .../Fuzzer/test/SimpleThreadedTest.cpp | 25 + .../Fuzzer/test/SingleMemcmpTest.cpp | 17 + .../Fuzzer/test/SingleStrcmpTest.cpp | 17 + .../Fuzzer/test/SingleStrncmpTest.cpp | 17 + test/fuzz_test/Fuzzer/test/SpamyTest.cpp | 21 + test/fuzz_test/Fuzzer/test/StrcmpTest.cpp | 32 + test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp | 21 + test/fuzz_test/Fuzzer/test/StrncmpTest.cpp | 28 + test/fuzz_test/Fuzzer/test/StrstrTest.cpp | 28 + test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp | 34 + test/fuzz_test/Fuzzer/test/Switch2Test.cpp | 35 + test/fuzz_test/Fuzzer/test/SwitchTest.cpp | 58 ++ .../Fuzzer/test/ThreadedLeakTest.cpp | 18 + test/fuzz_test/Fuzzer/test/ThreadedTest.cpp | 26 + .../Fuzzer/test/TimeoutEmptyTest.cpp | 14 + test/fuzz_test/Fuzzer/test/TimeoutTest.cpp | 26 + .../fuzz_test/Fuzzer/test/TraceMallocTest.cpp | 27 + .../Fuzzer/test/UninstrumentedTest.cpp | 11 + .../Fuzzer/test/afl-driver-extra-stats.test | 28 + .../Fuzzer/test/afl-driver-stderr.test | 10 + test/fuzz_test/Fuzzer/test/caller-callee.test | 2 + test/fuzz_test/Fuzzer/test/coverage.test | 19 + test/fuzz_test/Fuzzer/test/dict1.txt | 4 + test/fuzz_test/Fuzzer/test/dump_coverage.test | 16 + .../Fuzzer/test/fuzzer-customcrossover.test | 10 + .../Fuzzer/test/fuzzer-custommutator.test | 4 + test/fuzz_test/Fuzzer/test/fuzzer-dict.test | 6 + test/fuzz_test/Fuzzer/test/fuzzer-dirs.test | 15 + test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test | 30 + .../Fuzzer/test/fuzzer-finalstats.test | 11 + test/fuzz_test/Fuzzer/test/fuzzer-flags.test | 10 + test/fuzz_test/Fuzzer/test/fuzzer-jobs.test | 29 + test/fuzz_test/Fuzzer/test/fuzzer-leak.test | 35 + .../Fuzzer/test/fuzzer-oom-with-profile.test | 6 + test/fuzz_test/Fuzzer/test/fuzzer-oom.test | 11 + .../Fuzzer/test/fuzzer-printcovpcs.test | 8 + test/fuzz_test/Fuzzer/test/fuzzer-runs.test | 8 + test/fuzz_test/Fuzzer/test/fuzzer-seed.test | 3 + test/fuzz_test/Fuzzer/test/fuzzer-segv.test | 5 + .../Fuzzer/test/fuzzer-singleinputs.test | 16 + .../Fuzzer/test/fuzzer-threaded.test | 7 + .../fuzz_test/Fuzzer/test/fuzzer-timeout.test | 19 + .../Fuzzer/test/fuzzer-traces-hooks.test | 25 + test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test | 4 + test/fuzz_test/Fuzzer/test/fuzzer.test | 57 ++ test/fuzz_test/Fuzzer/test/hi.txt | 1 + test/fuzz_test/Fuzzer/test/lit.cfg | 29 + test/fuzz_test/Fuzzer/test/lit.site.cfg.in | 4 + test/fuzz_test/Fuzzer/test/merge.test | 46 + .../fuzz_test/Fuzzer/test/minimize_crash.test | 6 + .../Fuzzer/test/no-coverage/CMakeLists.txt | 29 + .../fuzz_test/Fuzzer/test/repeated-bytes.test | 2 + test/fuzz_test/Fuzzer/test/shrink.test | 7 + test/fuzz_test/Fuzzer/test/simple-cmp.test | 2 + test/fuzz_test/Fuzzer/test/standalone.test | 4 + test/fuzz_test/Fuzzer/test/swap-cmp.test | 2 + test/fuzz_test/Fuzzer/test/trace-malloc.test | 10 + .../Fuzzer/test/ubsan/CMakeLists.txt | 15 + test/fuzz_test/Fuzzer/test/ulimit.test | 2 + .../Fuzzer/test/uninstrumented/CMakeLists.txt | 16 + test/fuzz_test/Fuzzer/test/unit/lit.cfg | 7 + .../Fuzzer/test/unit/lit.site.cfg.in | 2 + .../Fuzzer/test/value-profile-cmp.test | 2 + .../Fuzzer/test/value-profile-cmp2.test | 2 + .../Fuzzer/test/value-profile-cmp3.test | 2 + .../Fuzzer/test/value-profile-cmp4.test | 2 + .../Fuzzer/test/value-profile-div.test | 3 + .../Fuzzer/test/value-profile-load.test | 3 + .../Fuzzer/test/value-profile-mem.test | 2 + .../Fuzzer/test/value-profile-set.test | 3 + .../Fuzzer/test/value-profile-strcmp.test | 2 + .../Fuzzer/test/value-profile-strncmp.test | 2 + .../Fuzzer/test/value-profile-switch.test | 3 + 156 files changed, 9535 insertions(+) create mode 100644 test/fuzz_test/Fuzzer/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/FuzzerCorpus.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerDefs.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerDictionary.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerDriver.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctions.def create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctions.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerFlags.def create mode 100644 test/fuzz_test/Fuzzer/FuzzerIO.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerIO.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerInterface.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerInternal.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerLoop.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMain.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMerge.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMerge.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerMutate.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerMutate.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerOptions.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerRandom.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerSHA1.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerSHA1.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerTracePC.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerTracePC.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerTraceState.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtil.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtil.h create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp create mode 100644 test/fuzz_test/Fuzzer/FuzzerValueBitMap.h create mode 100644 test/fuzz_test/Fuzzer/README.txt create mode 100644 test/fuzz_test/Fuzzer/afl/afl_driver.cpp create mode 100755 test/fuzz_test/Fuzzer/build.sh create mode 100644 test/fuzz_test/Fuzzer/cxx.dict create mode 100644 test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c create mode 100644 test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp create mode 100644 test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CounterTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSO1.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSO2.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DSOTestMain.cpp create mode 100644 test/fuzz_test/Fuzzer/test/DivTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/EmptyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/InitializeTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/LeakTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/LoadTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/MemcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/NullDerefTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SpamyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrcmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrncmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/StrstrTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/Switch2Test.cpp create mode 100644 test/fuzz_test/Fuzzer/test/SwitchTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/ThreadedTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/TimeoutTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp create mode 100644 test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test create mode 100644 test/fuzz_test/Fuzzer/test/afl-driver-stderr.test create mode 100644 test/fuzz_test/Fuzzer/test/caller-callee.test create mode 100644 test/fuzz_test/Fuzzer/test/coverage.test create mode 100644 test/fuzz_test/Fuzzer/test/dict1.txt create mode 100644 test/fuzz_test/Fuzzer/test/dump_coverage.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-dict.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-dirs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-flags.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-jobs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-leak.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-oom.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-runs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-seed.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-segv.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-threaded.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-timeout.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test create mode 100644 test/fuzz_test/Fuzzer/test/fuzzer.test create mode 100644 test/fuzz_test/Fuzzer/test/hi.txt create mode 100644 test/fuzz_test/Fuzzer/test/lit.cfg create mode 100644 test/fuzz_test/Fuzzer/test/lit.site.cfg.in create mode 100644 test/fuzz_test/Fuzzer/test/merge.test create mode 100644 test/fuzz_test/Fuzzer/test/minimize_crash.test create mode 100644 test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/repeated-bytes.test create mode 100644 test/fuzz_test/Fuzzer/test/shrink.test create mode 100644 test/fuzz_test/Fuzzer/test/simple-cmp.test create mode 100644 test/fuzz_test/Fuzzer/test/standalone.test create mode 100644 test/fuzz_test/Fuzzer/test/swap-cmp.test create mode 100644 test/fuzz_test/Fuzzer/test/trace-malloc.test create mode 100644 test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/ulimit.test create mode 100644 test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt create mode 100644 test/fuzz_test/Fuzzer/test/unit/lit.cfg create mode 100644 test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp2.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp3.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-cmp4.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-div.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-load.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-mem.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-set.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-strcmp.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-strncmp.test create mode 100644 test/fuzz_test/Fuzzer/test/value-profile-switch.test diff --git a/.gitignore b/.gitignore index 42a18e82..099cad5c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ cmake-build-debug test/test-* +test/fuzz_test/Fuzzer/.svn diff --git a/test/fuzz_test/Fuzzer/CMakeLists.txt b/test/fuzz_test/Fuzzer/CMakeLists.txt new file mode 100644 index 00000000..70bd017b --- /dev/null +++ b/test/fuzz_test/Fuzzer/CMakeLists.txt @@ -0,0 +1,45 @@ +set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}") +# Disable the coverage and sanitizer instrumentation for the fuzzer itself. +set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror") +if( LLVM_USE_SANITIZE_COVERAGE ) + if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address") + message(FATAL_ERROR + "LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and " + "LLVM_USE_SANITIZE_COVERAGE=YES to be set." + ) + endif() + add_library(LLVMFuzzerNoMainObjects OBJECT + FuzzerCrossOver.cpp + FuzzerDriver.cpp + FuzzerExtFunctionsDlsym.cpp + FuzzerExtFunctionsWeak.cpp + FuzzerExtFunctionsWeakAlias.cpp + FuzzerIO.cpp + FuzzerIOPosix.cpp + FuzzerIOWindows.cpp + FuzzerLoop.cpp + FuzzerMerge.cpp + FuzzerMutate.cpp + FuzzerSHA1.cpp + FuzzerTracePC.cpp + FuzzerTraceState.cpp + FuzzerUtil.cpp + FuzzerUtilDarwin.cpp + FuzzerUtilLinux.cpp + FuzzerUtilPosix.cpp + FuzzerUtilWindows.cpp + ) + add_library(LLVMFuzzerNoMain STATIC + $ + ) + target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB}) + add_library(LLVMFuzzer STATIC + FuzzerMain.cpp + $ + ) + target_link_libraries(LLVMFuzzer ${PTHREAD_LIB}) + + if( LLVM_INCLUDE_TESTS ) + add_subdirectory(test) + endif() +endif() diff --git a/test/fuzz_test/Fuzzer/FuzzerCorpus.h b/test/fuzz_test/Fuzzer/FuzzerCorpus.h new file mode 100644 index 00000000..663c5854 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerCorpus.h @@ -0,0 +1,217 @@ +//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::InputCorpus +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_CORPUS +#define LLVM_FUZZER_CORPUS + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include +#include +#include + +namespace fuzzer { + +struct InputInfo { + Unit U; // The actual input data. + uint8_t Sha1[kSHA1NumBytes]; // Checksum. + // Number of features that this input has and no smaller input has. + size_t NumFeatures = 0; + size_t Tmp = 0; // Used by ValidateFeatureSet. + // Stats. + size_t NumExecutedMutations = 0; + size_t NumSuccessfullMutations = 0; + bool MayDeleteFile = false; +}; + +class InputCorpus { + public: + static const size_t kFeatureSetSize = 1 << 16; + InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) { + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + ~InputCorpus() { + for (auto II : Inputs) + delete II; + } + size_t size() const { return Inputs.size(); } + size_t SizeInBytes() const { + size_t Res = 0; + for (auto II : Inputs) + Res += II->U.size(); + return Res; + } + size_t NumActiveUnits() const { + size_t Res = 0; + for (auto II : Inputs) + Res += !II->U.empty(); + return Res; + } + bool empty() const { return Inputs.empty(); } + const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } + void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile = false) { + assert(!U.empty()); + uint8_t Hash[kSHA1NumBytes]; + if (FeatureDebug) + Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + ComputeSHA1(U.data(), U.size(), Hash); + Hashes.insert(Sha1ToString(Hash)); + Inputs.push_back(new InputInfo()); + InputInfo &II = *Inputs.back(); + II.U = U; + II.NumFeatures = NumFeatures; + II.MayDeleteFile = MayDeleteFile; + memcpy(II.Sha1, Hash, kSHA1NumBytes); + UpdateCorpusDistribution(); + ValidateFeatureSet(); + } + + bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } + bool HasUnit(const std::string &H) { return Hashes.count(H); } + InputInfo &ChooseUnitToMutate(Random &Rand) { + InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; + assert(!II.U.empty()); + return II; + }; + + // Returns an index of random unit from the corpus to mutate. + // Hypothesis: units added to the corpus last are more likely to be + // interesting. This function gives more weight to the more recent units. + size_t ChooseUnitIdxToMutate(Random &Rand) { + size_t Idx = static_cast(CorpusDistribution(Rand.Get_mt19937())); + assert(Idx < Inputs.size()); + return Idx; + } + + void PrintStats() { + for (size_t i = 0; i < Inputs.size(); i++) { + const auto &II = *Inputs[i]; + Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i, + Sha1ToString(II.Sha1).c_str(), II.U.size(), + II.NumExecutedMutations, II.NumSuccessfullMutations); + } + } + + void PrintFeatureSet() { + for (size_t i = 0; i < kFeatureSetSize; i++) { + if(size_t Sz = GetFeature(i)) + Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + } + Printf("\n\t"); + for (size_t i = 0; i < Inputs.size(); i++) + if (size_t N = Inputs[i]->NumFeatures) + Printf(" %zd=>%zd ", i, N); + Printf("\n"); + } + + void DeleteInput(size_t Idx) { + InputInfo &II = *Inputs[Idx]; + if (!OutputCorpus.empty() && II.MayDeleteFile) + RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1))); + Unit().swap(II.U); + if (FeatureDebug) + Printf("EVICTED %zd\n", Idx); + } + + bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || (Shrink && OldSize > NewSize)) { + if (OldSize > 0) { + size_t OldIdx = SmallestElementPerFeature[Idx]; + InputInfo &II = *Inputs[OldIdx]; + assert(II.NumFeatures > 0); + II.NumFeatures--; + if (II.NumFeatures == 0) + DeleteInput(OldIdx); + } + if (FeatureDebug) + Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); + SmallestElementPerFeature[Idx] = Inputs.size(); + InputSizesPerFeature[Idx] = NewSize; + CountingFeatures = true; + return true; + } + return false; + } + + size_t NumFeatures() const { + size_t Res = 0; + for (size_t i = 0; i < kFeatureSetSize; i++) + Res += GetFeature(i) != 0; + return Res; + } + + void ResetFeatureSet() { + assert(Inputs.empty()); + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + +private: + + static const bool FeatureDebug = false; + + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + + void ValidateFeatureSet() { + if (!CountingFeatures) return; + if (FeatureDebug) + PrintFeatureSet(); + for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) + if (GetFeature(Idx)) + Inputs[SmallestElementPerFeature[Idx]]->Tmp++; + for (auto II: Inputs) { + if (II->Tmp != II->NumFeatures) + Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures); + assert(II->Tmp == II->NumFeatures); + II->Tmp = 0; + } + } + + // Updates the probability distribution for the units in the corpus. + // Must be called whenever the corpus or unit weights are changed. + void UpdateCorpusDistribution() { + size_t N = Inputs.size(); + Intervals.resize(N + 1); + Weights.resize(N); + std::iota(Intervals.begin(), Intervals.end(), 0); + if (CountingFeatures) + for (size_t i = 0; i < N; i++) + Weights[i] = Inputs[i]->NumFeatures * (i + 1); + else + std::iota(Weights.begin(), Weights.end(), 1); + CorpusDistribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + } + std::piecewise_constant_distribution CorpusDistribution; + + std::vector Intervals; + std::vector Weights; + + std::unordered_set Hashes; + std::vector Inputs; + + bool CountingFeatures = false; + uint32_t InputSizesPerFeature[kFeatureSetSize]; + uint32_t SmallestElementPerFeature[kFeatureSetSize]; + + std::string OutputCorpus; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_CORPUS diff --git a/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp b/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp new file mode 100644 index 00000000..8b0fd7d5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,52 @@ +//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Cross over test inputs. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include + +namespace fuzzer { + +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + assert(Size1 || Size2); + MaxOutSize = Rand(MaxOutSize) + 1; + size_t OutPos = 0; + size_t Pos1 = 0; + size_t Pos2 = 0; + size_t *InPos = &Pos1; + size_t InSize = Size1; + const uint8_t *Data = Data1; + bool CurrentlyUsingFirstData = true; + while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) { + // Merge a part of Data into Out. + size_t OutSizeLeft = MaxOutSize - OutPos; + if (*InPos < InSize) { + size_t InSizeLeft = InSize - *InPos; + size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft); + size_t ExtraSize = Rand(MaxExtraSize) + 1; + memcpy(Out + OutPos, Data + *InPos, ExtraSize); + OutPos += ExtraSize; + (*InPos) += ExtraSize; + } + // Use the other input data on the next iteration. + InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1; + InSize = CurrentlyUsingFirstData ? Size2 : Size1; + Data = CurrentlyUsingFirstData ? Data2 : Data1; + CurrentlyUsingFirstData = !CurrentlyUsingFirstData; + } + return OutPos; +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerDefs.h b/test/fuzz_test/Fuzzer/FuzzerDefs.h new file mode 100644 index 00000000..0f5b8a7c --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerDefs.h @@ -0,0 +1,89 @@ +//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Basic definitions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DEFS_H +#define LLVM_FUZZER_DEFS_H + +#include +#include +#include +#include +#include +#include + +// Platform detection. +#ifdef __linux__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_WINDOWS 0 +#elif __APPLE__ +#define LIBFUZZER_APPLE 1 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_WINDOWS 0 +#elif _WIN32 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_WINDOWS 1 +#else +#error "Support for your platform has not been implemented" +#endif + +#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX + +#ifdef __x86_64 +#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif + + +#ifdef __clang__ // avoid gcc warning. +# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +#else +# define ATTRIBUTE_NO_SANITIZE_MEMORY +#endif + +namespace fuzzer { + +template T Min(T a, T b) { return a < b ? a : b; } +template T Max(T a, T b) { return a > b ? a : b; } + +class Random; +class Dictionary; +class DictionaryEntry; +class MutationDispatcher; +struct FuzzingOptions; +class InputCorpus; +struct InputInfo; +struct ExternalFunctions; + +// Global interface to functions that may or may not be available. +extern ExternalFunctions *EF; + +typedef std::vector Unit; +typedef std::vector UnitVector; +typedef int (*UserCallback)(const uint8_t *Data, size_t Size); + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); + +struct ScopedDoingMyOwnMemmem { + ScopedDoingMyOwnMemmem(); + ~ScopedDoingMyOwnMemmem(); +}; + +inline uint8_t Bswap(uint8_t x) { return x; } +inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } +inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } +inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DEFS_H diff --git a/test/fuzz_test/Fuzzer/FuzzerDictionary.h b/test/fuzz_test/Fuzzer/FuzzerDictionary.h new file mode 100644 index 00000000..eba0eabb --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerDictionary.h @@ -0,0 +1,124 @@ +//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::Dictionary +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DICTIONARY_H +#define LLVM_FUZZER_DICTIONARY_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include + +namespace fuzzer { +// A simple POD sized array of bytes. +template class FixedWord { +public: + FixedWord() {} + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + + void Set(const uint8_t *B, uint8_t S) { + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = S; + } + + bool operator==(const FixedWord &w) const { + return Size == w.Size && 0 == memcmp(Data, w.Data, Size); + } + + bool operator<(const FixedWord &w) const { + if (Size != w.Size) + return Size < w.Size; + return memcmp(Data, w.Data, Size) < 0; + } + + static size_t GetMaxSize() { return kMaxSize; } + const uint8_t *data() const { return Data; } + uint8_t size() const { return Size; } + +private: + uint8_t Size = 0; + uint8_t Data[kMaxSize]; +}; + +typedef FixedWord<27> Word; // 28 bytes. + +class DictionaryEntry { + public: + DictionaryEntry() {} + DictionaryEntry(Word W) : W(W) {} + DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + const Word &GetW() const { return W; } + + bool HasPositionHint() const { return PositionHint != std::numeric_limits::max(); } + size_t GetPositionHint() const { + assert(HasPositionHint()); + return PositionHint; + } + void IncUseCount() { UseCount++; } + void IncSuccessCount() { SuccessCount++; } + size_t GetUseCount() const { return UseCount; } + size_t GetSuccessCount() const {return SuccessCount; } + + void Print(const char *PrintAfter = "\n") { + PrintASCII(W.data(), W.size()); + if (HasPositionHint()) + Printf("@%zd", GetPositionHint()); + Printf("%s", PrintAfter); + } + +private: + Word W; + size_t PositionHint = std::numeric_limits::max(); + size_t UseCount = 0; + size_t SuccessCount = 0; +}; + +class Dictionary { + public: + static const size_t kMaxDictSize = 1 << 14; + + bool ContainsWord(const Word &W) const { + return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) { + return DE.GetW() == W; + }); + } + const DictionaryEntry *begin() const { return &DE[0]; } + const DictionaryEntry *end() const { return begin() + Size; } + DictionaryEntry & operator[] (size_t Idx) { + assert(Idx < Size); + return DE[Idx]; + } + void push_back(DictionaryEntry DE) { + if (Size < kMaxDictSize) + this->DE[Size++] = DE; + } + void clear() { Size = 0; } + bool empty() const { return Size == 0; } + size_t size() const { return Size; } + +private: + DictionaryEntry DE[kMaxDictSize]; + size_t Size = 0; +}; + +// Parses one dictionary entry. +// If successfull, write the enty to Unit and returns true, +// otherwise returns false. +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); +// Parses the dictionary file, fills Units, returns true iff all lines +// were parsed succesfully. +bool ParseDictionaryFile(const std::string &Text, std::vector *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/test/fuzz_test/Fuzzer/FuzzerDriver.cpp b/test/fuzz_test/Fuzzer/FuzzerDriver.cpp new file mode 100644 index 00000000..95b0721c --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerDriver.cpp @@ -0,0 +1,545 @@ +//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// FuzzerDriver and flag parsing. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include +#include +#include + +// This function should be present in the libFuzzer so that the client +// binary can test for its existence. +extern "C" __attribute__((used)) void __libfuzzer_is_present() {} + +namespace fuzzer { + +// Program arguments. +struct FlagDescription { + const char *Name; + const char *Description; + int Default; + int *IntFlag; + const char **StrFlag; + unsigned int *UIntFlag; +}; + +struct { +#define FUZZER_DEPRECATED_FLAG(Name) +#define FUZZER_FLAG_INT(Name, Default, Description) int Name; +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; +#define FUZZER_FLAG_STRING(Name, Description) const char *Name; +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING +} Flags; + +static const FlagDescription FlagDescriptions [] { +#define FUZZER_DEPRECATED_FLAG(Name) \ + {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, +#define FUZZER_FLAG_INT(Name, Default, Description) \ + {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ + {#Name, Description, static_cast(Default), \ + nullptr, nullptr, &Flags.Name}, +#define FUZZER_FLAG_STRING(Name, Description) \ + {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static std::vector *Inputs; +static std::string *ProgName; + +static void PrintHelp() { + Printf("Usage:\n"); + auto Prog = ProgName->c_str(); + Printf("\nTo run fuzzing pass 0 or more directories.\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); + + Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); + + Printf("\nFlags: (strictly in form -flag=value)\n"); + size_t MaxFlagLen = 0; + for (size_t F = 0; F < kNumFlags; F++) + MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); + + for (size_t F = 0; F < kNumFlags; F++) { + const auto &D = FlagDescriptions[F]; + if (strstr(D.Description, "internal flag") == D.Description) continue; + Printf(" %s", D.Name); + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + Printf(" "); + Printf("\t"); + Printf("%d\t%s\n", D.Default, D.Description); + } + Printf("\nFlags starting with '--' will be ignored and " + "will be passed verbatim to subprocesses.\n"); +} + +static const char *FlagValue(const char *Param, const char *Name) { + size_t Len = strlen(Name); + if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && + Param[Len + 1] == '=') + return &Param[Len + 2]; + return nullptr; +} + +// Avoid calling stol as it triggers a bug in clang/glibc build. +static long MyStol(const char *Str) { + long Res = 0; + long Sign = 1; + if (*Str == '-') { + Str++; + Sign = -1; + } + for (size_t i = 0; Str[i]; i++) { + char Ch = Str[i]; + if (Ch < '0' || Ch > '9') + return Res; + Res = Res * 10 + (Ch - '0'); + } + return Res * Sign; +} + +static bool ParseOneFlag(const char *Param) { + if (Param[0] != '-') return false; + if (Param[1] == '-') { + static bool PrintedWarning = false; + if (!PrintedWarning) { + PrintedWarning = true; + Printf("INFO: libFuzzer ignores flags that start with '--'\n"); + } + for (size_t F = 0; F < kNumFlags; F++) + if (FlagValue(Param + 1, FlagDescriptions[F].Name)) + Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); + return true; + } + for (size_t F = 0; F < kNumFlags; F++) { + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + if (FlagDescriptions[F].IntFlag) { + 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) { + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; + if (Flags.verbosity >= 2) + Printf("Flag: %s %u\n", Name, Val); + return true; + } else if (FlagDescriptions[F].StrFlag) { + *FlagDescriptions[F].StrFlag = Str; + if (Flags.verbosity >= 2) + Printf("Flag: %s %s\n", Name, Str); + return true; + } else { // Deprecated flag. + Printf("Flag: %s: deprecated, don't use\n", Name); + return true; + } + } + } + Printf("\n\nWARNING: unrecognized flag '%s'; " + "use -help=1 to list all flags\n\n", Param); + return true; +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(const std::vector &Args) { + for (size_t F = 0; F < kNumFlags; F++) { + if (FlagDescriptions[F].IntFlag) + *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; + if (FlagDescriptions[F].UIntFlag) + *FlagDescriptions[F].UIntFlag = + static_cast(FlagDescriptions[F].Default); + if (FlagDescriptions[F].StrFlag) + *FlagDescriptions[F].StrFlag = nullptr; + } + Inputs = new std::vector; + for (size_t A = 1; A < Args.size(); A++) { + if (ParseOneFlag(Args[A].c_str())) continue; + Inputs->push_back(Args[A]); + } +} + +static std::mutex Mu; + +static void PulseThread() { + while (true) { + SleepSeconds(600); + std::lock_guard Lock(Mu); + Printf("pulse...\n"); + } +} + +static void WorkerThread(const std::string &Cmd, std::atomic *Counter, + unsigned NumJobs, std::atomic *HasErrors) { + while (true) { + unsigned C = (*Counter)++; + if (C >= NumJobs) break; + std::string Log = "fuzz-" + std::to_string(C) + ".log"; + std::string ToRun = Cmd + " > " + Log + " 2>&1\n"; + if (Flags.verbosity) + Printf("%s", ToRun.c_str()); + int ExitCode = ExecuteCommand(ToRun); + if (ExitCode != 0) + *HasErrors = true; + std::lock_guard Lock(Mu); + Printf("================== Job %u exited with exit code %d ============\n", + C, ExitCode); + fuzzer::CopyFileToErr(Log); + } +} + +std::string CloneArgsWithoutX(const std::vector &Args, + const char *X1, const char *X2) { + std::string Cmd; + for (auto &S : Args) { + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) + continue; + Cmd += S + " "; + } + return Cmd; +} + +static int RunInMultipleProcesses(const std::vector &Args, + unsigned NumWorkers, unsigned NumJobs) { + std::atomic Counter(0); + std::atomic HasErrors(false); + std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers"); + std::vector V; + std::thread Pulse(PulseThread); + Pulse.detach(); + for (unsigned i = 0; i < NumWorkers; i++) + V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors)); + for (auto &T : V) + T.join(); + return HasErrors ? 1 : 0; +} + +static void RssThread(Fuzzer *F, size_t RssLimitMb) { + while (true) { + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) + F->RssLimitCallback(); + } +} + +static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + if (!RssLimitMb) return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); +} + +int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { + Unit U = FileToVector(InputFilePath); + if (MaxLen && MaxLen < U.size()) + U.resize(MaxLen); + F->RunOne(U.data(), U.size()); + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + return 0; +} + +static bool AllInputsAreFiles() { + if (Inputs->empty()) return false; + for (auto &Path : *Inputs) + if (!IsFile(Path)) + return false; + return true; +} + +int MinimizeCrashInput(const std::vector &Args) { + if (Inputs->size() != 1) { + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + } + std::string InputFilePath = Inputs->at(0); + std::string BaseCmd = + CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path"); + auto InputPos = BaseCmd.find(" " + InputFilePath + " "); + assert(InputPos != std::string::npos); + BaseCmd.erase(InputPos, InputFilePath.size() + 1); + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + Printf("INFO: you need to specify -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" + "INFO: defaulting to -max_total_time=600\n"); + BaseCmd += " -max_total_time=600"; + } + // BaseCmd += " > /dev/null 2>&1 "; + + std::string CurrentFilePath = InputFilePath; + while (true) { + Unit U = FileToVector(CurrentFilePath); + if (U.size() < 2) { + Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str()); + return 0; + } + Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", + CurrentFilePath.c_str(), U.size()); + + auto Cmd = BaseCmd + " " + CurrentFilePath; + + Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + } + Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " + "it further\n", + CurrentFilePath.c_str(), U.size()); + + std::string ArtifactPath = "minimized-from-" + Hash(U); + Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" + + ArtifactPath; + Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + if (Flags.exact_artifact_path) { + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + } + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + return 0; + } + CurrentFilePath = ArtifactPath; + Printf("\n\n\n\n\n\n*********************************\n"); + } + return 0; +} + +int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { + assert(Inputs->size() == 1); + std::string InputFilePath = Inputs->at(0); + Unit U = FileToVector(InputFilePath); + assert(U.size() > 2); + Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + Corpus->AddToCorpus(U, 0); + F->SetMaxInputLen(U.size()); + F->SetMaxMutationLen(U.size() - 1); + F->MinimizeCrashLoop(U); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; +} + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { + using namespace fuzzer; + assert(argc && argv && "Argument pointers cannot be nullptr"); + EF = new ExternalFunctions(); + if (EF->LLVMFuzzerInitialize) + EF->LLVMFuzzerInitialize(argc, argv); + const std::vector Args(*argv, *argv + *argc); + assert(!Args.empty()); + ProgName = new std::string(Args[0]); + ParseFlags(Args); + if (Flags.help) { + PrintHelp(); + return 0; + } + + if (Flags.minimize_crash) + return MinimizeCrashInput(Args); + + if (Flags.close_fd_mask & 2) + DupAndCloseStderr(); + if (Flags.close_fd_mask & 1) + CloseStdout(); + + if (Flags.jobs > 0 && Flags.workers == 0) { + Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); + if (Flags.workers > 1) + Printf("Running %u workers\n", Flags.workers); + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); + + const size_t kMaxSaneLen = 1 << 20; + const size_t kMinDefaultLen = 64; + FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.UnitTimeoutSec = Flags.timeout; + Options.ErrorExitCode = Flags.error_exitcode; + Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.MaxTotalTimeSec = Flags.max_total_time; + Options.DoCrossOver = Flags.cross_over; + Options.MutateDepth = Flags.mutate_depth; + Options.UseCounters = Flags.use_counters; + Options.UseIndirCalls = Flags.use_indir_calls; + Options.UseMemcmp = Flags.use_memcmp; + Options.UseMemmem = Flags.use_memmem; + Options.UseCmp = Flags.use_cmp; + Options.UseValueProfile = Flags.use_value_profile; + Options.Shrink = Flags.shrink; + Options.ShuffleAtStartUp = Flags.shuffle; + Options.PreferSmall = Flags.prefer_small; + Options.ReloadIntervalSec = Flags.reload; + Options.OnlyASCII = Flags.only_ascii; + Options.OutputCSV = Flags.output_csv; + Options.DetectLeaks = Flags.detect_leaks; + Options.TraceMalloc = Flags.trace_malloc; + Options.RssLimitMb = Flags.rss_limit_mb; + if (Flags.runs >= 0) + Options.MaxNumberOfRuns = Flags.runs; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) + Options.OutputCorpus = (*Inputs)[0]; + Options.ReportSlowUnits = Flags.report_slow_units; + if (Flags.artifact_prefix) + Options.ArtifactPrefix = Flags.artifact_prefix; + if (Flags.exact_artifact_path) + Options.ExactArtifactPath = Flags.exact_artifact_path; + std::vector Dictionary; + if (Flags.dict) + if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) + return 1; + if (Flags.verbosity > 0 && !Dictionary.empty()) + Printf("Dictionary: %zd entries\n", Dictionary.size()); + bool DoPlainRun = AllInputsAreFiles(); + Options.SaveArtifacts = + !DoPlainRun || Flags.minimize_crash_internal_step; + Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintCorpusStats = Flags.print_corpus_stats; + Options.PrintCoverage = Flags.print_coverage; + Options.DumpCoverage = Flags.dump_coverage; + if (Flags.exit_on_src_pos) + Options.ExitOnSrcPos = Flags.exit_on_src_pos; + if (Flags.exit_on_item) + Options.ExitOnItem = Flags.exit_on_item; + + unsigned Seed = Flags.seed; + // Initialize Seed. + if (Seed == 0) + Seed = (std::chrono::system_clock::now().time_since_epoch().count() << 10) + + GetPid(); + if (Flags.verbosity) + Printf("INFO: Seed: %u\n", Seed); + + Random Rand(Seed); + auto *MD = new MutationDispatcher(Rand, Options); + auto *Corpus = new InputCorpus(Options.OutputCorpus); + auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); + + for (auto &U: Dictionary) + if (U.size() <= Word::GetMaxSize()) + MD->AddWordToManualDictionary(Word(U.data(), U.size())); + + StartRssThread(F, Flags.rss_limit_mb); + + Options.HandleAbrt = Flags.handle_abrt; + Options.HandleBus = Flags.handle_bus; + Options.HandleFpe = Flags.handle_fpe; + Options.HandleIll = Flags.handle_ill; + Options.HandleInt = Flags.handle_int; + Options.HandleSegv = Flags.handle_segv; + Options.HandleTerm = Flags.handle_term; + SetSignalHandler(Options); + + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(F, Corpus); + + if (DoPlainRun) { + Options.SaveArtifacts = false; + int Runs = std::max(1, Flags.runs); + Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), + Inputs->size(), Runs); + for (auto &Path : *Inputs) { + auto StartTime = system_clock::now(); + Printf("Running: %s\n", Path.c_str()); + for (int Iter = 0; Iter < Runs; Iter++) + RunOneTest(F, Path.c_str(), Options.MaxLen); + auto StopTime = system_clock::now(); + auto MS = duration_cast(StopTime - StartTime).count(); + Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); + } + Printf("***\n" + "*** NOTE: fuzzing was not performed, you have only\n" + "*** executed the target code on a fixed set of inputs.\n" + "***\n"); + F->PrintFinalStats(); + exit(0); + } + + if (Flags.merge) { + if (Options.MaxLen == 0) + F->SetMaxInputLen(kMaxSaneLen); + if (TPC.UsingTracePcGuard()) { + if (Flags.merge_control_file) + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + else + F->CrashResistantMerge(Args, *Inputs); + } else { + F->Merge(*Inputs); + } + exit(0); + } + + size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen; + + UnitVector InitialCorpus; + for (auto &Inp : *Inputs) { + Printf("Loading corpus dir: %s\n", Inp.c_str()); + ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, + TemporaryMaxLen, /*ExitOnError=*/false); + } + + if (Options.MaxLen == 0) { + size_t MaxLen = 0; + for (auto &U : InitialCorpus) + MaxLen = std::max(U.size(), MaxLen); + F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen)); + } + + if (InitialCorpus.empty()) { + InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input. + if (Options.Verbosity) + Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); + } + F->ShuffleAndMinimize(&InitialCorpus); + InitialCorpus.clear(); // Don't need this memory any more. + F->Loop(); + + if (Flags.verbosity) + Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + F->secondsSinceProcessStartUp()); + F->PrintFinalStats(); + + exit(0); // Don't let F destroy itself. +} + +// Storage for global ExternalFunctions object. +ExternalFunctions *EF = nullptr; + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def new file mode 100644 index 00000000..61c72e4a --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def @@ -0,0 +1,50 @@ +//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This defines the external function pointers that +// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The +// EXT_FUNC macro must be defined at the point of inclusion. The signature of +// the macro is: +// +// EXT_FUNC(, , , ) +//===----------------------------------------------------------------------===// + +// Optional user functions +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t * Data1, size_t Size1, + const uint8_t * Data2, size_t Size2, + uint8_t * Out, size_t MaxOutSize, unsigned int Seed), + false); + +// Sanitizer functions +EXT_FUNC(__lsan_enable, void, (), false); +EXT_FUNC(__lsan_disable, void, (), false); +EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_get_number_of_counters, size_t, (), false); +EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, + (void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)), + false); +EXT_FUNC(__sanitizer_get_total_unique_caller_callee_pairs, size_t, (), false); +EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true); +EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false); +EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); +EXT_FUNC(__sanitizer_symbolize_pc, void, + (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); +EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), false); +EXT_FUNC(__sanitizer_reset_coverage, void, (), true); +EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); +EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); +EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t, + (uint8_t*), false); diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h new file mode 100644 index 00000000..2672a385 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h @@ -0,0 +1,35 @@ +//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Defines an interface to (possibly optional) functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H +#define LLVM_FUZZER_EXT_FUNCTIONS_H + +#include +#include + +namespace fuzzer { + +struct ExternalFunctions { + // Initialize function pointers. Functions that are not available will be set + // to nullptr. Do not call this constructor before ``main()`` has been + // entered. + ExternalFunctions(); + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE(*NAME) FUNC_SIG = nullptr + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +}; +} // namespace fuzzer + +#endif diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp new file mode 100644 index 00000000..06bddd5d --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp @@ -0,0 +1,52 @@ +//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation for operating systems that support dlsym(). We only use it on +// Apple platforms for now. We don't use this approach on Linux because it +// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker. +// That is a complication we don't wish to expose to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_APPLE + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include + +using namespace fuzzer; + +template +static T GetFnPtr(const char *FnName, bool WarnIfMissing) { + dlerror(); // Clear any previous errors. + void *Fn = dlsym(RTLD_DEFAULT, FnName); + if (Fn == nullptr) { + if (WarnIfMissing) { + const char *ErrorMsg = dlerror(); + Printf("WARNING: Failed to find function \"%s\".", FnName); + if (ErrorMsg) + Printf(" Reason %s.", ErrorMsg); + Printf("\n"); + } + } + return reinterpret_cast(Fn); +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(#NAME, WARN) + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp new file mode 100644 index 00000000..7b02b6f0 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp @@ -0,0 +1,53 @@ +//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation for Linux. This relies on the linker's support for weak +// symbols. We don't use this approach on Apple platforms because it requires +// clients of LibFuzzer to pass ``-U _`` to the linker to allow +// weak symbols to be undefined. That is a complication we don't want to expose +// to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_LINUX + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +extern "C" { +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak)) RETURN_TYPE NAME FUNC_SIG + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +using namespace fuzzer; + +static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) { + if (FnPtr == nullptr && WarnIfMissing) { + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + } +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = ::NAME; \ + CheckFnPtr((void *)::NAME, #NAME, WARN); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp new file mode 100644 index 00000000..e10f7b4d --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp @@ -0,0 +1,56 @@ +//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation using weak aliases. Works for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" + +using namespace fuzzer; + +extern "C" { +// Declare these symbols as weak to allow them to be optionally defined. +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + } \ + RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def"))); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +template +static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) { + if (Fun == FunDef) { + if (WarnIfMissing) + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + return nullptr; + } + return Fun; +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(::NAME, ::NAME##Def, #NAME, WARN); + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/test/fuzz_test/Fuzzer/FuzzerFlags.def b/test/fuzz_test/Fuzzer/FuzzerFlags.def new file mode 100644 index 00000000..25ef1741 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerFlags.def @@ -0,0 +1,115 @@ +//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the +// point of inclusion. We are not using any flag parsing library for better +// portability and independence. +//===----------------------------------------------------------------------===// +FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.") +FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") +FUZZER_FLAG_INT(runs, -1, + "Number of individual test runs (-1 for infinite runs).") +FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. " + "If 0, libFuzzer tries to guess a good value based on the corpus " + "and reports it. ") +FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(mutate_depth, 5, + "Apply this number of consecutive mutations to each input.") +FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup") +FUZZER_FLAG_INT(prefer_small, 1, + "If 1, always prefer smaller inputs during the corpus shuffle.") +FUZZER_FLAG_INT( + timeout, 1200, + "Timeout in seconds (if positive). " + "If one unit runs more than this number of seconds the process will abort.") +FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug " + "this exit code will be used.") +FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout " + "this exit code will be used.") +FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " + "time in seconds to run the fuzzer.") +FUZZER_FLAG_INT(help, 0, "Print help.") +FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " + "merged into the 1-st corpus. Only interesting units will be taken. " + "This flag can be used to minimize a corpus.") +FUZZER_FLAG_STRING(merge_control_file, "internal flag") +FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" + " crash input. Use with -runs=N or -max_total_time=N to limit " + "the number attempts") +FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") +FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") +FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters") +FUZZER_FLAG_INT(use_memcmp, 1, + "Use hints from intercepting memcmp, strcmp, etc") +FUZZER_FLAG_INT(use_memmem, 1, + "Use hints from intercepting memmem, strstr, etc") +FUZZER_FLAG_INT(use_value_profile, 0, + "Experimental. Use value profile to guide fuzzing.") +FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations") +FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.") +FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" + " this number of jobs in separate worker processes" + " with stdout/stderr redirected to fuzz-JOB.log.") +FUZZER_FLAG_UNSIGNED(workers, 0, + "Number of simultaneous worker processes to run the jobs." + " If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.") +FUZZER_FLAG_INT(reload, 1, + "Reload the main corpus every seconds to get new units" + " discovered by other processes. If 0, disabled") +FUZZER_FLAG_INT(report_slow_units, 10, + "Report slowest units if they run for more than this number of seconds.") +FUZZER_FLAG_INT(only_ascii, 0, + "If 1, generate only ASCII (isprint+isspace) inputs.") +FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.") +FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, " + "timeout, or slow inputs) as " + "$(artifact_prefix)file") +FUZZER_FLAG_STRING(exact_artifact_path, + "Write the single artifact on failure (crash, timeout) " + "as $(exact_artifact_path). This overrides -artifact_prefix " + "and will not use checksum in the file name. Do not " + "use the same path for several parallel processes.") +FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.") +FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.") +FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.") +FUZZER_FLAG_INT(print_corpus_stats, 0, + "If 1, print statistics on corpus elements at exit.") +FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information at exit." + " Experimental, only with trace-pc-guard") +FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit." + " Experimental, only with trace-pc-guard") +FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") +FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") +FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") +FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") +FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") +FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " + "if 2, close stderr; if 3, close both. " + "Be careful, this will also close e.g. asan's stderr/stdout.") +FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled " + "try to detect memory leaks during fuzzing (i.e. not only at shut down).") +FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. " + "If >= 2 will also print stack traces.") +FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon" + "reaching this limit of RSS memory usage.") +FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates" + " from the given source location. Example: -exit_on_src_pos=foo.cc:123. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" + " was added to the corpus. " + "Used primarily for testing libFuzzer itself.") + +FUZZER_DEPRECATED_FLAG(exit_on_first) +FUZZER_DEPRECATED_FLAG(save_minimized_corpus) +FUZZER_DEPRECATED_FLAG(sync_command) +FUZZER_DEPRECATED_FLAG(sync_timeout) +FUZZER_DEPRECATED_FLAG(test_single_input) +FUZZER_DEPRECATED_FLAG(drill) +FUZZER_DEPRECATED_FLAG(truncate_units) diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.cpp b/test/fuzz_test/Fuzzer/FuzzerIO.cpp new file mode 100644 index 00000000..eda8e877 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIO.cpp @@ -0,0 +1,117 @@ +//===- FuzzerIO.cpp - IO utils. -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO functions. +//===----------------------------------------------------------------------===// + +#include "FuzzerIO.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static FILE *OutputFile = stderr; + +long GetEpoch(const std::string &Path) { + struct stat St; + if (stat(Path.c_str(), &St)) + return 0; // Can't stat, be conservative. + return St.st_mtime; +} + +Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { + std::ifstream T(Path); + if (ExitOnError && !T) { + Printf("No such directory: %s; exiting\n", Path.c_str()); + exit(1); + } + + T.seekg(0, T.end); + size_t FileLen = T.tellg(); + if (MaxSize) + FileLen = std::min(FileLen, MaxSize); + + T.seekg(0, T.beg); + Unit Res(FileLen); + T.read(reinterpret_cast(Res.data()), FileLen); + return Res; +} + +std::string FileToString(const std::string &Path) { + std::ifstream T(Path); + return std::string((std::istreambuf_iterator(T)), + std::istreambuf_iterator()); +} + +void CopyFileToErr(const std::string &Path) { + Printf("%s", FileToString(Path).c_str()); +} + +void WriteToFile(const Unit &U, const std::string &Path) { + // Use raw C interface because this function may be called from a sig handler. + FILE *Out = fopen(Path.c_str(), "w"); + if (!Out) return; + fwrite(U.data(), sizeof(U[0]), U.size(), Out); + fclose(Out); +} + +void ReadDirToVectorOfUnits(const char *Path, std::vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError) { + long E = Epoch ? *Epoch : 0; + std::vector Files; + ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); + size_t NumLoaded = 0; + for (size_t i = 0; i < Files.size(); i++) { + auto &X = Files[i]; + if (Epoch && GetEpoch(X) < E) continue; + NumLoaded++; + 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()) + V->push_back(S); + } +} + +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName) { + return DirPath + GetSeparator() + FileName; +} + +void DupAndCloseStderr() { + int OutputFd = DuplicateFile(2); + if (OutputFd > 0) { + FILE *NewOutputFile = OpenFile(OutputFd, "w"); + if (NewOutputFile) { + OutputFile = NewOutputFile; + if (EF->__sanitizer_set_report_fd) + EF->__sanitizer_set_report_fd(reinterpret_cast(OutputFd)); + CloseFile(2); + } + } +} + +void CloseStdout() { + CloseFile(1); +} + +void Printf(const char *Fmt, ...) { + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.h b/test/fuzz_test/Fuzzer/FuzzerIO.h new file mode 100644 index 00000000..741fecf4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIO.h @@ -0,0 +1,64 @@ +//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO interface. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IO_H +#define LLVM_FUZZER_IO_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +long GetEpoch(const std::string &Path); + +Unit FileToVector(const std::string &Path, size_t MaxSize = 0, + bool ExitOnError = true); + +std::string FileToString(const std::string &Path); + +void CopyFileToErr(const std::string &Path); + +void WriteToFile(const Unit &U, const std::string &Path); + +void ReadDirToVectorOfUnits(const char *Path, std::vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError); + +// Returns "Dir/FileName" or equivalent for the current OS. +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName); + +// Returns the name of the dir, similar to the 'dirname' utility. +std::string DirName(const std::string &FileName); + +void DupAndCloseStderr(); + +void CloseStdout(); + +void Printf(const char *Fmt, ...); + +// Platform specific functions: +bool IsFile(const std::string &Path); + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector *V, bool TopDir); + +char GetSeparator(); + +FILE* OpenFile(int Fd, const char *Mode); + +int CloseFile(int Fd); + +int DuplicateFile(int Fd); + +void RemoveFile(const std::string &Path); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_IO_H diff --git a/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp b/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp new file mode 100644 index 00000000..720bc130 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp @@ -0,0 +1,88 @@ +//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO functions implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_POSIX + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +bool IsFile(const std::string &Path) { + struct stat St; + if (stat(Path.c_str(), &St)) + return false; + return S_ISREG(St.st_mode); +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector *V, bool TopDir) { + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + DIR *D = opendir(Dir.c_str()); + if (!D) { + Printf("No such directory: %s; exiting\n", Dir.c_str()); + exit(1); + } + while (auto E = readdir(D)) { + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK) + V->push_back(Path); + else if (E->d_type == DT_DIR && *E->d_name != '.') + ListFilesInDirRecursive(Path, Epoch, V, false); + } + closedir(D); + if (Epoch && TopDir) + *Epoch = E; +} + +char GetSeparator() { + return '/'; +} + +FILE* OpenFile(int Fd, const char* Mode) { + return fdopen(Fd, Mode); +} + +int CloseFile(int fd) { + return close(fd); +} + +int DuplicateFile(int Fd) { + return dup(Fd); +} + +void RemoveFile(const std::string &Path) { + unlink(Path.c_str()); +} + +std::string DirName(const std::string &FileName) { + char *Tmp = new char[FileName.size() + 1]; + memcpy(Tmp, FileName.c_str(), FileName.size() + 1); + std::string Res = dirname(Tmp); + delete [] Tmp; + return Res; +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX diff --git a/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp b/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp new file mode 100644 index 00000000..a4738eb9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp @@ -0,0 +1,282 @@ +//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// IO functions implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static bool IsFile(const std::string &Path, const DWORD &FileAttributes) { + + if (FileAttributes & FILE_ATTRIBUTE_NORMAL) + return true; + + if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return false; + + HANDLE FileHandle( + CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0)); + + if (FileHandle == INVALID_HANDLE_VALUE) { + Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + return false; + } + + DWORD FileType = GetFileType(FileHandle); + + if (FileType == FILE_TYPE_UNKNOWN) { + Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + CloseHandle(FileHandle); + return false; + } + + if (FileType != FILE_TYPE_DISK) { + CloseHandle(FileHandle); + return false; + } + + CloseHandle(FileHandle); + return true; +} + +bool IsFile(const std::string &Path) { + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + } + + return IsFile(Path, Att); +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector *V, bool TopDir) { + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + std::string Path(Dir); + assert(!Path.empty()); + if (Path.back() != '\\') + Path.push_back('\\'); + Path.push_back('*'); + + // Get the first directory entry. + WIN32_FIND_DATAA FindInfo; + HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); + if (FindHandle == INVALID_HANDLE_VALUE) + { + Printf("No file found in: %s.\n", Dir.c_str()); + return; + } + + do { + std::string FileName = DirPlusFile(Dir, FindInfo.cFileName); + + if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + size_t FilenameLen = strlen(FindInfo.cFileName); + if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') || + (FilenameLen == 2 && FindInfo.cFileName[0] == '.' && + FindInfo.cFileName[1] == '.')) + continue; + + ListFilesInDirRecursive(FileName, Epoch, V, false); + } + else if (IsFile(FileName, FindInfo.dwFileAttributes)) + V->push_back(FileName); + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed (Error code: %lu).\n", LastError); + + FindClose(FindHandle); + + if (Epoch && TopDir) + *Epoch = E; +} + +char GetSeparator() { + return '\\'; +} + +FILE* OpenFile(int Fd, const char* Mode) { + return _fdopen(Fd, Mode); +} + +int CloseFile(int Fd) { + return _close(Fd); +} + +int DuplicateFile(int Fd) { + return _dup(Fd); +} + +void RemoveFile(const std::string &Path) { + _unlink(Path.c_str()); +} + +static bool IsSeparator(char C) { + return C == '\\' || C == '/'; +} + +// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:". +// Returns number of characters considered if successful. +static size_t ParseDrive(const std::string &FileName, const size_t Offset, + bool Relative = true) { + if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') + return 0; + if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) { + if (!Relative) // Accept relative path? + return 0; + else + return 2; + } + return 3; +} + +// Parse a file name, like: SomeFile.txt +// Returns number of characters considered if successful. +static size_t ParseFileName(const std::string &FileName, const size_t Offset) { + size_t Pos = Offset; + const size_t End = FileName.size(); + for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + return Pos - Offset; +} + +// Parse a directory ending in separator, like: SomeDir\ +// Returns number of characters considered if successful. +static size_t ParseDir(const std::string &FileName, const size_t Offset) { + size_t Pos = Offset; + const size_t End = FileName.size(); + if (Pos >= End || IsSeparator(FileName[Pos])) + return 0; + for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + if (Pos >= End) + return 0; + ++Pos; // Include separator. + return Pos - Offset; +} + +// Parse a servername and share, like: SomeServer\SomeShare\ +// Returns number of characters considered if successful. +static size_t ParseServerAndShare(const std::string &FileName, + const size_t Offset) { + size_t Pos = Offset, Res; + if (!(Res = ParseDir(FileName, Pos))) + return 0; + Pos += Res; + if (!(Res = ParseDir(FileName, Pos))) + return 0; + Pos += Res; + return Pos - Offset; +} + +// Parse the given Ref string from the position Offset, to exactly match the given +// string Patt. +// Returns number of characters considered if successful. +static size_t ParseCustomString(const std::string &Ref, size_t Offset, + const char *Patt) { + size_t Len = strlen(Patt); + if (Offset + Len > Ref.size()) + return 0; + return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0; +} + +// Parse a location, like: +// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C: +// Returns number of characters considered if successful. +static size_t ParseLocation(const std::string &FileName) { + size_t Pos = 0, Res; + + if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) { + Pos += Res; + if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) { + Pos += Res; + if ((Res = ParseServerAndShare(FileName, Pos))) + return Pos + Res; + return 0; + } + if ((Res = ParseDrive(FileName, Pos, false))) + return Pos + Res; + return 0; + } + + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + ++Pos; + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + ++Pos; + if ((Res = ParseServerAndShare(FileName, Pos))) + return Pos + Res; + return 0; + } + return Pos; + } + + if ((Res = ParseDrive(FileName, Pos))) + return Pos + Res; + + return Pos; +} + +std::string DirName(const std::string &FileName) { + size_t LocationLen = ParseLocation(FileName); + size_t DirLen = 0, Res; + while ((Res = ParseDir(FileName, LocationLen + DirLen))) + DirLen += Res; + size_t FileLen = ParseFileName(FileName, LocationLen + DirLen); + + if (LocationLen + DirLen + FileLen != FileName.size()) { + Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str()); + exit(1); + } + + if (DirLen) { + --DirLen; // Remove trailing separator. + if (!FileLen) { // Path ended in separator. + assert(DirLen); + // Remove file name from Dir. + while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1])) + --DirLen; + if (DirLen) // Remove trailing separator. + --DirLen; + } + } + + if (!LocationLen) { // Relative path. + if (!DirLen) + return "."; + return std::string(".\\").append(FileName, 0, DirLen); + } + + return FileName.substr(0, LocationLen + DirLen); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/test/fuzz_test/Fuzzer/FuzzerInterface.h b/test/fuzz_test/Fuzzer/FuzzerInterface.h new file mode 100644 index 00000000..d47e20e3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerInterface.h @@ -0,0 +1,67 @@ +//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Define the interface between libFuzzer and the library being tested. +//===----------------------------------------------------------------------===// + +// NOTE: the libFuzzer interface is thin and in the majority of cases +// you should not include this file into your target. In 95% of cases +// all you need is to define the following function in your file: +// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// WARNING: keep the interface in C. + +#ifndef LLVM_FUZZER_INTERFACE_H +#define LLVM_FUZZER_INTERFACE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Mandatory user-provided target function. +// Executes the code under test with [Data, Data+Size) as the input. +// libFuzzer will invoke this function *many* times with different inputs. +// Must return 0. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// Optional user-provided initialization function. +// If provided, this function will be called by libFuzzer once at startup. +// It may read and modify argc/argv. +// Must return 0. +int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Optional user-provided custom mutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +// Given the same Seed produces the same mutation. +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); + +// Optional user-provided custom cross-over function. +// Combines pieces of Data1 & Data2 together into Out. +// Returns the new size, which is not greater than MaxOutSize. +// Should produce the same mutation given the same Seed. +size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, + unsigned int Seed); + +// Experimental, may go away in future. +// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // LLVM_FUZZER_INTERFACE_H diff --git a/test/fuzz_test/Fuzzer/FuzzerInternal.h b/test/fuzz_test/Fuzzer/FuzzerInternal.h new file mode 100644 index 00000000..c0417060 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerInternal.h @@ -0,0 +1,182 @@ +//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Define the main class fuzzer::Fuzzer and most functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_INTERNAL_H +#define LLVM_FUZZER_INTERNAL_H + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerInterface.h" +#include "FuzzerOptions.h" +#include "FuzzerSHA1.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +using namespace std::chrono; + +class Fuzzer { +public: + + // Aggregates all available coverage measurements. + struct Coverage { + Coverage() { Reset(); } + + void Reset() { + BlockCoverage = 0; + CallerCalleeCoverage = 0; + CounterBitmapBits = 0; + CounterBitmap.clear(); + VPMap.Reset(); + } + + size_t BlockCoverage; + size_t CallerCalleeCoverage; + // Precalculated number of bits in CounterBitmap. + size_t CounterBitmapBits; + std::vector CounterBitmap; + ValueBitMap VPMap; + }; + + Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options); + ~Fuzzer(); + void Loop(); + void MinimizeCrashLoop(const Unit &U); + void ShuffleAndMinimize(UnitVector *V); + void InitializeTraceState(); + void RereadOutputCorpus(size_t MaxSize); + + size_t secondsSinceProcessStartUp() { + return duration_cast(system_clock::now() - ProcessStartTime) + .count(); + } + + bool TimedOut() { + return Options.MaxTotalTimeSec > 0 && + secondsSinceProcessStartUp() > + static_cast(Options.MaxTotalTimeSec); + } + + size_t execPerSec() { + size_t Seconds = secondsSinceProcessStartUp(); + return Seconds ? TotalNumberOfRuns / Seconds : 0; + } + + size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } + + static void StaticAlarmCallback(); + static void StaticCrashSignalCallback(); + static void StaticInterruptCallback(); + + void ExecuteCallback(const uint8_t *Data, size_t Size); + size_t RunOne(const uint8_t *Data, size_t Size); + + // Merge Corpora[1:] into Corpora[0]. + void Merge(const std::vector &Corpora); + void CrashResistantMerge(const std::vector &Args, + const std::vector &Corpora); + void CrashResistantMergeInternalStep(const std::string &ControlFilePath); + // Returns a subset of 'Extra' that adds coverage to 'Initial'. + UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra); + MutationDispatcher &GetMD() { return MD; } + void PrintFinalStats(); + void SetMaxInputLen(size_t MaxInputLen); + void SetMaxMutationLen(size_t MaxMutationLen); + void RssLimitCallback(); + + // Public for tests. + void ResetCoverage(); + + bool InFuzzingThread() const { return IsMyThread; } + size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; + void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution); + + void HandleMalloc(size_t Size); + +private: + void AlarmCallback(); + void CrashCallback(); + void InterruptCallback(); + void MutateAndTestOne(); + void ReportNewCoverage(InputInfo *II, const Unit &U); + size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); } + void WriteToOutputCorpus(const Unit &U); + void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0); + void PrintStatusForNewUnit(const Unit &U); + void ShuffleCorpus(UnitVector *V); + void AddToCorpus(const Unit &U); + void CheckExitOnSrcPosOrItem(); + + // Trace-based fuzzing: we run a unit with some kind of tracing + // enabled and record potentially useful mutations. Then + // We apply these mutations one by one to the unit and run it again. + + // Start tracing; forget all previously proposed mutations. + void StartTraceRecording(); + // Stop tracing. + void StopTraceRecording(); + + void SetDeathCallback(); + static void StaticDeathCallback(); + void DumpCurrentUnit(const char *Prefix); + void DeathCallback(); + + void ResetEdgeCoverage(); + void ResetCounters(); + void PrepareCounters(Fuzzer::Coverage *C); + bool RecordMaxCoverage(Fuzzer::Coverage *C); + + void AllocateCurrentUnitData(); + uint8_t *CurrentUnitData = nullptr; + std::atomic CurrentUnitSize; + uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit. + bool RunningCB = false; + + size_t TotalNumberOfRuns = 0; + size_t NumberOfNewUnitsAdded = 0; + + bool HasMoreMallocsThanFrees = false; + size_t NumberOfLeakDetectionAttempts = 0; + + UserCallback CB; + InputCorpus &Corpus; + MutationDispatcher &MD; + FuzzingOptions Options; + + system_clock::time_point ProcessStartTime = system_clock::now(); + system_clock::time_point UnitStartTime, UnitStopTime; + long TimeOfLongestUnitInSeconds = 0; + long EpochOfLastReadOfOutputCorpus = 0; + + // Maximum recorded coverage. + Coverage MaxCoverage; + + size_t MaxInputLen = 0; + size_t MaxMutationLen = 0; + + // Need to know our own thread. + static thread_local bool IsMyThread; + + bool InMergeMode = false; +}; + +}; // namespace fuzzer + +#endif // LLVM_FUZZER_INTERNAL_H diff --git a/test/fuzz_test/Fuzzer/FuzzerLoop.cpp b/test/fuzz_test/Fuzzer/FuzzerLoop.cpp new file mode 100644 index 00000000..73e058ff --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerLoop.cpp @@ -0,0 +1,792 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include + +#if defined(__has_include) +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif +#endif + +#define NO_SANITIZE_MEMORY +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#undef NO_SANITIZE_MEMORY +#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#endif +#endif + +namespace fuzzer { +static const size_t kMaxUnitSizeToPrint = 256; + +thread_local bool Fuzzer::IsMyThread; + +static void MissingExternalApiFunction(const char *FnName) { + Printf("ERROR: %s is not defined. Exiting.\n" + "Did you use -fsanitize-coverage=... to build your code?\n", + FnName); + exit(1); +} + +#define CHECK_EXTERNAL_FUNCTION(fn) \ + do { \ + if (!(EF->fn)) \ + MissingExternalApiFunction(#fn); \ + } while (false) + +// Only one Fuzzer per process. +static Fuzzer *F; + +void Fuzzer::ResetEdgeCoverage() { + CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage); + EF->__sanitizer_reset_coverage(); +} + +void Fuzzer::ResetCounters() { + if (Options.UseCounters) + EF->__sanitizer_update_counter_bitset_and_clear_counters(0); +} + +void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) { + if (Options.UseCounters) { + size_t NumCounters = EF->__sanitizer_get_number_of_counters(); + C->CounterBitmap.resize(NumCounters); + } +} + +// Records data to a maximum coverage tracker. Returns true if additional +// coverage was discovered. +bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) { + bool Res = false; + + uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage(); + if (NewBlockCoverage > C->BlockCoverage) { + Res = true; + C->BlockCoverage = NewBlockCoverage; + } + + if (Options.UseIndirCalls && + EF->__sanitizer_get_total_unique_caller_callee_pairs) { + uint64_t NewCallerCalleeCoverage = + EF->__sanitizer_get_total_unique_caller_callee_pairs(); + if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) { + Res = true; + C->CallerCalleeCoverage = NewCallerCalleeCoverage; + } + } + + if (Options.UseCounters) { + uint64_t CounterDelta = + EF->__sanitizer_update_counter_bitset_and_clear_counters( + C->CounterBitmap.data()); + if (CounterDelta > 0) { + Res = true; + C->CounterBitmapBits += CounterDelta; + } + } + + return Res; +} + +// Leak detection is expensive, so we first check if there were more mallocs +// than frees (using the sanitizer malloc hooks) and only then try to call lsan. +struct MallocFreeTracer { + void Start(int TraceLevel) { + this->TraceLevel = TraceLevel; + if (TraceLevel) + Printf("MallocFreeTracer: START\n"); + Mallocs = 0; + Frees = 0; + } + // Returns true if there were more mallocs than frees. + bool Stop() { + if (TraceLevel) + Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(), + Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT"); + bool Result = Mallocs > Frees; + Mallocs = 0; + Frees = 0; + TraceLevel = 0; + return Result; + } + std::atomic Mallocs; + std::atomic Frees; + int TraceLevel = 0; +}; + +static MallocFreeTracer AllocTracer; + +ATTRIBUTE_NO_SANITIZE_MEMORY +void MallocHook(const volatile void *ptr, size_t size) { + size_t N = AllocTracer.Mallocs++; + F->HandleMalloc(size); + if (int TraceLevel = AllocTracer.TraceLevel) { + Printf("MALLOC[%zd] %p %zd\n", N, ptr, size); + if (TraceLevel >= 2 && EF) + EF->__sanitizer_print_stack_trace(); + } +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void FreeHook(const volatile void *ptr) { + size_t N = AllocTracer.Frees++; + if (int TraceLevel = AllocTracer.TraceLevel) { + Printf("FREE[%zd] %p\n", N, ptr); + if (TraceLevel >= 2 && EF) + EF->__sanitizer_print_stack_trace(); + } +} + +// Crash on a single malloc that exceeds the rss limit. +void Fuzzer::HandleMalloc(size_t Size) { + if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb) + return; + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), + Size); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. +} + +Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options) + : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { + SetDeathCallback(); + InitializeTraceState(); + assert(!F); + F = this; + TPC.ResetMaps(); + ResetCoverage(); + IsMyThread = true; + if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) + EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + TPC.SetUseCounters(Options.UseCounters); + TPC.SetUseValueProfile(Options.UseValueProfile); + TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + + if (Options.Verbosity) + TPC.PrintModuleInfo(); + if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) + EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); + MaxInputLen = MaxMutationLen = Options.MaxLen; + AllocateCurrentUnitData(); + CurrentUnitSize = 0; + memset(BaseSha1, 0, sizeof(BaseSha1)); +} + +Fuzzer::~Fuzzer() { } + +void Fuzzer::AllocateCurrentUnitData() { + if (CurrentUnitData || MaxInputLen == 0) return; + CurrentUnitData = new uint8_t[MaxInputLen]; +} + +void Fuzzer::SetDeathCallback() { + CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback); + EF->__sanitizer_set_death_callback(StaticDeathCallback); +} + +void Fuzzer::StaticDeathCallback() { + assert(F); + F->DeathCallback(); +} + +static void WarnOnUnsuccessfullMerge(bool DoWarn) { + if (!DoWarn) return; + Printf( + "***\n" + "***\n" + "***\n" + "*** NOTE: merge did not succeed due to a failure on one of the inputs.\n" + "*** You will need to filter out crashes from the corpus, e.g. like this:\n" + "*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n" + "*** Future versions may have crash-resistant merge, stay tuned.\n" + "***\n" + "***\n" + "***\n"); +} + +void Fuzzer::DumpCurrentUnit(const char *Prefix) { + WarnOnUnsuccessfullMerge(InMergeMode); + if (!CurrentUnitData) return; // Happens when running individual inputs. + MD.PrintMutationSequence(); + Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); + size_t UnitSize = CurrentUnitSize; + if (UnitSize <= kMaxUnitSizeToPrint) { + PrintHexArray(CurrentUnitData, UnitSize, "\n"); + PrintASCII(CurrentUnitData, UnitSize, "\n"); + } + WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize}, + Prefix); +} + +NO_SANITIZE_MEMORY +void Fuzzer::DeathCallback() { + DumpCurrentUnit("crash-"); + PrintFinalStats(); +} + +void Fuzzer::StaticAlarmCallback() { + assert(F); + F->AlarmCallback(); +} + +void Fuzzer::StaticCrashSignalCallback() { + assert(F); + F->CrashCallback(); +} + +void Fuzzer::StaticInterruptCallback() { + assert(F); + F->InterruptCallback(); +} + +void Fuzzer::CrashCallback() { + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + Printf("NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + exit(Options.ErrorExitCode); +} + +void Fuzzer::InterruptCallback() { + Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + _Exit(0); // Stop right now, don't perform any at-exit actions. +} + +NO_SANITIZE_MEMORY +void Fuzzer::AlarmCallback() { + assert(Options.UnitTimeoutSec > 0); + if (!InFuzzingThread()) return; + if (!RunningCB) + return; // We have not started running units yet. + size_t Seconds = + duration_cast(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) + return; + if (Options.Verbosity >= 2) + Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + DumpCurrentUnit("timeout-"); + Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Seconds); + if (EF->__sanitizer_print_stack_trace) + EF->__sanitizer_print_stack_trace(); + Printf("SUMMARY: libFuzzer: timeout\n"); + PrintFinalStats(); + _Exit(Options.TimeoutExitCode); // Stop right now. + } +} + +void Fuzzer::RssLimitCallback() { + Printf( + "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + if (EF->__sanitizer_print_memory_profile) + EF->__sanitizer_print_memory_profile(95); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. +} + +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { + size_t ExecPerSec = execPerSec(); + if (Options.OutputCSV) { + static bool csvHeaderPrinted = false; + if (!csvHeaderPrinted) { + csvHeaderPrinted = true; + Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); + } + Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, + MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits, + MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where); + } + + if (!Options.Verbosity) + return; + Printf("#%zd\t%s", TotalNumberOfRuns, Where); + if (MaxCoverage.BlockCoverage) + Printf(" cov: %zd", MaxCoverage.BlockCoverage); + if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge()) + Printf(" vp: %zd", N); + if (size_t N = TPC.GetTotalPCCoverage()) + Printf(" cov: %zd", N); + if (auto TB = MaxCoverage.CounterBitmapBits) + Printf(" bits: %zd", TB); + if (size_t N = Corpus.NumFeatures()) + Printf( " ft: %zd", N); + if (MaxCoverage.CallerCalleeCoverage) + Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage); + if (!Corpus.empty()) { + Printf(" corp: %zd", Corpus.NumActiveUnits()); + if (size_t N = Corpus.SizeInBytes()) { + if (N < (1<<14)) + Printf("/%zdb", N); + else if (N < (1 << 24)) + Printf("/%zdKb", N >> 10); + else + Printf("/%zdMb", N >> 20); + } + } + if (Units) + Printf(" units: %zd", Units); + + Printf(" exec/s: %zd", ExecPerSec); + Printf(" rss: %zdMb", GetPeakRSSMb()); + Printf("%s", End); +} + +void Fuzzer::PrintFinalStats() { + if (Options.PrintCoverage) + TPC.PrintCoverage(); + if (Options.DumpCoverage) + TPC.DumpCoverage(); + if (Options.PrintCorpusStats) + Corpus.PrintStats(); + if (!Options.PrintFinalStats) return; + size_t ExecPerSec = execPerSec(); + Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); + Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); + Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); + Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); +} + +void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { + assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0. + assert(MaxInputLen); + this->MaxInputLen = MaxInputLen; + this->MaxMutationLen = MaxInputLen; + AllocateCurrentUnitData(); + Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen); +} + +void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { + assert(MaxMutationLen && MaxMutationLen <= MaxInputLen); + this->MaxMutationLen = MaxMutationLen; +} + +void Fuzzer::CheckExitOnSrcPosOrItem() { + if (!Options.ExitOnSrcPos.empty()) { + static auto *PCsSet = new std::set; + for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) { + uintptr_t PC = TPC.GetPC(i); + if (!PC) continue; + if (!PCsSet->insert(PC).second) continue; + std::string Descr = DescribePC("%L", PC); + if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) { + Printf("INFO: found line matching '%s', exiting.\n", + Options.ExitOnSrcPos.c_str()); + _Exit(0); + } + } + } + if (!Options.ExitOnItem.empty()) { + if (Corpus.HasUnit(Options.ExitOnItem)) { + Printf("INFO: found item with checksum '%s', exiting.\n", + Options.ExitOnItem.c_str()); + _Exit(0); + } + } +} + +void Fuzzer::RereadOutputCorpus(size_t MaxSize) { + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; + std::vector AdditionalCorpus; + 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 (auto &U : AdditionalCorpus) { + if (U.size() > MaxSize) + U.resize(MaxSize); + if (!Corpus.HasUnit(U)) { + if (size_t NumFeatures = RunOne(U)) { + CheckExitOnSrcPosOrItem(); + Corpus.AddToCorpus(U, NumFeatures); + Reloaded = true; + } + } + } + if (Reloaded) + PrintStats("RELOAD"); +} + +void Fuzzer::ShuffleCorpus(UnitVector *V) { + std::random_shuffle(V->begin(), V->end(), MD.GetRand()); + if (Options.PreferSmall) + std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) { + return A.size() < B.size(); + }); +} + +void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) { + Printf("#0\tREAD units: %zd\n", InitialCorpus->size()); + if (Options.ShuffleAtStartUp) + ShuffleCorpus(InitialCorpus); + + // Test the callback with empty input and never try it again. + uint8_t dummy; + ExecuteCallback(&dummy, 0); + + for (const auto &U : *InitialCorpus) { + if (size_t NumFeatures = RunOne(U)) { + CheckExitOnSrcPosOrItem(); + Corpus.AddToCorpus(U, NumFeatures); + if (Options.Verbosity >= 2) + Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size()); + } + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); + } + PrintStats("INITED"); + if (Corpus.empty()) { + Printf("ERROR: no interesting inputs were found. " + "Is the code instrumented for coverage? Exiting.\n"); + exit(1); + } +} + +size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { + if (!Size) return 0; + TotalNumberOfRuns++; + + ExecuteCallback(Data, Size); + + size_t Res = 0; + if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool { + return Corpus.AddFeature(Feature, Size, Options.Shrink); + })) + Res = NumFeatures; + + if (!TPC.UsingTracePcGuard()) { + if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap)) + Res = 1; + if (!Res && RecordMaxCoverage(&MaxCoverage)) + Res = 1; + } + + auto TimeOfUnit = + duration_cast(UnitStopTime - UnitStartTime).count(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) + PrintStats("pulse "); + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); + } + return Res; +} + +size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { + assert(InFuzzingThread()); + *Data = CurrentUnitData; + return CurrentUnitSize; +} + +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { + assert(InFuzzingThread()); + // We copy the contents of Unit into a separate heap buffer + // so that we reliably find buffer overflows in it. + uint8_t *DataCopy = new uint8_t[Size]; + memcpy(DataCopy, Data, Size); + if (CurrentUnitData && CurrentUnitData != Data) + memcpy(CurrentUnitData, Data, Size); + CurrentUnitSize = Size; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + ResetCounters(); // Reset coverage right before the callback. + TPC.ResetMaps(); + RunningCB = true; + int Res = CB(DataCopy, Size); + RunningCB = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + CurrentUnitSize = 0; + delete[] DataCopy; +} + +void Fuzzer::WriteToOutputCorpus(const Unit &U) { + if (Options.OnlyASCII) + assert(IsASCII(U)); + if (Options.OutputCorpus.empty()) + return; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written to %s\n", Path.c_str()); +} + +void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { + if (!Options.SaveArtifacts) + return; + std::string Path = Options.ArtifactPrefix + Prefix + Hash(U); + if (!Options.ExactArtifactPath.empty()) + Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix. + WriteToFile(U, Path); + Printf("artifact_prefix='%s'; Test unit written to %s\n", + Options.ArtifactPrefix.c_str(), Path.c_str()); + if (U.size() <= kMaxUnitSizeToPrint) + Printf("Base64: %s\n", Base64(U).c_str()); +} + +void Fuzzer::PrintStatusForNewUnit(const Unit &U) { + if (!Options.PrintNEW) + return; + PrintStats("NEW ", ""); + if (Options.Verbosity) { + Printf(" L: %zd ", U.size()); + MD.PrintMutationSequence(); + Printf("\n"); + } +} + +void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { + II->NumSuccessfullMutations++; + MD.RecordSuccessfulMutationSequence(); + PrintStatusForNewUnit(U); + WriteToOutputCorpus(U); + NumberOfNewUnitsAdded++; + TPC.PrintNewPCs(); +} + +// Finds minimal number of units in 'Extra' that add coverage to 'Initial'. +// We do it by actually executing the units, sometimes more than once, +// because we may be using different coverage-like signals and the only +// common thing between them is that we can say "this unit found new stuff". +UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial, + const UnitVector &Extra) { + UnitVector Res = Extra; + UnitVector Tmp; + size_t OldSize = Res.size(); + for (int Iter = 0; Iter < 10; Iter++) { + ShuffleCorpus(&Res); + TPC.ResetMaps(); + Corpus.ResetFeatureSet(); + ResetCoverage(); + + for (auto &U : Initial) { + TPC.ResetMaps(); + RunOne(U); + } + + Tmp.clear(); + for (auto &U : Res) { + TPC.ResetMaps(); + if (RunOne(U)) + Tmp.push_back(U); + } + + char Stat[7] = "MIN "; + Stat[3] = '0' + Iter; + PrintStats(Stat, "\n", Tmp.size()); + + size_t NewSize = Tmp.size(); + assert(NewSize <= OldSize); + Res.swap(Tmp); + + if (NewSize + 5 >= OldSize) + break; + OldSize = NewSize; + } + return Res; +} + +void Fuzzer::Merge(const std::vector &Corpora) { + if (Corpora.size() <= 1) { + Printf("Merge requires two or more corpus dirs\n"); + return; + } + InMergeMode = true; + std::vector ExtraCorpora(Corpora.begin() + 1, Corpora.end()); + + assert(MaxInputLen > 0); + UnitVector Initial, Extra; + ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen, + true); + for (auto &C : ExtraCorpora) + ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true); + + if (!Initial.empty()) { + Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size()); + Initial = FindExtraUnits({}, Initial); + } + + Printf("=== Merging extra %zd units\n", Extra.size()); + auto Res = FindExtraUnits(Initial, Extra); + + for (auto &U: Res) + WriteToOutputCorpus(U); + + Printf("=== Merge: written %zd units\n", Res.size()); +} + +// Tries detecting a memory leak on the particular input that we have just +// executed before calling this function. +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { + if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. + if (!Options.DetectLeaks) return; + if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) || + !(EF->__lsan_do_recoverable_leak_check)) + return; // No lsan. + // Run the target once again, but with lsan disabled so that if there is + // a real leak we do not report it twice. + EF->__lsan_disable(); + ExecuteCallback(Data, Size); + EF->__lsan_enable(); + if (!HasMoreMallocsThanFrees) return; // a leak is unlikely. + if (NumberOfLeakDetectionAttempts++ > 1000) { + Options.DetectLeaks = false; + Printf("INFO: libFuzzer disabled leak detection after every mutation.\n" + " Most likely the target function accumulates allocated\n" + " memory in a global state w/o actually leaking it.\n" + " You may try running this binary with -trace_malloc=[12]" + " to get a trace of mallocs and frees.\n" + " If LeakSanitizer is enabled in this process it will still\n" + " run on the process shutdown.\n"); + return; + } + // Now perform the actual lsan pass. This is expensive and we must ensure + // we don't call it too often. + if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. + if (DuringInitialCorpusExecution) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + CurrentUnitSize = Size; + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + } +} + +void Fuzzer::MutateAndTestOne() { + MD.StartMutationSequence(); + + auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + const auto &U = II.U; + memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); + assert(CurrentUnitData); + size_t Size = U.size(); + assert(Size <= MaxInputLen && "Oversized Unit"); + memcpy(CurrentUnitData, U.data(), Size); + + assert(MaxMutationLen > 0); + + for (int i = 0; i < Options.MutateDepth; i++) { + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + break; + size_t NewSize = 0; + NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen); + assert(NewSize > 0 && "Mutator returned empty unit"); + assert(NewSize <= MaxMutationLen && "Mutator return overisized unit"); + Size = NewSize; + if (i == 0) + StartTraceRecording(); + II.NumExecutedMutations++; + if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) { + Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures, + /*MayDeleteFile=*/true); + ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + CheckExitOnSrcPosOrItem(); + } + StopTraceRecording(); + TryDetectingAMemoryLeak(CurrentUnitData, Size, + /*DuringInitialCorpusExecution*/ false); + } +} + +void Fuzzer::ResetCoverage() { + ResetEdgeCoverage(); + MaxCoverage.Reset(); + PrepareCounters(&MaxCoverage); +} + +void Fuzzer::Loop() { + system_clock::time_point LastCorpusReload = system_clock::now(); + if (Options.DoCrossOver) + MD.SetCorpus(&Corpus); + while (true) { + auto Now = system_clock::now(); + if (duration_cast(Now - LastCorpusReload).count() >= + Options.ReloadIntervalSec) { + RereadOutputCorpus(MaxInputLen); + LastCorpusReload = system_clock::now(); + } + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + break; + if (TimedOut()) break; + // Perform several mutations and runs. + MutateAndTestOne(); + } + + PrintStats("DONE ", "\n"); + MD.PrintRecommendedDictionary(); +} + +void Fuzzer::MinimizeCrashLoop(const Unit &U) { + if (U.size() <= 2) return; + while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { + MD.StartMutationSequence(); + memcpy(CurrentUnitData, U.data(), U.size()); + for (int i = 0; i < Options.MutateDepth; i++) { + size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); + assert(NewSize > 0 && NewSize <= MaxMutationLen); + RunOne(CurrentUnitData, NewSize); + TryDetectingAMemoryLeak(CurrentUnitData, NewSize, + /*DuringInitialCorpusExecution*/ false); + } + } +} + +} // namespace fuzzer + +extern "C" { + +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + assert(fuzzer::F); + return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); +} +} // extern "C" diff --git a/test/fuzz_test/Fuzzer/FuzzerMain.cpp b/test/fuzz_test/Fuzzer/FuzzerMain.cpp new file mode 100644 index 00000000..af865720 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMain.cpp @@ -0,0 +1,21 @@ +//===- FuzzerMain.cpp - main() function and flags -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// main() and flags. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" + +extern "C" { +// This function should be defined by the user. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +} // extern "C" + +int main(int argc, char **argv) { + return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput); +} diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.cpp b/test/fuzz_test/Fuzzer/FuzzerMerge.cpp new file mode 100644 index 00000000..84660e0f --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMerge.cpp @@ -0,0 +1,261 @@ +//===- FuzzerMerge.cpp - merging corpora ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Merging corpora. +//===----------------------------------------------------------------------===// + +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMerge.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include + +namespace fuzzer { + +bool Merger::Parse(const std::string &Str, bool ParseCoverage) { + std::istringstream SS(Str); + return Parse(SS, ParseCoverage); +} + +void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { + if (!Parse(IS, ParseCoverage)) { + Printf("MERGE: failed to parse the control file (unexpected error)\n"); + exit(1); + } +} + +// The control file example: +// +// 3 # The number of inputs +// 1 # The number of inputs in the first corpus, <= the previous number +// file0 +// file1 +// file2 # One file name per line. +// STARTED 0 123 # FileID, file size +// DONE 0 1 4 6 8 # FileID COV1 COV2 ... +// STARTED 1 456 # If DONE is missing, the input crashed while processing. +// STARTED 2 567 +// DONE 2 8 9 +bool Merger::Parse(std::istream &IS, bool ParseCoverage) { + LastFailure.clear(); + std::string Line; + + // Parse NumFiles. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L1(Line); + size_t NumFiles = 0; + L1 >> NumFiles; + if (NumFiles == 0 || NumFiles > 10000000) return false; + + // Parse NumFilesInFirstCorpus. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L2(Line); + NumFilesInFirstCorpus = NumFiles + 1; + L2 >> NumFilesInFirstCorpus; + if (NumFilesInFirstCorpus > NumFiles) return false; + + // Parse file names. + Files.resize(NumFiles); + for (size_t i = 0; i < NumFiles; i++) + if (!std::getline(IS, Files[i].Name, '\n')) + return false; + + // Parse STARTED and DONE lines. + size_t ExpectedStartMarker = 0; + const size_t kInvalidStartMarker = -1; + size_t LastSeenStartMarker = kInvalidStartMarker; + while (std::getline(IS, Line, '\n')) { + std::istringstream ISS1(Line); + std::string Marker; + size_t N; + ISS1 >> Marker; + ISS1 >> N; + if (Marker == "STARTED") { + // STARTED FILE_ID FILE_SIZE + if (ExpectedStartMarker != N) + return false; + ISS1 >> Files[ExpectedStartMarker].Size; + LastSeenStartMarker = ExpectedStartMarker; + assert(ExpectedStartMarker < Files.size()); + ExpectedStartMarker++; + } else if (Marker == "DONE") { + // DONE FILE_SIZE COV1 COV2 COV3 ... + size_t CurrentFileIdx = N; + if (CurrentFileIdx != LastSeenStartMarker) + return false; + LastSeenStartMarker = kInvalidStartMarker; + if (ParseCoverage) { + auto &V = Files[CurrentFileIdx].Features; + V.clear(); + while (ISS1 >> std::hex >> N) + V.push_back(N); + std::sort(V.begin(), V.end()); + } + } else { + return false; + } + } + if (LastSeenStartMarker != kInvalidStartMarker) + LastFailure = Files[LastSeenStartMarker].Name; + + FirstNotProcessedFile = ExpectedStartMarker; + return true; +} + +// Decides which files need to be merged (add thost to NewFiles). +// Returns the number of new features added. +size_t Merger::Merge(std::vector *NewFiles) { + NewFiles->clear(); + assert(NumFilesInFirstCorpus <= Files.size()); + std::set AllFeatures; + + // What features are in the initial corpus? + for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { + auto &Cur = Files[i].Features; + AllFeatures.insert(Cur.begin(), Cur.end()); + } + size_t InitialNumFeatures = AllFeatures.size(); + + // Remove all features that we already know from all other inputs. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + auto &Cur = Files[i].Features; + std::vector Tmp; + std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), + AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); + Cur.swap(Tmp); + } + + // Sort. Give preference to + // * smaller files + // * files with more features. + std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(), + [&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool { + if (a.Size != b.Size) + return a.Size < b.Size; + return a.Features.size() > b.Features.size(); + }); + + // One greedy pass: add the file's features to AllFeatures. + // If new features were added, add this file to NewFiles. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + auto &Cur = Files[i].Features; + // Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(), + // Files[i].Size, Cur.size()); + size_t OldSize = AllFeatures.size(); + AllFeatures.insert(Cur.begin(), Cur.end()); + if (AllFeatures.size() > OldSize) + NewFiles->push_back(Files[i].Name); + } + return AllFeatures.size() - InitialNumFeatures; +} + +// Inner process. May crash if the target crashes. +void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { + Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, false); + IF.close(); + if (!M.LastFailure.empty()) + Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", + M.LastFailure.c_str()); + + Printf("MERGE-INNER: %zd total files;" + " %zd processed earlier; will process %zd files now\n", + M.Files.size(), M.FirstNotProcessedFile, + M.Files.size() - M.FirstNotProcessedFile); + + std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); + for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { + auto U = FileToVector(M.Files[i].Name); + if (U.size() > MaxInputLen) { + U.resize(MaxInputLen); + U.shrink_to_fit(); + } + std::ostringstream StartedLine; + // Write the pre-run marker. + OF << "STARTED " << std::dec << i << " " << U.size() << "\n"; + OF.flush(); // Flush is important since ExecuteCommand may crash. + // Run. + TPC.ResetMaps(); + ExecuteCallback(U.data(), U.size()); + // Collect coverage. + std::set Features; + TPC.CollectFeatures([&](size_t Feature) -> bool { + Features.insert(Feature); + return true; + }); + // Show stats. + TotalNumberOfRuns++; + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) + PrintStats("pulse "); + // Write the post-run marker and the coverage. + OF << "DONE " << i; + for (size_t F : Features) + OF << " " << std::hex << F; + OF << "\n"; + } +} + +// Outer process. Does not call the target code and thus sohuld not fail. +void Fuzzer::CrashResistantMerge(const std::vector &Args, + const std::vector &Corpora) { + if (Corpora.size() <= 1) { + Printf("Merge requires two or more corpus dirs\n"); + return; + } + std::vector AllFiles; + ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true); + size_t NumFilesInFirstCorpus = AllFiles.size(); + for (size_t i = 1; i < Corpora.size(); i++) + ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true); + Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n", + AllFiles.size(), NumFilesInFirstCorpus); + std::string CFPath = + "libFuzzerTemp." + std::to_string(GetPid()) + ".txt"; + // Write the control file. + RemoveFile(CFPath); + std::ofstream ControlFile(CFPath); + ControlFile << AllFiles.size() << "\n"; + ControlFile << NumFilesInFirstCorpus << "\n"; + for (auto &Path: AllFiles) + ControlFile << Path << "\n"; + ControlFile.close(); + + // Execute the inner process untill it passes. + // Every inner process should execute at least one input. + std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags"); + for (size_t i = 1; i <= AllFiles.size(); i++) { + Printf("MERGE-OUTER: attempt %zd\n", i); + auto ExitCode = + ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath); + if (!ExitCode) { + Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i); + break; + } + } + // Read the control file and do the merge. + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, true); + IF.close(); + std::vector NewFiles; + size_t NumNewFeatures = M.Merge(&NewFiles); + Printf("MERGE-OUTER: %zd new files with %zd new features added\n", + NewFiles.size(), NumNewFeatures); + for (auto &F: NewFiles) + WriteToOutputCorpus(FileToVector(F)); + // We are done, delete the control file. + RemoveFile(CFPath); +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.h b/test/fuzz_test/Fuzzer/FuzzerMerge.h new file mode 100644 index 00000000..8a2fe5d7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMerge.h @@ -0,0 +1,70 @@ +//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Merging Corpora. +// +// The task: +// Take the existing corpus (possibly empty) and merge new inputs into +// it so that only inputs with new coverage ('features') are added. +// The process should tolerate the crashes, OOMs, leaks, etc. +// +// Algorithm: +// The outter process collects the set of files and writes their names +// into a temporary "control" file, then repeatedly launches the inner +// process until all inputs are processed. +// The outer process does not actually execute the target code. +// +// The inner process reads the control file and sees a) list of all the inputs +// and b) the last processed input. Then it starts processing the inputs one +// by one. Before processing every input it writes one line to control file: +// STARTED INPUT_ID INPUT_SIZE +// After processing an input it write another line: +// DONE INPUT_ID Feature1 Feature2 Feature3 ... +// If a crash happens while processing an input the last line in the control +// file will be "STARTED INPUT_ID" and so the next process will know +// where to resume. +// +// Once all inputs are processed by the innner process(es) the outer process +// reads the control files and does the merge based entirely on the contents +// of control file. +// It uses a single pass greedy algorithm choosing first the smallest inputs +// within the same size the inputs that have more new features. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MERGE_H +#define LLVM_FUZZER_MERGE_H + +#include "FuzzerDefs.h" + +#include +#include + +namespace fuzzer { + +struct MergeFileInfo { + std::string Name; + size_t Size = 0; + std::vector Features; +}; + +struct Merger { + std::vector Files; + size_t NumFilesInFirstCorpus = 0; + size_t FirstNotProcessedFile = 0; + std::string LastFailure; + + bool Parse(std::istream &IS, bool ParseCoverage); + bool Parse(const std::string &Str, bool ParseCoverage); + void ParseOrExit(std::istream &IS, bool ParseCoverage); + size_t Merge(std::vector *NewFiles); +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MERGE_H diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.cpp b/test/fuzz_test/Fuzzer/FuzzerMutate.cpp new file mode 100644 index 00000000..80b7a5f9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMutate.cpp @@ -0,0 +1,527 @@ +//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Mutate a test input. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerOptions.h" + +namespace fuzzer { + +const size_t Dictionary::kMaxDictSize; + +static void PrintASCII(const Word &W, const char *PrintAfter) { + PrintASCII(W.data(), W.size(), PrintAfter); +} + +MutationDispatcher::MutationDispatcher(Random &Rand, + const FuzzingOptions &Options) + : Rand(Rand), Options(Options) { + DefaultMutators.insert( + DefaultMutators.begin(), + { + {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"}, + {&MutationDispatcher::Mutate_InsertByte, "InsertByte"}, + {&MutationDispatcher::Mutate_InsertRepeatedBytes, + "InsertRepeatedBytes"}, + {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"}, + {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"}, + {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"}, + {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"}, + {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"}, + {&MutationDispatcher::Mutate_CopyPart, "CopyPart"}, + {&MutationDispatcher::Mutate_CrossOver, "CrossOver"}, + {&MutationDispatcher::Mutate_AddWordFromManualDictionary, + "ManualDict"}, + {&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, + "TempAutoDict"}, + {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, + "PersAutoDict"}, + }); + if(Options.UseCmp) + DefaultMutators.push_back( + {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"}); + + if (EF->LLVMFuzzerCustomMutator) + Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"}); + else + Mutators = DefaultMutators; + + if (EF->LLVMFuzzerCustomCrossOver) + Mutators.push_back( + {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); +} + +static char RandCh(Random &Rand) { + 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) { + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); +} + +size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (!Corpus || Corpus->size() < 2 || Size == 0) + return 0; + size_t Idx = Rand(Corpus->size()); + const Unit &Other = (*Corpus)[Idx]; + if (Other.empty()) + return 0; + MutateInPlaceHere.resize(MaxSize); + auto &U = MutateInPlaceHere; + size_t NewSize = EF->LLVMFuzzerCustomCrossOver( + Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); + if (!NewSize) + return 0; + assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; +} + +size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + assert(Size); + size_t ShuffleAmount = + Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size. + size_t ShuffleStart = Rand(Size - ShuffleAmount); + assert(ShuffleStart + ShuffleAmount <= Size); + std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, + Rand); + return Size; +} + +size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + assert(Size); + if (Size == 1) return 0; + size_t N = Rand(Size / 2) + 1; + assert(N < Size); + size_t Idx = Rand(Size - N + 1); + // Erase Data[Idx:Idx+N]. + memmove(Data + Idx, Data + Idx + N, Size - Idx - N); + // Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx); + return Size - N; +} + +size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size >= MaxSize) return 0; + size_t Idx = Rand(Size + 1); + // Insert new value at Data[Idx]. + memmove(Data + Idx + 1, Data + Idx, Size - Idx); + Data[Idx] = RandCh(Rand); + return Size + 1; +} + +size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, + size_t Size, + size_t MaxSize) { + const size_t kMinBytesToInsert = 3; + if (Size + kMinBytesToInsert >= MaxSize) return 0; + size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128); + size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert; + assert(Size + N <= MaxSize && N); + size_t Idx = Rand(Size + 1); + // Insert new values at Data[Idx]. + memmove(Data + Idx + N, Data + Idx, Size - Idx); + // Give preference to 0x00 and 0xff. + 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; +} + +size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] = RandCh(Rand); + return Size; +} + +size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] ^= 1 << Rand(8); + return Size; +} + +size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, + size_t Size, + size_t MaxSize) { + return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); +} + +size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize); +} + +size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, + size_t MaxSize, + DictionaryEntry &DE) { + const Word &W = DE.GetW(); + bool UsePositionHint = DE.HasPositionHint() && + DE.GetPositionHint() + W.size() < Size && + Rand.RandBool(); + if (Rand.RandBool()) { // Insert W. + if (Size + W.size() > MaxSize) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1); + memmove(Data + Idx + W.size(), Data + Idx, Size - Idx); + memcpy(Data + Idx, W.data(), W.size()); + Size += W.size(); + } else { // Overwrite some bytes with W. + if (W.size() > Size) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); + memcpy(Data + Idx, W.data(), W.size()); + } + return Size; +} + +// Somewhere in the past we have observed a comparison instructions +// with arguments Arg1 Arg2. This function tries to guess a dictionary +// entry that will satisfy that comparison. +// It first tries to find one of the arguments (possibly swapped) in the +// input and if it succeeds it creates a DE with a position hint. +// Otherwise it creates a DE with one of the arguments w/o a position hint. +template +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + T Arg1, T Arg2, const uint8_t *Data, size_t Size) { + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + bool HandleFirst = Rand.RandBool(); + T ExistingBytes, DesiredBytes; + Word W; + const uint8_t *End = Data + Size; + for (int Arg = 0; Arg < 2; Arg++) { + ExistingBytes = HandleFirst ? Arg1 : Arg2; + DesiredBytes = HandleFirst ? Arg2 : Arg1; + DesiredBytes += Rand(-1, 1); + if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes); + if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes); + HandleFirst = !HandleFirst; + W.Set(reinterpret_cast(&DesiredBytes), sizeof(T)); + const size_t kMaxNumPositions = 8; + size_t Positions[kMaxNumPositions]; + size_t NumPositions = 0; + for (const uint8_t *Cur = Data; + Cur < End && NumPositions < kMaxNumPositions; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T)); + if (!Cur) break; + Positions[NumPositions++] = Cur - Data; + } + if (!NumPositions) break; + return DictionaryEntry(W, Positions[Rand(NumPositions)]); + } + DictionaryEntry DE(W); + return DE; +} + +size_t MutationDispatcher::Mutate_AddWordFromTORC( + uint8_t *Data, size_t Size, size_t MaxSize) { + Word W; + DictionaryEntry DE; + if (Rand.RandBool()) { + auto X = TPC.TORC8.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } else { + 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); + } + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DictionaryEntry &DERef = + CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ % + kCmpDictionaryEntriesDequeSize]; + DERef = DE; + CurrentDictionaryEntrySequence.push_back(&DERef); + return Size; +} + +size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize); +} + +size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data, + size_t Size, size_t MaxSize) { + if (Size > MaxSize) return 0; + if (D.empty()) return 0; + DictionaryEntry &DE = D[Rand(D.size())]; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DE.IncUseCount(); + CurrentDictionaryEntrySequence.push_back(&DE); + return Size; +} + +// Overwrites part of To[0,ToSize) with a part of From[0,FromSize). +// Returns ToSize. +size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize) { + // Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize). + size_t ToBeg = Rand(ToSize); + size_t CopySize = Rand(ToSize - ToBeg) + 1; + assert(ToBeg + CopySize <= ToSize); + CopySize = std::min(CopySize, FromSize); + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + memmove(To + ToBeg, From + FromBeg, CopySize); + return ToSize; +} + +// Inserts part of From[0,ToSize) into To. +// Returns new size of To on success or 0 on failure. +size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize, + size_t MaxToSize) { + if (ToSize >= MaxToSize) return 0; + size_t AvailableSpace = MaxToSize - ToSize; + size_t MaxCopySize = std::min(AvailableSpace, FromSize); + size_t CopySize = Rand(MaxCopySize) + 1; + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + size_t ToInsertPos = Rand(ToSize + 1); + assert(ToInsertPos + CopySize <= MaxToSize); + size_t TailSize = ToSize - ToInsertPos; + if (To == From) { + MutateInPlaceHere.resize(MaxToSize); + memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize); + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize); + } else { + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, From + FromBeg, CopySize); + } + return ToSize + CopySize; +} + +size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + if (Rand.RandBool()) + return CopyPartOf(Data, Size, Data, Size); + else + return InsertPartOf(Data, Size, Data, Size, MaxSize); +} + +size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + size_t B = Rand(Size); + while (B < Size && !isdigit(Data[B])) B++; + if (B == Size) return 0; + size_t E = B; + while (E < Size && isdigit(Data[E])) E++; + assert(B < E); + // now we have digits in [B, E). + // strtol and friends don't accept non-zero-teminated data, parse it manually. + uint64_t Val = Data[B] - '0'; + for (size_t i = B + 1; i < E; i++) + Val = Val * 10 + Data[i] - '0'; + + // Mutate the integer value. + switch(Rand(5)) { + case 0: Val++; break; + case 1: Val--; break; + case 2: Val /= 2; break; + case 3: Val *= 2; break; + case 4: Val = Rand(Val * Val); break; + default: assert(0); + } + // Just replace the bytes with the new ones, don't bother moving bytes. + for (size_t i = B; i < E; i++) { + size_t Idx = E + B - i - 1; + assert(Idx >= B && Idx < E); + Data[Idx] = (Val % 10) + '0'; + Val /= 10; + } + return Size; +} + +template +size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { + if (Size < sizeof(T)) return 0; + size_t Off = Rand(Size - sizeof(T) + 1); + assert(Off + sizeof(T) <= Size); + T Val; + if (Off < 64 && !Rand(4)) { + Val = Size; + if (Rand.RandBool()) + Val = Bswap(Val); + } else { + memcpy(&Val, Data + Off, sizeof(Val)); + T Add = Rand(21); + Add -= 10; + if (Rand.RandBool()) + Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. + else + Val = Val + Add; // Add assuming current endiannes. + if (Add == 0 || Rand.RandBool()) // Maybe negate. + Val = -Val; + } + memcpy(Data + Off, &Val, sizeof(Val)); + return Size; +} + +size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, + size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + switch (Rand(4)) { + case 3: return ChangeBinaryInteger(Data, Size, Rand); + case 2: return ChangeBinaryInteger(Data, Size, Rand); + case 1: return ChangeBinaryInteger(Data, Size, Rand); + case 0: return ChangeBinaryInteger(Data, Size, Rand); + default: assert(0); + } + return 0; +} + +size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + if (Size > MaxSize) return 0; + if (!Corpus || Corpus->size() < 2 || Size == 0) return 0; + size_t Idx = Rand(Corpus->size()); + const Unit &O = (*Corpus)[Idx]; + if (O.empty()) return 0; + MutateInPlaceHere.resize(MaxSize); + auto &U = MutateInPlaceHere; + size_t NewSize = 0; + switch(Rand(3)) { + case 0: + NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size()); + break; + case 1: + NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize); + if (NewSize) + break; + // LLVM_FALLTHROUGH; + case 2: + NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); + break; + default: assert(0); + } + assert(NewSize > 0 && "CrossOver returned empty unit"); + assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; +} + +void MutationDispatcher::StartMutationSequence() { + CurrentMutatorSequence.clear(); + CurrentDictionaryEntrySequence.clear(); +} + +// Copy successful dictionary entries to PersistentAutoDictionary. +void MutationDispatcher::RecordSuccessfulMutationSequence() { + for (auto DE : CurrentDictionaryEntrySequence) { + // PersistentAutoDictionary.AddWithSuccessCountOne(DE); + DE->IncSuccessCount(); + // Linear search is fine here as this happens seldom. + if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) + PersistentAutoDictionary.push_back({DE->GetW(), 1}); + } +} + +void MutationDispatcher::PrintRecommendedDictionary() { + std::vector V; + for (auto &DE : PersistentAutoDictionary) + if (!ManualDictionary.ContainsWord(DE.GetW())) + V.push_back(DE); + if (V.empty()) return; + Printf("###### Recommended dictionary. ######\n"); + for (auto &DE: V) { + Printf("\""); + PrintASCII(DE.GetW(), "\""); + Printf(" # Uses: %zd\n", DE.GetUseCount()); + } + Printf("###### End of recommended dictionary. ######\n"); +} + +void MutationDispatcher::PrintMutationSequence() { + Printf("MS: %zd ", CurrentMutatorSequence.size()); + for (auto M : CurrentMutatorSequence) + Printf("%s-", M.Name); + if (!CurrentDictionaryEntrySequence.empty()) { + Printf(" DE: "); + for (auto DE : CurrentDictionaryEntrySequence) { + Printf("\""); + PrintASCII(DE->GetW(), "\"-"); + } + } +} + +size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + return MutateImpl(Data, Size, MaxSize, Mutators); +} + +size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + return MutateImpl(Data, Size, MaxSize, DefaultMutators); +} + +// Mutates Data in place, returns new size. +size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, + size_t MaxSize, + const std::vector &Mutators) { + assert(MaxSize > 0); + if (Size == 0) { + for (size_t i = 0; i < MaxSize; i++) + Data[i] = RandCh(Rand); + if (Options.OnlyASCII) + ToASCII(Data, MaxSize); + return MaxSize; + } + assert(Size > 0); + // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), + // in which case they will return 0. + // Try several times before returning un-mutated data. + for (int Iter = 0; Iter < 100; Iter++) { + auto M = Mutators[Rand(Mutators.size())]; + size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize); + if (NewSize && NewSize <= MaxSize) { + if (Options.OnlyASCII) + ToASCII(Data, NewSize); + CurrentMutatorSequence.push_back(M); + return NewSize; + } + } + return std::min(Size, MaxSize); +} + +void MutationDispatcher::AddWordToManualDictionary(const Word &W) { + ManualDictionary.push_back( + {W, std::numeric_limits::max()}); +} + +void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) { + static const size_t kMaxAutoDictSize = 1 << 14; + if (TempAutoDictionary.size() >= kMaxAutoDictSize) return; + TempAutoDictionary.push_back(DE); +} + +void MutationDispatcher::ClearAutoDictionary() { + TempAutoDictionary.clear(); +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.h b/test/fuzz_test/Fuzzer/FuzzerMutate.h new file mode 100644 index 00000000..d3c0b001 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerMutate.h @@ -0,0 +1,145 @@ +//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::MutationDispatcher +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MUTATE_H +#define LLVM_FUZZER_MUTATE_H + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerRandom.h" + +namespace fuzzer { + +class MutationDispatcher { +public: + MutationDispatcher(Random &Rand, const FuzzingOptions &Options); + ~MutationDispatcher() {} + /// Indicate that we are about to start a new sequence of mutations. + void StartMutationSequence(); + /// Print the current sequence of mutations. + void PrintMutationSequence(); + /// Indicate that the current sequence of mutations was successfull. + void RecordSuccessfulMutationSequence(); + /// Mutates data by invoking user-provided mutator. + size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by invoking user-provided crossover. + size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by shuffling bytes. + size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by erasing bytes. + size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting a byte. + size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting several repeated bytes. + size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one byte. + size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one bit. + size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by copying/inserting a part of data into a different place. + size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the manual dictionary. + size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the temporary automatic dictionary. + size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the TORC. + size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the persistent automatic dictionary. + size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Tries to find an ASCII integer in Data, changes it to another ASCII int. + size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize); + /// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways. + size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize); + + /// CrossOver Data with some other element of the corpus. + size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations. + /// Returns the new size of data which could be up to MaxSize. + size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize); + /// Applies one of the default mutations. Provided as a service + /// to mutation authors. + size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Creates a cross-over of two pieces of Data, returns its size. + size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2, + size_t Size2, uint8_t *Out, size_t MaxOutSize); + + void AddWordToManualDictionary(const Word &W); + + void AddWordToAutoDictionary(DictionaryEntry DE); + void ClearAutoDictionary(); + void PrintRecommendedDictionary(); + + void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; } + + Random &GetRand() { return Rand; } + +private: + + struct Mutator { + size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); + const char *Name; + }; + + size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, + size_t MaxSize); + size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, + const std::vector &Mutators); + + size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize, size_t MaxToSize); + size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize); + size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize, + DictionaryEntry &DE); + + template + DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2, + const uint8_t *Data, size_t Size); + + Random &Rand; + const FuzzingOptions &Options; + + // Dictionary provided by the user via -dict=DICT_FILE. + Dictionary ManualDictionary; + // Temporary dictionary modified by the fuzzer itself, + // recreated periodically. + Dictionary TempAutoDictionary; + // Persistent dictionary modified by the fuzzer, consists of + // entries that led to successfull discoveries in the past mutations. + Dictionary PersistentAutoDictionary; + + std::vector CurrentMutatorSequence; + std::vector CurrentDictionaryEntrySequence; + + static const size_t kCmpDictionaryEntriesDequeSize = 16; + DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; + size_t CmpDictionaryEntriesDequeIdx = 0; + + const InputCorpus *Corpus = nullptr; + std::vector MutateInPlaceHere; + + std::vector Mutators; + std::vector DefaultMutators; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MUTATE_H diff --git a/test/fuzz_test/Fuzzer/FuzzerOptions.h b/test/fuzz_test/Fuzzer/FuzzerOptions.h new file mode 100644 index 00000000..34e93fc3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerOptions.h @@ -0,0 +1,68 @@ +//===- FuzzerOptions.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::FuzzingOptions +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_OPTIONS_H +#define LLVM_FUZZER_OPTIONS_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +struct FuzzingOptions { + int Verbosity = 1; + size_t MaxLen = 0; + int UnitTimeoutSec = 300; + int TimeoutExitCode = 77; + int ErrorExitCode = 77; + int MaxTotalTimeSec = 0; + int RssLimitMb = 0; + bool DoCrossOver = true; + int MutateDepth = 5; + bool UseCounters = false; + bool UseIndirCalls = true; + bool UseMemcmp = true; + bool UseMemmem = true; + bool UseCmp = false; + bool UseValueProfile = false; + bool Shrink = false; + int ReloadIntervalSec = 1; + bool ShuffleAtStartUp = true; + bool PreferSmall = true; + size_t MaxNumberOfRuns = -1L; + int ReportSlowUnits = 10; + bool OnlyASCII = false; + std::string OutputCorpus; + std::string ArtifactPrefix = "./"; + std::string ExactArtifactPath; + std::string ExitOnSrcPos; + std::string ExitOnItem; + bool SaveArtifacts = true; + bool PrintNEW = true; // Print a status line when new units are found; + bool OutputCSV = false; + bool PrintNewCovPcs = false; + bool PrintFinalStats = false; + bool PrintCorpusStats = false; + bool PrintCoverage = false; + bool DumpCoverage = false; + bool DetectLeaks = true; + int TraceMalloc = 0; + bool HandleAbrt = false; + bool HandleBus = false; + bool HandleFpe = false; + bool HandleIll = false; + bool HandleInt = false; + bool HandleSegv = false; + bool HandleTerm = false; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_OPTIONS_H diff --git a/test/fuzz_test/Fuzzer/FuzzerRandom.h b/test/fuzz_test/Fuzzer/FuzzerRandom.h new file mode 100644 index 00000000..b1be0bb9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerRandom.h @@ -0,0 +1,36 @@ +//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::Random +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_RANDOM_H +#define LLVM_FUZZER_RANDOM_H + +#include + +namespace fuzzer { +class Random { + public: + Random(unsigned int seed) : R(seed) {} + size_t Rand() { return R(); } + size_t RandBool() { return Rand() % 2; } + size_t operator()(size_t n) { return n ? Rand() % n : 0; } + intptr_t operator()(intptr_t From, intptr_t To) { + assert(From < To); + intptr_t RangeSize = To - From + 1; + return operator()(RangeSize) + From; + } + std::mt19937 &Get_mt19937() { return R; } + private: + std::mt19937 R; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_RANDOM_H diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp b/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp new file mode 100644 index 00000000..d2f8e811 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp @@ -0,0 +1,222 @@ +//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by adding anonymous namespace, adding an interface +// function fuzzer::ComputeSHA1() and removing unnecessary code. +// +// lib/Fuzzer can not use SHA1 implementation from openssl because +// openssl may not be available and because we may be fuzzing openssl itself. +// For the same reason we do not want to depend on SHA1 from LLVM tree. +//===----------------------------------------------------------------------===// + +#include "FuzzerSHA1.h" +#include "FuzzerDefs.h" + +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include +#include +#include + +namespace { // Added for LibFuzzer + +#ifdef __BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +#elif defined __LITTLE_ENDIAN__ +/* override */ +#elif defined __BYTE_ORDER +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +# endif +#else // ! defined __LITTLE_ENDIAN__ +# include // machine/endian.h +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +# endif +#endif + + +/* header */ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + uint32_t buffer[BLOCK_LENGTH/4]; + uint32_t state[HASH_LENGTH/4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t* sha1_result(sha1nfo *s); + + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void sha1_init(sha1nfo *s) { + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); +} + +void sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state[0]; + b=s->state[1]; + c=s->state[2]; + d=s->state[3]; + e=s->state[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; + s->buffer[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); +} + +void sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); +} + +uint8_t* sha1_result(sha1nfo *s) { + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i=0; i<5; i++) { + s->state[i]= + (((s->state[i])<<24)& 0xff000000) + | (((s->state[i])<<8) & 0x00ff0000) + | (((s->state[i])>>8) & 0x0000ff00) + | (((s->state[i])>>24)& 0x000000ff); + } +#endif + + // Return pointer to hash (20 characters) + return (uint8_t*) s->state; +} + +} // namespace; Added for LibFuzzer + +namespace fuzzer { + +// The rest is added for LibFuzzer +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) { + sha1nfo s; + sha1_init(&s); + sha1_write(&s, (const char*)Data, Len); + memcpy(Out, sha1_result(&s), HASH_LENGTH); +} + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) { + std::stringstream SS; + for (int i = 0; i < kSHA1NumBytes; i++) + SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i]; + return SS.str(); +} + +std::string Hash(const Unit &U) { + uint8_t Hash[kSHA1NumBytes]; + ComputeSHA1(U.data(), U.size(), Hash); + return Sha1ToString(Hash); +} + +} diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.h b/test/fuzz_test/Fuzzer/FuzzerSHA1.h new file mode 100644 index 00000000..3b5e6e80 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerSHA1.h @@ -0,0 +1,33 @@ +//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SHA1 utils. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHA1_H +#define LLVM_FUZZER_SHA1_H + +#include "FuzzerDefs.h" +#include +#include + +namespace fuzzer { + +// Private copy of SHA1 implementation. +static const int kSHA1NumBytes = 20; + +// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'. +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out); + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]); + +std::string Hash(const Unit &U); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHA1_H diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp b/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp new file mode 100644 index 00000000..81c084b5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp @@ -0,0 +1,339 @@ +//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Trace PCs. +// This module implements __sanitizer_cov_trace_pc_guard[_init], +// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation. +// +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerTracePC.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include + +namespace fuzzer { + +TracePC TPC; + +void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) { + uint32_t Idx = *Guard; + if (!Idx) return; + PCs[Idx % kNumPCs] = PC; + Counters[Idx % kNumCounters]++; +} + +size_t TracePC::GetTotalPCCoverage() { + size_t Res = 0; + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i]) + Res++; + return Res; +} + +void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) { + if (Start == Stop || *Start) return; + assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); + for (uint32_t *P = Start; P < Stop; P++) + *P = ++NumGuards; + Modules[NumModules].Start = Start; + Modules[NumModules].Stop = Stop; + NumModules++; +} + +void TracePC::PrintModuleInfo() { + Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards); + for (size_t i = 0; i < NumModules; i++) + Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop); + Printf("\n"); +} + +void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { + const uintptr_t kBits = 12; + const uintptr_t kMask = (1 << kBits) - 1; + uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits); + HandleValueProfile(Idx); +} + +static bool IsInterestingCoverageFile(std::string &File) { + if (File.find("compiler-rt/lib/") != std::string::npos) + return false; // sanitizer internal. + if (File.find("/usr/lib/") != std::string::npos) + return false; + if (File.find("/usr/include/") != std::string::npos) + return false; + if (File == "") + return false; + return true; +} + +void TracePC::PrintNewPCs() { + if (DoPrintNewPCs) { + if (!PrintedPCs) + PrintedPCs = new std::set; + for (size_t i = 1; i < GetNumPCs(); i++) + if (PCs[i] && PrintedPCs->insert(PCs[i]).second) + PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]); + } +} + +void TracePC::PrintCoverage() { + if (!EF->__sanitizer_symbolize_pc || + !EF->__sanitizer_get_module_and_offset_for_pc) { + Printf("INFO: __sanitizer_symbolize_pc or " + "__sanitizer_get_module_and_offset_for_pc is not available," + " not printing coverage\n"); + return; + } + std::map> CoveredPCsPerModule; + std::map ModuleOffsets; + std::set CoveredDirs, CoveredFiles, CoveredFunctions, + CoveredLines; + Printf("COVERAGE:\n"); + for (size_t i = 1; i < GetNumPCs(); i++) { + if (!PCs[i]) continue; + std::string FileStr = DescribePC("%s", PCs[i]); + if (!IsInterestingCoverageFile(FileStr)) continue; + std::string FixedPCStr = DescribePC("%p", PCs[i]); + std::string FunctionStr = DescribePC("%F", PCs[i]); + std::string LineStr = DescribePC("%l", PCs[i]); + char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? + void *OffsetRaw = nullptr; + if (!EF->__sanitizer_get_module_and_offset_for_pc( + reinterpret_cast(PCs[i]), ModulePathRaw, + sizeof(ModulePathRaw), &OffsetRaw)) + continue; + std::string Module = ModulePathRaw; + uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16); + uintptr_t PcOffset = reinterpret_cast(OffsetRaw); + ModuleOffsets[Module] = FixedPC - PcOffset; + CoveredPCsPerModule[Module].push_back(PcOffset); + CoveredFunctions.insert(FunctionStr); + CoveredFiles.insert(FileStr); + CoveredDirs.insert(DirName(FileStr)); + if (!CoveredLines.insert(FileStr + ":" + LineStr).second) + continue; + Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(), + FileStr.c_str(), LineStr.c_str()); + } + + std::string CoveredDirsStr; + for (auto &Dir : CoveredDirs) { + if (!CoveredDirsStr.empty()) + CoveredDirsStr += ","; + CoveredDirsStr += Dir; + } + Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str()); + + for (auto &M : CoveredPCsPerModule) { + std::set UncoveredFiles, UncoveredFunctions; + std::map > UncoveredLines; // Func+File => lines + auto &ModuleName = M.first; + auto &CoveredOffsets = M.second; + uintptr_t ModuleOffset = ModuleOffsets[ModuleName]; + std::sort(CoveredOffsets.begin(), CoveredOffsets.end()); + Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str()); + // sancov does not yet fully support DSOs. + // std::string Cmd = "sancov -print-coverage-pcs " + ModuleName; + std::string Cmd = "objdump -d " + ModuleName + + " | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'"; + std::string SanCovOutput; + if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) { + Printf("INFO: Command failed: %s\n", Cmd.c_str()); + continue; + } + std::istringstream ISS(SanCovOutput); + std::string S; + while (std::getline(ISS, S, '\n')) { + uintptr_t PcOffset = std::stol(S, 0, 16); + if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(), + PcOffset)) { + uintptr_t PC = ModuleOffset + PcOffset; + auto FileStr = DescribePC("%s", PC); + if (!IsInterestingCoverageFile(FileStr)) continue; + if (CoveredFiles.count(FileStr) == 0) { + UncoveredFiles.insert(FileStr); + continue; + } + auto FunctionStr = DescribePC("%F", PC); + if (CoveredFunctions.count(FunctionStr) == 0) { + UncoveredFunctions.insert(FunctionStr); + continue; + } + std::string LineStr = DescribePC("%l", PC); + uintptr_t Line = std::stoi(LineStr); + std::string FileLineStr = FileStr + ":" + LineStr; + if (CoveredLines.count(FileLineStr) == 0) + UncoveredLines[FunctionStr + " " + FileStr].insert(Line); + } + } + for (auto &FileLine: UncoveredLines) + for (int Line : FileLine.second) + Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line); + for (auto &Func : UncoveredFunctions) + Printf("UNCOVERED_FUNC: %s\n", Func.c_str()); + for (auto &File : UncoveredFiles) + Printf("UNCOVERED_FILE: %s\n", File.c_str()); + } +} + +void TracePC::DumpCoverage() { + __sanitizer_dump_coverage(PCs, GetNumPCs()); +} + +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map. +// Every new bit in the map is treated as a new coverage. +// +// For memcmp/strcmp/etc the interesting value is the length of the common +// prefix of the parameters. +// For cmp instructions the interesting value is a XOR of the parameters. +// The interesting value is mixed up with the PC and is then added to the map. + +ATTRIBUTE_NO_SANITIZE_MEMORY +void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n) { + if (!n) return; + size_t Len = std::min(n, (size_t)32); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I]) + break; + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = I; + // if (I < Len) + // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; + TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, + size_t n) { + if (!n) return; + size_t Len = std::min(n, (size_t)32); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I] || A1[I] == 0) + break; + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = I; + // if (I < Len && A1[I]) + // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; + TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); +} + +template +ATTRIBUTE_TARGET_POPCNT +#ifdef __clang__ // g++ can't handle this __attribute__ here :( +__attribute__((always_inline)) +#endif // __clang__ +void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) { + uintptr_t PCuint = reinterpret_cast(PC); + uint64_t ArgXor = Arg1 ^ Arg2; + uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65] + uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance; + if (sizeof(T) == 4) + TORC4.Insert(ArgXor, Arg1, Arg2); + else if (sizeof(T) == 8) + TORC8.Insert(ArgXor, Arg1, Arg2); + HandleValueProfile(Idx); +} + +} // namespace fuzzer + +extern "C" { +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { + uintptr_t PC = (uintptr_t)__builtin_return_address(0); + fuzzer::TPC.HandleTrace(Guard, PC); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { + fuzzer::TPC.HandleInit(Start, Stop); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { + uintptr_t PC = (uintptr_t)__builtin_return_address(0); + fuzzer::TPC.HandleCallerCallee(PC, Callee); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { + // Updates the value profile based on the relative position of Val and Cases. + // We want to handle one random case at every call (handling all is slow). + // Since none of the arguments contain any random bits we use a thread-local + // counter to choose the random case to handle. + static thread_local size_t Counter; + Counter++; + uint64_t N = Cases[0]; + uint64_t *Vals = Cases + 2; + char *PC = (char*)__builtin_return_address(0); + // We need a random number < N using Counter as a seed. But w/o DIV. + // * find a power of two >= N + // * mask Counter with this power of two. + // * maybe subtract N. + size_t Nlog = sizeof(long) * 8 - __builtin_clzl((long)N); + size_t PowerOfTwoGeN = 1U << Nlog; + assert(PowerOfTwoGeN >= N); + size_t Idx = Counter & (PowerOfTwoGeN - 1); + if (Idx >= N) + Idx -= N; + assert(Idx < N); + uint64_t TwoIn32 = 1ULL << 32; + if ((Val | Vals[Idx]) < TwoIn32) + fuzzer::TPC.HandleCmp(PC + Idx, static_cast(Val), + static_cast(Vals[Idx])); + else + fuzzer::TPC.HandleCmp(PC + Idx, Val, Vals[Idx]); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_div4(uint32_t Val) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_div8(uint64_t Val) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_gep(uintptr_t Idx) { + fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0); +} + +} // extern "C" diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.h b/test/fuzz_test/Fuzzer/FuzzerTracePC.h new file mode 100644 index 00000000..68827f80 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerTracePC.h @@ -0,0 +1,158 @@ +//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::TracePC +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_TRACE_PC +#define LLVM_FUZZER_TRACE_PC + +#include "FuzzerDefs.h" +#include "FuzzerValueBitMap.h" +#include + +namespace fuzzer { + +// TableOfRecentCompares (TORC) remembers the most recently performed +// comparisons of type T. +// We record the arguments of CMP instructions in this table unconditionally +// because it seems cheaper this way than to compute some expensive +// conditions inside __sanitizer_cov_trace_cmp*. +// After the unit has been executed we may decide to use the contents of +// this table to populate a Dictionary. +template +struct TableOfRecentCompares { + static const size_t kSize = kSizeT; + struct Pair { + T A, B; + }; + void Insert(size_t Idx, T Arg1, T Arg2) { + Idx = Idx % kSize; + Table[Idx].A = Arg1; + Table[Idx].B = Arg2; + } + + Pair Get(size_t I) { return Table[I % kSize]; } + + Pair Table[kSize]; +}; + +class TracePC { + public: + static const size_t kFeatureSetSize = ValueBitMap::kNumberOfItems; + + void HandleTrace(uint32_t *guard, uintptr_t PC); + void HandleInit(uint32_t *start, uint32_t *stop); + void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); + void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); } + template void HandleCmp(void *PC, T Arg1, T Arg2); + size_t GetTotalPCCoverage(); + void SetUseCounters(bool UC) { UseCounters = UC; } + void SetUseValueProfile(bool VP) { UseValueProfile = VP; } + void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } + template size_t CollectFeatures(Callback CB); + bool UpdateValueProfileMap(ValueBitMap *MaxValueProfileMap) { + return UseValueProfile && MaxValueProfileMap->MergeFrom(ValueProfileMap); + } + + void ResetMaps() { + ValueProfileMap.Reset(); + memset(Counters, 0, sizeof(Counters)); + } + + void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); + void PrintFeatureSet(); + + void PrintModuleInfo(); + + void PrintCoverage(); + void DumpCoverage(); + + void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n); + void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, + size_t n); + + bool UsingTracePcGuard() const {return NumModules; } + + static const size_t kTORCSize = 1 << 5; + TableOfRecentCompares TORC4; + TableOfRecentCompares TORC8; + + void PrintNewPCs(); + size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); } + uintptr_t GetPC(size_t Idx) { + assert(Idx < GetNumPCs()); + return PCs[Idx]; + } + +private: + bool UseCounters = false; + bool UseValueProfile = false; + bool DoPrintNewPCs = false; + + struct Module { + uint32_t *Start, *Stop; + }; + + Module Modules[4096]; + size_t NumModules; // linker-initialized. + size_t NumGuards; // linker-initialized. + + static const size_t kNumCounters = 1 << 14; + alignas(8) uint8_t Counters[kNumCounters]; + + static const size_t kNumPCs = 1 << 24; + uintptr_t PCs[kNumPCs]; + + std::set *PrintedPCs; + + ValueBitMap ValueProfileMap; +}; + +template +size_t TracePC::CollectFeatures(Callback CB) { + if (!UsingTracePcGuard()) return 0; + size_t Res = 0; + const size_t Step = 8; + assert(reinterpret_cast(Counters) % Step == 0); + size_t N = Min(kNumCounters, NumGuards + 1); + N = (N + Step - 1) & ~(Step - 1); // Round up. + for (size_t Idx = 0; Idx < N; Idx += Step) { + uint64_t Bundle = *reinterpret_cast(&Counters[Idx]); + if (!Bundle) continue; + for (size_t i = Idx; i < Idx + Step; i++) { + uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff; + if (!Counter) continue; + Counters[i] = 0; + unsigned Bit = 0; + /**/ if (Counter >= 128) Bit = 7; + else if (Counter >= 32) Bit = 6; + else if (Counter >= 16) Bit = 5; + else if (Counter >= 8) Bit = 4; + else if (Counter >= 4) Bit = 3; + else if (Counter >= 3) Bit = 2; + else if (Counter >= 2) Bit = 1; + size_t Feature = (i * 8 + Bit); + if (CB(Feature)) + Res++; + } + } + if (UseValueProfile) + ValueProfileMap.ForEach([&](size_t Idx) { + if (CB(NumGuards * 8 + Idx)) + Res++; + }); + return Res; +} + +extern TracePC TPC; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_TRACE_PC diff --git a/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp b/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp new file mode 100644 index 00000000..be62a662 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp @@ -0,0 +1,325 @@ +//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Data tracing. +//===----------------------------------------------------------------------===// + +#include "FuzzerDictionary.h" +#include "FuzzerInternal.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +namespace fuzzer { + +// For now, very simple: put Size bytes of Data at position Pos. +struct TraceBasedMutation { + uint32_t Pos; + Word W; +}; + +// Declared as static globals for faster checks inside the hooks. +static bool RecordingMemcmp = false; +static bool RecordingMemmem = false; +static bool DoingMyOwnMemmem = false; + +ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; } +ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; } + +class TraceState { +public: + TraceState(MutationDispatcher &MD, const FuzzingOptions &Options, + const Fuzzer *F) + : MD(MD), Options(Options), F(F) {} + + void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2); + + void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val, + size_t NumCases, uint64_t *Cases); + int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, + size_t DataSize); + int TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, size_t DataSize); + + void StartTraceRecording() { + if (!Options.UseMemcmp) + return; + RecordingMemcmp = Options.UseMemcmp; + RecordingMemmem = Options.UseMemmem; + NumMutations = 0; + InterestingWords.clear(); + MD.ClearAutoDictionary(); + } + + void StopTraceRecording() { + if (!RecordingMemcmp) + return; + RecordingMemcmp = false; + for (size_t i = 0; i < NumMutations; i++) { + auto &M = Mutations[i]; + if (Options.Verbosity >= 2) { + AutoDictUnitCounts[M.W]++; + AutoDictAdds++; + if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) { + typedef std::pair CU; + std::vector CountedUnits; + for (auto &I : AutoDictUnitCounts) + CountedUnits.push_back(std::make_pair(I.second, I.first)); + std::sort(CountedUnits.begin(), CountedUnits.end(), + [](const CU &a, const CU &b) { return a.first > b.first; }); + Printf("AutoDict:\n"); + for (auto &I : CountedUnits) { + Printf(" %zd ", I.first); + PrintASCII(I.second.data(), I.second.size()); + Printf("\n"); + } + } + } + MD.AddWordToAutoDictionary({M.W, M.Pos}); + } + for (auto &W : InterestingWords) + MD.AddWordToAutoDictionary({W}); + } + + void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) { + if (NumMutations >= kMaxMutations) return; + auto &M = Mutations[NumMutations++]; + M.Pos = Pos; + M.W.Set(Data, Size); + } + + void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { + assert(Size <= sizeof(Data)); + AddMutation(Pos, Size, reinterpret_cast(&Data)); + } + + void AddInterestingWord(const uint8_t *Data, size_t Size) { + if (!RecordingMemmem || !F->InFuzzingThread()) return; + if (Size <= 1) return; + Size = std::min(Size, Word::GetMaxSize()); + Word W(Data, Size); + InterestingWords.insert(W); + } + + private: + bool IsTwoByteData(uint64_t Data) { + int64_t Signed = static_cast(Data); + Signed >>= 16; + return Signed == 0 || Signed == -1L; + } + + // We don't want to create too many trace-based mutations as it is both + // expensive and useless. So after some number of mutations is collected, + // start rejecting some of them. The more there are mutations the more we + // reject. + bool WantToHandleOneMoreMutation() { + const size_t FirstN = 64; + // Gladly handle first N mutations. + if (NumMutations <= FirstN) return true; + size_t Diff = NumMutations - FirstN; + size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff); + assert(DiffLog > 0 && DiffLog < 64); + bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog. + return WantThisOne; + } + + static const size_t kMaxMutations = 1 << 16; + size_t NumMutations; + TraceBasedMutation Mutations[kMaxMutations]; + // TODO: std::set is too inefficient, need to have a custom DS here. + std::set InterestingWords; + MutationDispatcher &MD; + const FuzzingOptions Options; + const Fuzzer *F; + std::map AutoDictUnitCounts; + size_t AutoDictAdds = 0; +}; + +int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData, + size_t DataSize) { + if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const uint8_t *UnitData; + auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); + int Res = 0; + const uint8_t *Beg = UnitData; + const uint8_t *End = Beg + UnitSize; + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < UnitSize); + AddMutation(Pos, DataSize, DesiredData); + AddMutation(Pos, DataSize, DesiredData + 1); + AddMutation(Pos, DataSize, DesiredData - 1); + Res++; + } + return Res; +} + +int TraceState::TryToAddDesiredData(const uint8_t *PresentData, + const uint8_t *DesiredData, + size_t DataSize) { + if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; + ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const uint8_t *UnitData; + auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); + int Res = 0; + const uint8_t *Beg = UnitData; + const uint8_t *End = Beg + UnitSize; + for (const uint8_t *Cur = Beg; Cur < End; Cur++) { + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize); + if (!Cur) + break; + size_t Pos = Cur - Beg; + assert(Pos < UnitSize); + AddMutation(Pos, DataSize, DesiredData); + Res++; + } + return Res; +} + +void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, + const uint8_t *Data2) { + if (!RecordingMemcmp || !F->InFuzzingThread()) return; + CmpSize = std::min(CmpSize, Word::GetMaxSize()); + int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize); + int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize); + if ((Added1 || Added2) && Options.Verbosity >= 3) { + Printf("MemCmp Added %d%d: ", Added1, Added2); + if (Added1) PrintASCII(Data1, CmpSize); + if (Added2) PrintASCII(Data2, CmpSize); + Printf("\n"); + } +} + +void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, + uint64_t Val, size_t NumCases, + uint64_t *Cases) { + if (F->InFuzzingThread()) return; + size_t ValSize = ValSizeInBits / 8; + bool TryShort = IsTwoByteData(Val); + for (size_t i = 0; i < NumCases; i++) + TryShort &= IsTwoByteData(Cases[i]); + + if (Options.Verbosity >= 3) + Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases, + TryShort); + + for (size_t i = 0; i < NumCases; i++) { + TryToAddDesiredData(Val, Cases[i], ValSize); + if (TryShort) + TryToAddDesiredData(Val, Cases[i], 2); + } +} + +static TraceState *TS; + +void Fuzzer::StartTraceRecording() { + if (!TS) return; + TS->StartTraceRecording(); +} + +void Fuzzer::StopTraceRecording() { + if (!TS) return; + TS->StopTraceRecording(); +} + +void Fuzzer::InitializeTraceState() { + if (!Options.UseMemcmp) return; + TS = new TraceState(MD, Options, this); +} + +static size_t InternalStrnlen(const char *S, size_t MaxLen) { + size_t Len = 0; + for (; Len < MaxLen && S[Len]; Len++) {} + return Len; +} + +} // namespace fuzzer + +using fuzzer::TS; +using fuzzer::RecordingMemcmp; + +extern "C" { + +// We may need to avoid defining weak hooks to stay compatible with older clang. +#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1 +#endif + +#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, + const void *s2, size_t n, int result) { + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + if (n <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2)); +} + +void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, + const char *s2, size_t n, int result) { + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = fuzzer::InternalStrnlen(s1, n); + size_t Len2 = fuzzer::InternalStrnlen(s2, n); + n = std::min(n, Len1); + n = std::min(n, Len2); + if (n <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(n, reinterpret_cast(s1), + reinterpret_cast(s2)); +} + +void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, + const char *s2, int result) { + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64); + if (!RecordingMemcmp) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = strlen(s1); + size_t Len2 = strlen(s2); + size_t N = std::min(Len1, Len2); + if (N <= 1) return; // Not interesting. + TS->TraceMemcmpCallback(N, reinterpret_cast(s1), + reinterpret_cast(s2)); +} + +void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); +} +void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); +} +void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast(s2), strlen(s2)); +} +void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast(s2), strlen(s2)); +} +void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + if (fuzzer::DoingMyOwnMemmem) return; + TS->AddInterestingWord(reinterpret_cast(s2), len2); +} + +#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS +} // extern "C" diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.cpp b/test/fuzz_test/Fuzzer/FuzzerUtil.cpp new file mode 100644 index 00000000..f5fd3a85 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtil.cpp @@ -0,0 +1,218 @@ +//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils. +//===----------------------------------------------------------------------===// + +#include "FuzzerUtil.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter) { + for (size_t i = 0; i < Size; i++) + Printf("0x%x,", (unsigned)Data[i]); + Printf("%s", PrintAfter); +} + +void Print(const Unit &v, const char *PrintAfter) { + PrintHexArray(v.data(), v.size(), PrintAfter); +} + +void PrintASCIIByte(uint8_t Byte) { + if (Byte == '\\') + Printf("\\\\"); + else if (Byte == '"') + Printf("\\\""); + else if (Byte >= 32 && Byte < 127) + Printf("%c", Byte); + else + Printf("\\x%02x", Byte); +} + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { + for (size_t i = 0; i < Size; i++) + PrintASCIIByte(Data[i]); + Printf("%s", PrintAfter); +} + +void PrintASCII(const Unit &U, const char *PrintAfter) { + PrintASCII(U.data(), U.size(), PrintAfter); +} + +bool ToASCII(uint8_t *Data, size_t Size) { + bool Changed = false; + for (size_t i = 0; i < Size; i++) { + uint8_t &X = Data[i]; + auto NewX = X; + NewX &= 127; + if (!isspace(NewX) && !isprint(NewX)) + NewX = ' '; + Changed |= NewX != X; + X = NewX; + } + return Changed; +} + +bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); } + +bool IsASCII(const uint8_t *Data, size_t Size) { + for (size_t i = 0; i < Size; i++) + if (!(isprint(Data[i]) || isspace(Data[i]))) return false; + return true; +} + +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { + U->clear(); + if (Str.empty()) return false; + size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R]. + // Skip spaces from both sides. + while (L < R && isspace(Str[L])) L++; + while (R > L && isspace(Str[R])) R--; + if (R - L < 2) return false; + // Check the closing " + if (Str[R] != '"') return false; + R--; + // Find the opening " + while (L < R && Str[L] != '"') L++; + if (L >= R) return false; + assert(Str[L] == '\"'); + L++; + assert(L <= R); + for (size_t Pos = L; Pos <= R; Pos++) { + uint8_t V = (uint8_t)Str[Pos]; + if (!isprint(V) && !isspace(V)) return false; + if (V =='\\') { + // Handle '\\' + if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) { + U->push_back(Str[Pos + 1]); + Pos++; + continue; + } + // Handle '\xAB' + if (Pos + 3 <= R && Str[Pos + 1] == 'x' + && isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) { + char Hex[] = "0xAA"; + Hex[2] = Str[Pos + 2]; + Hex[3] = Str[Pos + 3]; + U->push_back(strtol(Hex, nullptr, 16)); + Pos += 3; + continue; + } + return false; // Invalid escape. + } else { + // Any other character. + U->push_back(V); + } + } + return true; +} + +bool ParseDictionaryFile(const std::string &Text, std::vector *Units) { + if (Text.empty()) { + Printf("ParseDictionaryFile: file does not exist or is empty\n"); + return false; + } + std::istringstream ISS(Text); + Units->clear(); + Unit U; + int LineNo = 0; + std::string S; + while (std::getline(ISS, S, '\n')) { + LineNo++; + size_t Pos = 0; + while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces. + if (Pos == S.size()) continue; // Empty line. + if (S[Pos] == '#') continue; // Comment line. + if (ParseOneDictionaryEntry(S, &U)) { + Units->push_back(U); + } else { + Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo, + S.c_str()); + return false; + } + } + return true; +} + +std::string Base64(const Unit &U) { + static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + std::string Res; + size_t i; + for (i = 0; i + 2 < U.size(); i += 3) { + uint32_t x = (U[i] << 16) + (U[i + 1] << 8) + U[i + 2]; + Res += Table[(x >> 18) & 63]; + Res += Table[(x >> 12) & 63]; + Res += Table[(x >> 6) & 63]; + Res += Table[x & 63]; + } + if (i + 1 == U.size()) { + uint32_t x = (U[i] << 16); + Res += Table[(x >> 18) & 63]; + Res += Table[(x >> 12) & 63]; + Res += "=="; + } else if (i + 2 == U.size()) { + uint32_t x = (U[i] << 16) + (U[i + 1] << 8); + Res += Table[(x >> 18) & 63]; + Res += Table[(x >> 12) & 63]; + Res += Table[(x >> 6) & 63]; + Res += "="; + } + return Res; +} + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { + if (!EF->__sanitizer_symbolize_pc) return ""; + char PcDescr[1024]; + EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), + SymbolizedFMT, PcDescr, sizeof(PcDescr)); + PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. + return PcDescr; +} + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { + if (EF->__sanitizer_symbolize_pc) + Printf("%s", DescribePC(SymbolizedFMT, PC).c_str()); + else + Printf(FallbackFMT, PC); +} + +unsigned NumberOfCpuCores() { + unsigned N = std::thread::hardware_concurrency(); + if (!N) { + Printf("WARNING: std::thread::hardware_concurrency not well defined for " + "your platform. Assuming CPU count of 1.\n"); + N = 1; + } + return N; +} + +bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) { + FILE *Pipe = OpenProcessPipe(Command.c_str(), "r"); + if (!Pipe) return false; + char Buff[1024]; + size_t N; + while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0) + Out->append(Buff, N); + return true; +} + +} // namespace fuzzer diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.h b/test/fuzz_test/Fuzzer/FuzzerUtil.h new file mode 100644 index 00000000..08058c56 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtil.h @@ -0,0 +1,72 @@ +//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Util functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_UTIL_H +#define LLVM_FUZZER_UTIL_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +void PrintHexArray(const Unit &U, const char *PrintAfter = ""); + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter = ""); + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = ""); + +void PrintASCII(const Unit &U, const char *PrintAfter = ""); + +// Changes U to contain only ASCII (isprint+isspace) characters. +// Returns true iff U has been changed. +bool ToASCII(uint8_t *Data, size_t Size); + +bool IsASCII(const Unit &U); + +bool IsASCII(const uint8_t *Data, size_t Size); + +std::string Base64(const Unit &U); + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC); + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); + +unsigned NumberOfCpuCores(); + +bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out); + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions& Options); + +void SleepSeconds(int Seconds); + +unsigned long GetPid(); + +size_t GetPeakRSSMb(); + +int ExecuteCommand(const std::string &Command); + +FILE *OpenProcessPipe(const char *Command, const char *Mode); + +const void *SearchMemory(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); + +std::string CloneArgsWithoutX(const std::vector &Args, + const char *X1, const char *X2); + +inline std::string CloneArgsWithoutX(const std::vector &Args, + const char *X) { + return CloneArgsWithoutX(Args, X, X); +} + +} // namespace fuzzer + +#endif // LLVM_FUZZER_UTIL_H diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp new file mode 100644 index 00000000..9674368c --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp @@ -0,0 +1,152 @@ +//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils for Darwin. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_APPLE + +#include "FuzzerIO.h" +#include +#include +#include +#include + +// There is no header for this on macOS so declare here +extern "C" char **environ; + +namespace fuzzer { + +static std::mutex SignalMutex; +// Global variables used to keep track of how signal handling should be +// restored. They should **not** be accessed without holding `SignalMutex`. +static int ActiveThreadCount = 0; +static struct sigaction OldSigIntAction; +static struct sigaction OldSigQuitAction; +static sigset_t OldBlockedSignalsSet; + +// This is a reimplementation of Libc's `system()`. On Darwin the Libc +// implementation contains a mutex which prevents it from being used +// concurrently. This implementation **can** be used concurrently. It sets the +// signal handlers when the first thread enters and restores them when the last +// thread finishes execution of the function and ensures this is not racey by +// using a mutex. +int ExecuteCommand(const std::string &Command) { + posix_spawnattr_t SpawnAttributes; + if (posix_spawnattr_init(&SpawnAttributes)) + return -1; + // Block and ignore signals of the current process when the first thread + // enters. + { + std::lock_guard Lock(SignalMutex); + if (ActiveThreadCount == 0) { + static struct sigaction IgnoreSignalAction; + sigset_t BlockedSignalsSet; + memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction)); + IgnoreSignalAction.sa_handler = SIG_IGN; + + if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) { + Printf("Failed to ignore SIGINT\n"); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + } + if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) { + Printf("Failed to ignore SIGQUIT\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + } + + (void)sigemptyset(&BlockedSignalsSet); + (void)sigaddset(&BlockedSignalsSet, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) == + -1) { + Printf("Failed to block SIGCHLD\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL); + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + } + } + ++ActiveThreadCount; + } + + // NOTE: Do not introduce any new `return` statements past this + // point. It is important that `ActiveThreadCount` always be decremented + // when leaving this function. + + // Make sure the child process uses the default handlers for the + // following signals rather than inheriting what the parent has. + sigset_t DefaultSigSet; + (void)sigemptyset(&DefaultSigSet); + (void)sigaddset(&DefaultSigSet, SIGQUIT); + (void)sigaddset(&DefaultSigSet, SIGINT); + (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); + // Make sure the child process doesn't block SIGCHLD + (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet); + short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); + + pid_t Pid; + char **Environ = environ; // Read from global + const char *CommandCStr = Command.c_str(); + const char *Argv[] = {"sh", "-c", CommandCStr, NULL}; + int ErrorCode = 0, ProcessStatus = 0; + // FIXME: We probably shouldn't hardcode the shell path. + ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, + (char *const *)Argv, Environ); + (void)posix_spawnattr_destroy(&SpawnAttributes); + if (!ErrorCode) { + pid_t SavedPid = Pid; + do { + // Repeat until call completes uninterrupted. + Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); + } while (Pid == -1 && errno == EINTR); + if (Pid == -1) { + // Fail for some other reason. + ProcessStatus = -1; + } + } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { + // Fork failure. + ProcessStatus = -1; + } else { + // Shell execution failure. + ProcessStatus = W_EXITCODE(127, 0); + } + + // Restore the signal handlers of the current process when the last thread + // using this function finishes. + { + std::lock_guard Lock(SignalMutex); + --ActiveThreadCount; + if (ActiveThreadCount == 0) { + bool FailedRestore = false; + if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) { + Printf("Failed to restore SIGINT handling\n"); + FailedRestore = true; + } + if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) { + Printf("Failed to restore SIGQUIT handling\n"); + FailedRestore = true; + } + if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) { + Printf("Failed to unblock SIGCHLD\n"); + FailedRestore = true; + } + if (FailedRestore) + ProcessStatus = -1; + } + } + return ProcessStatus; +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp new file mode 100644 index 00000000..dfe7e6f4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp @@ -0,0 +1,24 @@ +//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils for Linux. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_LINUX + +#include + +namespace fuzzer { + +int ExecuteCommand(const std::string &Command) { + return system(Command.c_str()); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_LINUX diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp new file mode 100644 index 00000000..8b484b8e --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp @@ -0,0 +1,117 @@ +//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_POSIX +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static void AlarmHandler(int, siginfo_t *, void *) { + Fuzzer::StaticAlarmCallback(); +} + +static void CrashHandler(int, siginfo_t *, void *) { + Fuzzer::StaticCrashSignalCallback(); +} + +static void InterruptHandler(int, siginfo_t *, void *) { + Fuzzer::StaticInterruptCallback(); +} + +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + } +} + +void SetTimer(int Seconds) { + struct itimerval T { + {Seconds, 0}, { Seconds, 0 } + }; + if (setitimer(ITIMER_REAL, &T, nullptr)) { + Printf("libFuzzer: setitimer failed with %d\n", errno); + exit(1); + } + SetSigaction(SIGALRM, AlarmHandler); +} + +void SetSignalHandler(const FuzzingOptions& Options) { + if (Options.UnitTimeoutSec > 0) + SetTimer(Options.UnitTimeoutSec / 2 + 1); + if (Options.HandleInt) + SetSigaction(SIGINT, InterruptHandler); + if (Options.HandleTerm) + SetSigaction(SIGTERM, InterruptHandler); + if (Options.HandleSegv) + SetSigaction(SIGSEGV, CrashHandler); + if (Options.HandleBus) + SetSigaction(SIGBUS, CrashHandler); + if (Options.HandleAbrt) + SetSigaction(SIGABRT, CrashHandler); + if (Options.HandleIll) + SetSigaction(SIGILL, CrashHandler); + if (Options.HandleFpe) + SetSigaction(SIGFPE, CrashHandler); +} + +void SleepSeconds(int Seconds) { + sleep(Seconds); // Use C API to avoid coverage from instrumented libc++. +} + +unsigned long GetPid() { return (unsigned long)getpid(); } + +size_t GetPeakRSSMb() { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) + return 0; + if (LIBFUZZER_LINUX) { + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + } else if (LIBFUZZER_APPLE) { + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + } + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + return popen(Command, Mode); +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + return memmem(Data, DataLen, Patt, PattLen); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp b/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp new file mode 100644 index 00000000..64adb7cd --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp @@ -0,0 +1,182 @@ +//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Misc utils implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static const FuzzingOptions* HandlerOpt = nullptr; + +LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { + switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + case EXCEPTION_STACK_OVERFLOW: + if (HandlerOpt->HandleSegv) + Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + case EXCEPTION_IN_PAGE_ERROR: + if (HandlerOpt->HandleBus) + Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + if (HandlerOpt->HandleIll) + Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + if (HandlerOpt->HandleFpe) + Fuzzer::StaticCrashSignalCallback(); + break; + } + return EXCEPTION_CONTINUE_SEARCH; +} + +BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { + switch (dwCtrlType) { + case CTRL_C_EVENT: + if (HandlerOpt->HandleInt) + Fuzzer::StaticInterruptCallback(); + return TRUE; + case CTRL_BREAK_EVENT: + if (HandlerOpt->HandleTerm) + Fuzzer::StaticInterruptCallback(); + return TRUE; + } + return FALSE; +} + +void CALLBACK AlarmHandler(PVOID, BOOLEAN) { + Fuzzer::StaticAlarmCallback(); +} + +class TimerQ { + HANDLE TimerQueue; + public: + TimerQ() : TimerQueue(NULL) {}; + ~TimerQ() { + if (TimerQueue) + DeleteTimerQueueEx(TimerQueue, NULL); + }; + void SetTimer(int Seconds) { + if (!TimerQueue) { + TimerQueue = CreateTimerQueue(); + if (!TimerQueue) { + Printf("libFuzzer: CreateTimerQueue failed.\n"); + exit(1); + } + } + HANDLE Timer; + if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL, + Seconds*1000, Seconds*1000, 0)) { + Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); + exit(1); + } + }; +}; + +static TimerQ Timer; + +static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } + +void SetSignalHandler(const FuzzingOptions& Options) { + HandlerOpt = &Options; + + if (Options.UnitTimeoutSec > 0) + Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); + + if (Options.HandleInt || Options.HandleTerm) + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { + DWORD LastError = GetLastError(); + Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n", + LastError); + exit(1); + } + + if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || + Options.HandleFpe) + if (!AddVectoredExceptionHandler(1, ExceptionHandler)) { + Printf("libFuzzer: AddVectoredExceptionHandler failed.\n"); + exit(1); + } + + if (Options.HandleAbrt) + if (SIG_ERR == signal(SIGABRT, CrashHandler)) { + Printf("libFuzzer: signal failed with %d\n", errno); + exit(1); + } +} + +void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); } + +unsigned long GetPid() { return GetCurrentProcessId(); } + +size_t GetPeakRSSMb() { + PROCESS_MEMORY_COUNTERS info; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) + return 0; + return info.PeakWorkingSetSize >> 20; +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + return _popen(Command, Mode); +} + +int ExecuteCommand(const std::string &Command) { + return system(Command.c_str()); +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + // TODO: make this implementation more efficient. + const char *Cdata = (const char *)Data; + const char *Cpatt = (const char *)Patt; + + if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen) + return NULL; + + if (PattLen == 1) + return memchr(Data, *Cpatt, DataLen); + + const char *End = Cdata + DataLen - PattLen + 1; + + for (const char *It = Cdata; It < End; ++It) + if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) + return It; + + return NULL; +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h b/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h new file mode 100644 index 00000000..0692acd1 --- /dev/null +++ b/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h @@ -0,0 +1,87 @@ +//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// ValueBitMap. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H +#define LLVM_FUZZER_VALUE_BIT_MAP_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +// A bit map containing kMapSizeInWords bits. +struct ValueBitMap { + static const size_t kMapSizeInBits = 65371; // Prime. + static const size_t kMapSizeInBitsAligned = 65536; // 2^16 + static const size_t kBitsInWord = (sizeof(uintptr_t) * 8); + static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord; + public: + static const size_t kNumberOfItems = kMapSizeInBits; + // Clears all bits. + void Reset() { memset(Map, 0, sizeof(Map)); } + + // Computes a hash function of Value and sets the corresponding bit. + // Returns true if the bit was changed from 0 to 1. + inline bool AddValue(uintptr_t Value) { + uintptr_t Idx = Value < kMapSizeInBits ? Value : Value % kMapSizeInBits; + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + uintptr_t Old = Map[WordIdx]; + uintptr_t New = Old | (1UL << BitIdx); + Map[WordIdx] = New; + return New != Old; + } + + inline bool Get(uintptr_t Idx) { + assert(Idx < kMapSizeInBits); + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + return Map[WordIdx] & (1UL << BitIdx); + } + + size_t GetNumBitsSinceLastMerge() const { return NumBits; } + + // Merges 'Other' into 'this', clears 'Other', updates NumBits, + // returns true if new bits were added. + ATTRIBUTE_TARGET_POPCNT + bool MergeFrom(ValueBitMap &Other) { + uintptr_t Res = 0; + size_t OldNumBits = NumBits; + for (size_t i = 0; i < kMapSizeInWords; i++) { + auto O = Other.Map[i]; + auto M = Map[i]; + if (O) { + Map[i] = (M |= O); + Other.Map[i] = 0; + } + if (M) + Res += __builtin_popcountl(M); + } + NumBits = Res; + return OldNumBits < NumBits; + } + + template + void ForEach(Callback CB) { + for (size_t i = 0; i < kMapSizeInWords; i++) + if (uintptr_t M = Map[i]) + for (size_t j = 0; j < sizeof(M) * 8; j++) + if (M & ((uintptr_t)1 << j)) + CB(i * sizeof(M) * 8 + j); + } + + private: + size_t NumBits = 0; + uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512))); +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_VALUE_BIT_MAP_H diff --git a/test/fuzz_test/Fuzzer/README.txt b/test/fuzz_test/Fuzzer/README.txt new file mode 100644 index 00000000..79f49b55 --- /dev/null +++ b/test/fuzz_test/Fuzzer/README.txt @@ -0,0 +1,2 @@ +Move to http://llvm.org/docs/LibFuzzer.html + diff --git a/test/fuzz_test/Fuzzer/afl/afl_driver.cpp b/test/fuzz_test/Fuzzer/afl/afl_driver.cpp new file mode 100644 index 00000000..fc958955 --- /dev/null +++ b/test/fuzz_test/Fuzzer/afl/afl_driver.cpp @@ -0,0 +1,295 @@ +//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// + +/* This file allows to fuzz libFuzzer-style target functions + (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. + +Usage: +################################################################################ +cat << EOF > test_fuzzer.cc +#include +#include +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + __builtin_trap(); + return 0; +} +EOF +# Build your target with -fsanitize-coverage=trace-pc using fresh clang. +clang -g -fsanitize-coverage=trace-pc test_fuzzer.cc -c +# Build afl-llvm-rt.o.c from the AFL distribution. +clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c +# Build this file, link it with afl-llvm-rt.o.o and the target code. +clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o +# Run AFL: +rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; +$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out +################################################################################ +Environment Variables: +There are a few environment variables that can be set to use features that +afl-fuzz doesn't have. + +AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file +specified. If the file does not exist, it is created. This is useful for getting +stack traces (when using ASAN for example) or original error messages on hard to +reproduce bugs. + +AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra +statistics to the file specified. Currently these are peak_rss_mb +(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If +the file does not exist it is created. If the file does exist then +afl_driver assumes it was restarted by afl-fuzz and will try to read old +statistics from the file. If that fails then the process will quit. + +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Platform detection. Copied from FuzzerInternal.h +#ifdef __linux__ +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_APPLE 0 +#elif __APPLE__ +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_APPLE 1 +#else +#error "Support for your platform has not been implemented" +#endif + +// Used to avoid repeating error checking boilerplate. If cond is false, a +// fatal error has occured in the program. In this event print error_message +// to stderr and abort(). Otherwise do nothing. Note that setting +// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended +// to the file as well, if the error occurs after the duplication is performed. +#define CHECK_ERROR(cond, error_message) \ + if (!(cond)) { \ + fprintf(stderr, (error_message)); \ + abort(); \ + } + +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +extern "C" { +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); +} + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; +extern "C" int __afl_persistent_loop(unsigned int); +static volatile char suppress_warning2 = AFL_PERSISTENT[0]; + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; +extern "C" void __afl_manual_init(); +static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; + +// Input buffer. +static const size_t kMaxAflInputSize = 1 << 20; +static uint8_t AflInputBuf[kMaxAflInputSize]; + +// Variables we need for writing to the extra stats file. +static FILE *extra_stats_file = NULL; +static uint32_t previous_peak_rss = 0; +static time_t slowest_unit_time_secs = 0; +static const int kNumExtraStats = 2; +static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n" + "slowest_unit_time_sec : %u\n"; + +// Copied from FuzzerUtil.cpp. +size_t GetPeakRSSMb() { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) + return 0; + if (LIBFUZZER_LINUX) { + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + } else if (LIBFUZZER_APPLE) { + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + } + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; +} + +// Based on SetSigaction in FuzzerUtil.cpp +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno); + exit(1); + } +} + +// Write extra stats to the file specified by the user. If none is specified +// this function will never be called. +static void write_extra_stats() { + uint32_t peak_rss = GetPeakRSSMb(); + + if (peak_rss < previous_peak_rss) + peak_rss = previous_peak_rss; + + int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString, + peak_rss, slowest_unit_time_secs); + + CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file"); + + CHECK_ERROR(fclose(extra_stats_file) == 0, + "Failed to close extra_stats_file"); +} + +// Call write_extra_stats before we exit. +static void crash_handler(int, siginfo_t *, void *) { + // Make sure we don't try calling write_extra_stats again if we crashed while + // trying to call it. + static bool first_crash = true; + CHECK_ERROR(first_crash, + "Crashed in crash signal handler. This is a bug in the fuzzer."); + + first_crash = false; + write_extra_stats(); +} + +// If the user has specified an extra_stats_file through the environment +// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up +// to write stats to it on exit. If no file is specified, do nothing. Otherwise +// install signal and exit handlers to write to the file when the process exits. +// Then if the file doesn't exist create it and set extra stats to 0. But if it +// does exist then read the initial values of the extra stats from the file +// and check that the file is writable. +static void maybe_initialize_extra_stats() { + // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do. + char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME"); + if (!extra_stats_filename) + return; + + // Open the file and find the previous peak_rss_mb value. + // This is necessary because the fuzzing process is restarted after N + // iterations are completed. So we may need to get this value from a previous + // process to be accurate. + extra_stats_file = fopen(extra_stats_filename, "r"); + + // If extra_stats_file already exists: read old stats from it. + if (extra_stats_file) { + int matches = fscanf(extra_stats_file, kExtraStatsFormatString, + &previous_peak_rss, &slowest_unit_time_secs); + + // Make sure we have read a real extra stats file and that we have used it + // to set slowest_unit_time_secs and previous_peak_rss. + CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt"); + + CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file"); + + // Now open the file for writing. + extra_stats_file = fopen(extra_stats_filename, "w"); + CHECK_ERROR(extra_stats_file, + "Failed to open extra stats file for writing"); + } else { + // Looks like this is the first time in a fuzzing job this is being called. + extra_stats_file = fopen(extra_stats_filename, "w+"); + CHECK_ERROR(extra_stats_file, "failed to create extra stats file"); + } + + // Make sure that crash_handler gets called on any kind of fatal error. + int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT, + SIGTERM}; + + const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]); + + for (size_t idx = 0; idx < num_signals; idx++) + SetSigaction(crash_signals[idx], crash_handler); + + // Make sure it gets called on other kinds of exits. + atexit(write_extra_stats); +} + +// If the user asks us to duplicate stderr, then do it. +static void maybe_duplicate_stderr() { + char* stderr_duplicate_filename = + getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + + if (!stderr_duplicate_filename) + return; + + FILE* stderr_duplicate_stream = + freopen(stderr_duplicate_filename, "a+", stderr); + + if (!stderr_duplicate_stream) { + fprintf( + stderr, + "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + abort(); + } +} + +int main(int argc, char **argv) { + fprintf(stderr, "======================= INFO =========================\n" + "This binary is built for AFL-fuzz.\n" + "To run the target function on a single input execute this:\n" + " %s < INPUT_FILE\n" + "To run the fuzzing execute this:\n" + " afl-fuzz [afl-flags] %s [N] " + "-- run N fuzzing iterations before " + "re-spawning the process (default: 1000)\n" + "======================================================\n", + argv[0], argv[0]); + if (LLVMFuzzerInitialize) + LLVMFuzzerInitialize(&argc, &argv); + // Do any other expensive one-time initialization here. + + maybe_duplicate_stderr(); + maybe_initialize_extra_stats(); + + __afl_manual_init(); + + int N = 1000; + if (argc >= 2) + N = atoi(argv[1]); + assert(N > 0); + time_t unit_time_secs; + int num_runs = 0; + while (__afl_persistent_loop(N)) { + ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize); + if (n_read > 0) { + // Copy AflInputBuf into a separate buffer to let asan find buffer + // overflows. Don't use unique_ptr/etc to avoid extra dependencies. + uint8_t *copy = new uint8_t[n_read]; + memcpy(copy, AflInputBuf, n_read); + + struct timeval unit_start_time; + CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0, + "Calling gettimeofday failed"); + + num_runs++; + LLVMFuzzerTestOneInput(copy, n_read); + + struct timeval unit_stop_time; + CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0, + "Calling gettimeofday failed"); + + // Update slowest_unit_time_secs if we see a new max. + unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec; + if (slowest_unit_time_secs < unit_time_secs) + slowest_unit_time_secs = unit_time_secs; + + delete[] copy; + } + } + fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs); +} diff --git a/test/fuzz_test/Fuzzer/build.sh b/test/fuzz_test/Fuzzer/build.sh new file mode 100755 index 00000000..27c148ad --- /dev/null +++ b/test/fuzz_test/Fuzzer/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +LIBFUZZER_SRC_DIR=$(dirname $0) +for f in $LIBFUZZER_SRC_DIR/*.cpp; do + clang -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c & +done +wait +rm -f libFuzzer.a +ar ru libFuzzer.a Fuzzer*.o +rm -f Fuzzer*.o + diff --git a/test/fuzz_test/Fuzzer/cxx.dict b/test/fuzz_test/Fuzzer/cxx.dict new file mode 100644 index 00000000..41350f47 --- /dev/null +++ b/test/fuzz_test/Fuzzer/cxx.dict @@ -0,0 +1,122 @@ +"++" +"--" +"<<" +">>" +"+=" +"-=" +"*=" +"/=" +">>=" +"<<=" +"&=" +"|=" +"^=" +"%=" +"!=" +"&&" +"||" +"==" +">=" +"<=" +"->" +"alignas" +"alignof" +"and" +"and_eq" +"asm" +"auto" +"bitand" +"bitor" +"bool" +"break" +"case" +"catch" +"char" +"char16_t" +"char32_t" +"class" +"compl" +"concept" +"const" +"constexpr" +"const_cast" +"continue" +"decltype" +"default" +"delete" +"do" +"double" +"dynamic_cast" +"else" +"enum" +"explicit" +"export" +"extern" +"false" +"float" +"for" +"friend" +"goto" +"if" +"inline" +"int" +"long" +"mutable" +"namespace" +"new" +"noexcept" +"not" +"not_eq" +"nullptr" +"operator" +"or" +"or_eq" +"private" +"protected" +"public" +"register" +"reinterpret_cast" +"requires" +"return" +"short" +"signed" +"sizeof" +"static" +"static_assert" +"static_cast" +"struct" +"switch" +"template" +"this" +"thread_local" +"throw" +"true" +"try" +"typedef" +"typeid" +"typename" +"union" +"unsigned" +"using" +"virtual" +"void" +"volatile" +"wchar_t" +"while" +"xor" +"xor_eq" +"if" +"elif" +"else" +"endif" +"defined" +"ifdef" +"ifndef" +"define" +"undef" +"include" +"line" +"error" +"pragma" +"override" +"final" diff --git a/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c b/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c new file mode 100644 index 00000000..0d76ea49 --- /dev/null +++ b/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c @@ -0,0 +1,41 @@ +/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This main() function can be linked to a fuzz target (i.e. a library +// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize()) +// instead of libFuzzer. This main() function will not perform any fuzzing +// but will simply feed all input files one by one to the fuzz target. +// +// Use this file to provide reproducers for bugs when linking against libFuzzer +// or other fuzzing engine is undesirable. +//===----------------------------------------------------------------------===*/ +#include +#include +#include + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); +__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv); +int main(int argc, char **argv) { + fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1); + if (LLVMFuzzerInitialize) + LLVMFuzzerInitialize(&argc, &argv); + for (int i = 1; i < argc; i++) { + fprintf(stderr, "Running: %s\n", argv[i]); + FILE *f = fopen(argv[i], "r"); + assert(f); + fseek(f, 0, SEEK_END); + size_t len = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char *buf = (unsigned char*)malloc(len); + size_t n_read = fread(buf, 1, len, f); + assert(n_read == len); + LLVMFuzzerTestOneInput(buf, len); + free(buf); + fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read); + } +} diff --git a/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp b/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp new file mode 100644 index 00000000..3dd0b611 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Contains dummy functions used to avoid dependency on AFL. +#include +#include + +extern "C" void __afl_manual_init() {} + +extern "C" int __afl_persistent_loop(unsigned int) { + return 0; +} + +// This declaration exists to prevent the Darwin linker +// from complaining about this being a missing weak symbol. +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp b/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp new file mode 100644 index 00000000..57748143 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp @@ -0,0 +1,23 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// abs(x) < 0 and y == Const puzzle, 64-bit variant. +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 16) return 0; + int64_t x; + uint64_t y; + memcpy(&x, Data, sizeof(x)); + memcpy(&y, Data + sizeof(x), sizeof(y)); + if (labs(x) < 0 && y == 0xbaddcafedeadbeefUL) { + printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y); + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp b/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp new file mode 100644 index 00000000..69075a45 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp @@ -0,0 +1,23 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// abs(x) < 0 and y == Const puzzle. +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 8) return 0; + int x; + unsigned y; + memcpy(&x, Data, sizeof(x)); + memcpy(&y, Data + sizeof(x), sizeof(y)); + if (abs(x) < 0 && y == 0xbaddcafe) { + printf("BINGO; Found the target, exiting; x = 0x%x y 0x%x\n", x, y); + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp b/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp new file mode 100644 index 00000000..604d8fa2 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test with a more mallocs than frees, but no leak. +#include +#include + +const int kAllocatedPointersSize = 10000; +int NumAllocatedPointers = 0; +int *AllocatedPointers[kAllocatedPointersSize]; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (NumAllocatedPointers < kAllocatedPointersSize) + AllocatedPointers[NumAllocatedPointers++] = new int; + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp b/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp new file mode 100644 index 00000000..b9d14052 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp @@ -0,0 +1,23 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include +#include +#include + +static volatile bool SeedLargeBuffer; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size >= 4) + SeedLargeBuffer = true; + if (Size == 3 && SeedLargeBuffer && Data[3]) { + std::cout << "Woops, reading Data[3] w/o crashing\n"; + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/CMakeLists.txt new file mode 100644 index 00000000..c0457746 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CMakeLists.txt @@ -0,0 +1,217 @@ +# Build all these tests with -O0, otherwise optimizations may merge some +# basic blocks and we'll fail to discover the targets. +# We change the flags for every build type because we might be doing +# a multi-configuration build (e.g. Xcode) where CMAKE_BUILD_TYPE doesn't +# mean anything. +set(variables_to_filter + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_MINSIZEREL + LIBFUZZER_FLAGS_BASE + ) +foreach (VARNAME ${variables_to_filter}) + string(REPLACE " " ";" BUILD_FLAGS_AS_LIST "${${VARNAME}}") + set(new_flags "") + foreach (flag ${BUILD_FLAGS_AS_LIST}) + # NOTE: Use of XX here is to avoid a CMake warning due to CMP0054 + if (NOT ("XX${flag}" MATCHES "XX-O[0123s]")) + set(new_flags "${new_flags} ${flag}") + else() + set(new_flags "${new_flags} -O0") + endif() + endforeach() + set(${VARNAME} "${new_flags}") +endforeach() + +# Enable the coverage instrumentation (it is disabled for the Fuzzer lib). +set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -g") + +# add_libfuzzer_test( +# SOURCES source0.cpp [source1.cpp ...] +# ) +# +# Declares a LibFuzzer test executable with target name LLVMFuzzer-. +# +# One or more source files to be compiled into the binary must be declared +# after the SOURCES keyword. +function(add_libfuzzer_test name) + set(multi_arg_options "SOURCES") + cmake_parse_arguments( + "add_libfuzzer_test" "" "" "${multi_arg_options}" ${ARGN}) + if ("${add_libfuzzer_test_SOURCES}" STREQUAL "") + message(FATAL_ERROR "Source files must be specified") + endif() + add_executable(LLVMFuzzer-${name} + ${add_libfuzzer_test_SOURCES} + ) + target_link_libraries(LLVMFuzzer-${name} LLVMFuzzer) + # Place binary where llvm-lit expects to find it + set_target_properties(LLVMFuzzer-${name} + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" + ) + set(TestBinaries ${TestBinaries} LLVMFuzzer-${name} PARENT_SCOPE) +endfunction() + +# Variable to keep track of all test targets +set(TestBinaries) + +############################################################################### +# Basic tests +############################################################################### + +set(Tests + AbsNegAndConstantTest + AbsNegAndConstant64Test + AccumulateAllocationsTest + BufferOverflowOnInput + CallerCalleeTest + CounterTest + CustomCrossOverTest + CustomMutatorTest + DivTest + EmptyTest + FourIndependentBranchesTest + FullCoverageSetTest + InitializeTest + MemcmpTest + LeakTest + LeakTimeoutTest + LoadTest + NullDerefTest + NullDerefOnEmptyTest + NthRunCrashTest + OneHugeAllocTest + OutOfMemoryTest + OutOfMemorySingleLargeMallocTest + RepeatedMemcmp + RepeatedBytesTest + SimpleCmpTest + SimpleDictionaryTest + SimpleHashTest + SimpleTest + SimpleThreadedTest + SingleMemcmpTest + SingleStrcmpTest + SingleStrncmpTest + SpamyTest + ShrinkControlFlowTest + ShrinkValueProfileTest + StrcmpTest + StrncmpOOBTest + StrncmpTest + StrstrTest + SwapCmpTest + SwitchTest + Switch2Test + ThreadedLeakTest + ThreadedTest + TimeoutTest + TimeoutEmptyTest + TraceMallocTest + ) + +if(APPLE) + # LeakSanitizer is not supported on OSX right now + set(HAS_LSAN 0) + message(WARNING "LeakSanitizer is not supported on Apple platforms." + " Building and running LibFuzzer LeakSanitizer tests is disabled." + ) +else() + set(HAS_LSAN 1) +endif() + +foreach(Test ${Tests}) + add_libfuzzer_test(${Test} SOURCES ${Test}.cpp) +endforeach() + + +############################################################################### +# Unit tests +############################################################################### + +add_executable(LLVMFuzzer-Unittest + FuzzerUnittest.cpp + ) + +add_executable(LLVMFuzzer-StandaloneInitializeTest + InitializeTest.cpp + ../standalone/StandaloneFuzzTargetMain.c + ) + +target_link_libraries(LLVMFuzzer-Unittest + gtest + gtest_main + LLVMFuzzerNoMain + ) + +target_include_directories(LLVMFuzzer-Unittest PRIVATE + "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include" + ) + +set(TestBinaries ${TestBinaries} LLVMFuzzer-Unittest) +set_target_properties(LLVMFuzzer-Unittest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}" +) + +set(TestBinaries ${TestBinaries} LLVMFuzzer-StandaloneInitializeTest) +set_target_properties(LLVMFuzzer-StandaloneInitializeTest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}" +) + +############################################################################### +# Additional tests +############################################################################### + +include_directories(..) + +# add_subdirectory(uninstrumented) +add_subdirectory(no-coverage) +add_subdirectory(ubsan) + +add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp) +add_library(LLVMFuzzer-DSO2 SHARED DSO2.cpp) + +add_executable(LLVMFuzzer-DSOTest + DSOTestMain.cpp + DSOTestExtra.cpp) + +target_link_libraries(LLVMFuzzer-DSOTest + LLVMFuzzer-DSO1 + LLVMFuzzer-DSO2 + LLVMFuzzer + ) + +set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") +set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") +set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") + +set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest) + +############################################################################### +# Configure lit to run the tests +# +# Note this is done after declaring all tests so we can inform lit if any tests +# need to be disabled. +############################################################################### + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg + ) + +add_lit_testsuite(check-fuzzer "Running Fuzzer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${TestBinaries} FileCheck not + ) diff --git a/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp b/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp new file mode 100644 index 00000000..3ec025d0 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp @@ -0,0 +1,59 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. +// Try to find the target using the indirect caller-callee pairs. +#include +#include +#include +#include +#include + +typedef void (*F)(); +static F t[256]; + +void f34() { + std::cerr << "BINGO\n"; + exit(1); +} +void f23() { t[(unsigned)'d'] = f34;} +void f12() { t[(unsigned)'c'] = f23;} +void f01() { t[(unsigned)'b'] = f12;} +void f00() {} + +static F t0[256] = { + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, + f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 4) return 0; + // Spoof the counters. + for (int i = 0; i < 200; i++) { + f23(); + f12(); + f01(); + } + memcpy(t, t0, sizeof(t)); + t[(unsigned)'a'] = f01; + t[Data[0]](); + t[Data[1]](); + t[Data[2]](); + t[Data[3]](); + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/CounterTest.cpp b/test/fuzz_test/Fuzzer/test/CounterTest.cpp new file mode 100644 index 00000000..4917934c --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CounterTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test for a fuzzer: must find the case where a particular basic block is +// executed many times. +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + int Num = 0; + for (size_t i = 0; i < Size; i++) + if (Data[i] == 'A' + i) + Num++; + if (Num >= 4) { + std::cerr << "BINGO!\n"; + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp b/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp new file mode 100644 index 00000000..b624088b --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp @@ -0,0 +1,63 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a cutom mutator. +#include +#include +#include +#include +#include +#include +#include + +#include "FuzzerInterface.h" + +static const char *Separator = "-_^_-"; +static const char *Target = "012-_^_-abc"; + +static volatile int sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + std::string Str(reinterpret_cast(Data), Size); + + // Ensure that two different elements exist in the corpus. + if (Size && Data[0] == '0') sink++; + if (Size && Data[0] == 'a') sink--; + + if (Str.find(Target) != std::string::npos) { + std::cout << "BINGO; Found the target, exiting\n"; + exit(1); + } + return 0; +} + +extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, + unsigned int Seed) { + static bool Printed; + static size_t SeparatorLen = strlen(Separator); + + if (!Printed) { + std::cerr << "In LLVMFuzzerCustomCrossover\n"; + Printed = true; + } + + std::mt19937 R(Seed); + + size_t Offset1 = 0; + size_t Len1 = R() % (Size1 - Offset1); + size_t Offset2 = 0; + size_t Len2 = R() % (Size2 - Offset2); + size_t Size = Len1 + Len2 + SeparatorLen; + + if (Size > MaxOutSize) + return 0; + + memcpy(Out, Data1 + Offset1, Len1); + memcpy(Out + Len1, Separator, SeparatorLen); + memcpy(Out + Len1 + SeparatorLen, Data2 + Offset2, Len2); + + return Len1 + Len2 + SeparatorLen; +} diff --git a/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp b/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp new file mode 100644 index 00000000..4f84519a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp @@ -0,0 +1,38 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a cutom mutator. +#include +#include +#include +#include +#include + +#include "FuzzerInterface.h" + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + std::cout << "BINGO; Found the target, exiting\n"; + exit(1); + } + } + } + return 0; +} + +extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) { + static bool Printed; + if (!Printed) { + std::cerr << "In LLVMFuzzerCustomMutator\n"; + Printed = true; + } + return LLVMFuzzerMutate(Data, Size, MaxSize); +} diff --git a/test/fuzz_test/Fuzzer/test/DSO1.cpp b/test/fuzz_test/Fuzzer/test/DSO1.cpp new file mode 100644 index 00000000..4a293890 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSO1.cpp @@ -0,0 +1,12 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +int DSO1(int a) { + if (a < 123456) + return 0; + return 1; +} + +void Uncovered1() { } diff --git a/test/fuzz_test/Fuzzer/test/DSO2.cpp b/test/fuzz_test/Fuzzer/test/DSO2.cpp new file mode 100644 index 00000000..04b308d1 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSO2.cpp @@ -0,0 +1,12 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +int DSO2(int a) { + if (a < 3598235) + return 0; + return 1; +} + +void Uncovered2() {} diff --git a/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp b/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp new file mode 100644 index 00000000..a2274d07 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp @@ -0,0 +1,11 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +int DSOTestExtra(int a) { + if (a < 452345) + return 0; + return 1; +} + diff --git a/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp b/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp new file mode 100644 index 00000000..3e225d88 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp @@ -0,0 +1,31 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Source code for a simple DSO. + +#include +#include +#include +#include +extern int DSO1(int a); +extern int DSO2(int a); +extern int DSOTestExtra(int a); + +static volatile int *nil = 0; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + int x, y, z; + if (Size < sizeof(int) * 3) { + x = y = z = 0; + } else { + memcpy(&x, Data + 0 * sizeof(int), sizeof(int)); + memcpy(&y, Data + 1 * sizeof(int), sizeof(int)); + memcpy(&z, Data + 2 * sizeof(int), sizeof(int)); + } + int sum = DSO1(x) + DSO2(y) + (z ? DSOTestExtra(z) : 0); + if (sum == 3) { + fprintf(stderr, "BINGO %d %d %d\n", x, y, z); + *nil = 0; + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/DivTest.cpp b/test/fuzz_test/Fuzzer/test/DivTest.cpp new file mode 100644 index 00000000..63f6960f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/DivTest.cpp @@ -0,0 +1,20 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer: find the interesting argument for div. +#include +#include +#include +#include +#include + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 4) return 0; + int a; + memcpy(&a, Data, 4); + Sink = 12345678 / (987654 - a); + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/EmptyTest.cpp b/test/fuzz_test/Fuzzer/test/EmptyTest.cpp new file mode 100644 index 00000000..5e843308 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/EmptyTest.cpp @@ -0,0 +1,11 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// A fuzzer with empty target function. + +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp b/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp new file mode 100644 index 00000000..62b3be76 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "FUZZ". +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + int bits = 0; + if (Size > 0 && Data[0] == 'F') bits |= 1; + if (Size > 1 && Data[1] == 'U') bits |= 2; + if (Size > 2 && Data[2] == 'Z') bits |= 4; + if (Size > 3 && Data[3] == 'Z') bits |= 8; + if (bits == 15) { + std::cerr << "BINGO!\n"; + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp b/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp new file mode 100644 index 00000000..415e0b47 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp @@ -0,0 +1,24 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "FUZZER". +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + int bits = 0; + if (Size > 0 && Data[0] == 'F') bits |= 1; + if (Size > 1 && Data[1] == 'U') bits |= 2; + if (Size > 2 && Data[2] == 'Z') bits |= 4; + if (Size > 3 && Data[3] == 'Z') bits |= 8; + if (Size > 4 && Data[4] == 'E') bits |= 16; + if (Size > 5 && Data[5] == 'R') bits |= 32; + if (bits == 63) { + std::cerr << "BINGO!\n"; + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp b/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp new file mode 100644 index 00000000..4992ef57 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp @@ -0,0 +1,738 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Avoid ODR violations (LibFuzzer is built without ASan and this test is built +// with ASan) involving C++ standard library types when using libcxx. +#define _LIBCPP_HAS_NO_ASAN + +#include "FuzzerCorpus.h" +#include "FuzzerInternal.h" +#include "FuzzerDictionary.h" +#include "FuzzerMerge.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include "gtest/gtest.h" +#include +#include + +using namespace fuzzer; + +// For now, have LLVMFuzzerTestOneInput just to make it link. +// Later we may want to make unittests that actually call LLVMFuzzerTestOneInput. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + abort(); +} + +TEST(Fuzzer, CrossOver) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + Unit A({0, 1, 2}), B({5, 6, 7}); + Unit C; + Unit Expected[] = { + { 0 }, + { 0, 1 }, + { 0, 5 }, + { 0, 1, 2 }, + { 0, 1, 5 }, + { 0, 5, 1 }, + { 0, 5, 6 }, + { 0, 1, 2, 5 }, + { 0, 1, 5, 2 }, + { 0, 1, 5, 6 }, + { 0, 5, 1, 2 }, + { 0, 5, 1, 6 }, + { 0, 5, 6, 1 }, + { 0, 5, 6, 7 }, + { 0, 1, 2, 5, 6 }, + { 0, 1, 5, 2, 6 }, + { 0, 1, 5, 6, 2 }, + { 0, 1, 5, 6, 7 }, + { 0, 5, 1, 2, 6 }, + { 0, 5, 1, 6, 2 }, + { 0, 5, 1, 6, 7 }, + { 0, 5, 6, 1, 2 }, + { 0, 5, 6, 1, 7 }, + { 0, 5, 6, 7, 1 }, + { 0, 1, 2, 5, 6, 7 }, + { 0, 1, 5, 2, 6, 7 }, + { 0, 1, 5, 6, 2, 7 }, + { 0, 1, 5, 6, 7, 2 }, + { 0, 5, 1, 2, 6, 7 }, + { 0, 5, 1, 6, 2, 7 }, + { 0, 5, 1, 6, 7, 2 }, + { 0, 5, 6, 1, 2, 7 }, + { 0, 5, 6, 1, 7, 2 }, + { 0, 5, 6, 7, 1, 2 } + }; + for (size_t Len = 1; Len < 8; Len++) { + std::set FoundUnits, ExpectedUnitsWitThisLength; + for (int Iter = 0; Iter < 3000; Iter++) { + C.resize(Len); + size_t NewSize = MD.CrossOver(A.data(), A.size(), B.data(), B.size(), + C.data(), C.size()); + C.resize(NewSize); + FoundUnits.insert(C); + } + for (const Unit &U : Expected) + if (U.size() <= Len) + ExpectedUnitsWitThisLength.insert(U); + EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits); + } +} + +TEST(Fuzzer, Hash) { + uint8_t A[] = {'a', 'b', 'c'}; + fuzzer::Unit U(A, A + sizeof(A)); + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", fuzzer::Hash(U)); + U.push_back('d'); + EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U)); +} + +typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size, + size_t MaxSize); + +void TestEraseBytes(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77}; + uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77}; + uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77}; + uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + + uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; + uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77}; + + uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44}; + uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77}; + + + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, sizeof(T), sizeof(T)); + if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(REM3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(REM4, T, 7)) FoundMask |= 1 << 4; + if (NewSize == 7 && !memcmp(REM5, T, 7)) FoundMask |= 1 << 5; + if (NewSize == 7 && !memcmp(REM6, T, 7)) FoundMask |= 1 << 6; + if (NewSize == 7 && !memcmp(REM7, T, 7)) FoundMask |= 1 << 7; + + if (NewSize == 6 && !memcmp(REM8, T, 6)) FoundMask |= 1 << 8; + if (NewSize == 6 && !memcmp(REM9, T, 6)) FoundMask |= 1 << 9; + if (NewSize == 6 && !memcmp(REM10, T, 6)) FoundMask |= 1 << 10; + + if (NewSize == 5 && !memcmp(REM11, T, 5)) FoundMask |= 1 << 11; + if (NewSize == 5 && !memcmp(REM12, T, 5)) FoundMask |= 1 << 12; + if (NewSize == 5 && !memcmp(REM13, T, 5)) FoundMask |= 1 << 13; + } + EXPECT_EQ(FoundMask, (1 << 14) - 1); +} + +TEST(FuzzerMutate, EraseBytes1) { + TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200); +} +TEST(FuzzerMutate, EraseBytes2) { + TestEraseBytes(&MutationDispatcher::Mutate, 2000); +} + +void TestInsertByte(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66}; + uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66}; + uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66}; + uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66}; + uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + size_t NewSize = (MD.*M)(T, 7, 8); + if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0; + if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1; + if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2; + if (NewSize == 8 && !memcmp(INS3, T, 8)) FoundMask |= 1 << 3; + if (NewSize == 8 && !memcmp(INS4, T, 8)) FoundMask |= 1 << 4; + if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, InsertByte1) { + TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15); +} +TEST(FuzzerMutate, InsertByte2) { + TestInsertByte(&MutationDispatcher::Mutate, 1 << 17); +} + +void TestInsertRepeatedBytes(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'}; + uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33}; + uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33}; + uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33}; + uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33}; + + uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'}; + uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33}; + uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33}; + uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33}; + uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33}; + + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33}; + size_t NewSize = (MD.*M)(T, 4, 8); + if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(INS3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(INS4, T, 7)) FoundMask |= 1 << 4; + + if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7; + if (NewSize == 8 && !memcmp(INS8, T, 8)) FoundMask |= 1 << 8; + if (NewSize == 8 && !memcmp(INS9, T, 8)) FoundMask |= 1 << 9; + + } + EXPECT_EQ(FoundMask, (1 << 10) - 1); +} + +TEST(FuzzerMutate, InsertRepeatedBytes1) { + TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes, 10000); +} +TEST(FuzzerMutate, InsertRepeatedBytes2) { + TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000); +} + +void TestChangeByte(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77}; + uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77}; + uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77}; + uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 8, 9); + if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4; + if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, ChangeByte1) { + TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15); +} +TEST(FuzzerMutate, ChangeByte2) { + TestChangeByte(&MutationDispatcher::Mutate, 1 << 17); +} + +void TestChangeBit(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77}; + uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77}; + uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77}; + uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 8, 9); + if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4; + if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, ChangeBit1) { + TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16); +} +TEST(FuzzerMutate, ChangeBit2) { + TestChangeBit(&MutationDispatcher::Mutate, 1 << 18); +} + +void TestShuffleBytes(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66}; + uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66}; + uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66}; + uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33}; + uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + size_t NewSize = (MD.*M)(T, 7, 7); + if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4; + } + EXPECT_EQ(FoundMask, 31); +} + +TEST(FuzzerMutate, ShuffleBytes1) { + TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16); +} +TEST(FuzzerMutate, ShuffleBytes2) { + TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20); +} + +void TestCopyPart(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + int FoundMask = 0; + uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11}; + uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66}; + uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66}; + uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66}; + uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66}; + + for (int i = 0; i < NumIter; i++) { + uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + size_t NewSize = (MD.*M)(T, 7, 7); + if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4; + } + + uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22}; + uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44}; + uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44}; + uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44}; + uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44}; + + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 5, 8); + if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + if (NewSize == 8 && !memcmp(CH8, T, 8)) FoundMask |= 1 << 8; + if (NewSize == 8 && !memcmp(CH9, T, 8)) FoundMask |= 1 << 9; + } + + EXPECT_EQ(FoundMask, 1023); +} + +TEST(FuzzerMutate, CopyPart1) { + TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10); +} +TEST(FuzzerMutate, CopyPart2) { + TestCopyPart(&MutationDispatcher::Mutate, 1 << 13); +} + +void TestAddWordFromDictionary(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t Word2[3] = {0xFF, 0xEE, 0xEF}; + MD.AddWordToManualDictionary(Word(Word1, sizeof(Word1))); + MD.AddWordToManualDictionary(Word(Word2, sizeof(Word2))); + int FoundMask = 0; + uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD}; + uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22}; + uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22}; + uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22}; + uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF}; + uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22}; + uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22}; + uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22}; + for (int i = 0; i < NumIter; i++) { + uint8_t T[7] = {0x00, 0x11, 0x22}; + size_t NewSize = (MD.*M)(T, 3, 7); + if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0; + if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1; + if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2; + if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3; + if (NewSize == 6 && !memcmp(CH4, T, 6)) FoundMask |= 1 << 4; + if (NewSize == 6 && !memcmp(CH5, T, 6)) FoundMask |= 1 << 5; + if (NewSize == 6 && !memcmp(CH6, T, 6)) FoundMask |= 1 << 6; + if (NewSize == 6 && !memcmp(CH7, T, 6)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, AddWordFromDictionary1) { + TestAddWordFromDictionary( + &MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15); +} + +TEST(FuzzerMutate, AddWordFromDictionary2) { + TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15); +} + +void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + uint8_t W[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xEE, 0xEF}; + size_t PosHint = 7777; + MD.AddWordToAutoDictionary({Word(W, sizeof(W)), PosHint}); + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[10000]; + memset(T, 0, sizeof(T)); + size_t NewSize = (MD.*M)(T, 9000, 10000); + if (NewSize >= PosHint + sizeof(W) && + !memcmp(W, T + PosHint, sizeof(W))) + FoundMask = 1; + } + EXPECT_EQ(FoundMask, 1); +} + +TEST(FuzzerMutate, AddWordFromDictionaryWithHint1) { + TestAddWordFromDictionaryWithHint( + &MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, 1 << 5); +} + +TEST(FuzzerMutate, AddWordFromDictionaryWithHint2) { + TestAddWordFromDictionaryWithHint(&MutationDispatcher::Mutate, 1 << 10); +} + +void TestChangeASCIIInteger(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + + uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'}; + uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'}; + uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'}; + uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'}; + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'}; + size_t NewSize = (MD.*M)(T, 8, 8); + /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + else if (NewSize == 8) FoundMask |= 1 << 4; + } + EXPECT_EQ(FoundMask, 31); +} + +TEST(FuzzerMutate, ChangeASCIIInteger1) { + TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger, + 1 << 15); +} + +TEST(FuzzerMutate, ChangeASCIIInteger2) { + TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15); +} + +void TestChangeBinaryInteger(Mutator M, int NumIter) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + MutationDispatcher MD(Rand, {}); + + uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79}; + uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77}; + uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77}; + uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88}; + uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size + uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size) + + int FoundMask = 0; + for (int i = 0; i < NumIter; i++) { + uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + size_t NewSize = (MD.*M)(T, 8, 8); + /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0; + else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1; + else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2; + else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3; + else if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4; + else if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5; + else if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6; + else if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7; + } + EXPECT_EQ(FoundMask, 255); +} + +TEST(FuzzerMutate, ChangeBinaryInteger1) { + TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger, + 1 << 12); +} + +TEST(FuzzerMutate, ChangeBinaryInteger2) { + TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15); +} + + +TEST(FuzzerDictionary, ParseOneDictionaryEntry) { + Unit U; + EXPECT_FALSE(ParseOneDictionaryEntry("", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry("\t ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" \" ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" zz\" ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" \"zz ", &U)); + EXPECT_FALSE(ParseOneDictionaryEntry(" \"\" ", &U)); + EXPECT_TRUE(ParseOneDictionaryEntry("\"a\"", &U)); + EXPECT_EQ(U, Unit({'a'})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"abc\"", &U)); + EXPECT_EQ(U, Unit({'a', 'b', 'c'})); + EXPECT_TRUE(ParseOneDictionaryEntry("abc=\"abc\"", &U)); + EXPECT_EQ(U, Unit({'a', 'b', 'c'})); + EXPECT_FALSE(ParseOneDictionaryEntry("\"\\\"", &U)); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\\\"", &U)); + EXPECT_EQ(U, Unit({'\\'})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xAB\"", &U)); + EXPECT_EQ(U, Unit({0xAB})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xABz\\xDE\"", &U)); + EXPECT_EQ(U, Unit({0xAB, 'z', 0xDE})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"#\"", &U)); + EXPECT_EQ(U, Unit({'#'})); + EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\"\"", &U)); + EXPECT_EQ(U, Unit({'"'})); +} + +TEST(FuzzerDictionary, ParseDictionaryFile) { + std::vector Units; + EXPECT_FALSE(ParseDictionaryFile("zzz\n", &Units)); + EXPECT_FALSE(ParseDictionaryFile("", &Units)); + EXPECT_TRUE(ParseDictionaryFile("\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile("#zzzz a b c d\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units)); + EXPECT_EQ(Units.size(), 0U); + EXPECT_TRUE(ParseDictionaryFile(" #zzzz\naaa=\"aa\"", &Units)); + EXPECT_EQ(Units, std::vector({Unit({'a', 'a'})})); + EXPECT_TRUE( + ParseDictionaryFile(" #zzzz\naaa=\"aa\"\n\nabc=\"abc\"", &Units)); + EXPECT_EQ(Units, + std::vector({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})})); +} + +TEST(FuzzerUtil, Base64) { + EXPECT_EQ("", Base64({})); + EXPECT_EQ("YQ==", Base64({'a'})); + EXPECT_EQ("eA==", Base64({'x'})); + EXPECT_EQ("YWI=", Base64({'a', 'b'})); + EXPECT_EQ("eHk=", Base64({'x', 'y'})); + EXPECT_EQ("YWJj", Base64({'a', 'b', 'c'})); + EXPECT_EQ("eHl6", Base64({'x', 'y', 'z'})); + EXPECT_EQ("YWJjeA==", Base64({'a', 'b', 'c', 'x'})); + EXPECT_EQ("YWJjeHk=", Base64({'a', 'b', 'c', 'x', 'y'})); + EXPECT_EQ("YWJjeHl6", Base64({'a', 'b', 'c', 'x', 'y', 'z'})); +} + +TEST(Corpus, Distribution) { + Random Rand(0); + InputCorpus C(""); + size_t N = 10; + size_t TriesPerUnit = 1<<16; + for (size_t i = 0; i < N; i++) + C.AddToCorpus(Unit{ static_cast(i) }, 0); + + std::vector Hist(N); + for (size_t i = 0; i < N * TriesPerUnit; i++) { + Hist[C.ChooseUnitIdxToMutate(Rand)]++; + } + for (size_t i = 0; i < N; i++) { + // A weak sanity check that every unit gets invoked. + EXPECT_GT(Hist[i], TriesPerUnit / N / 3); + } +} + +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\nDONE 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)); + } +} + +void EQ(const std::vector &A, const std::vector &B) { + EXPECT_EQ(A, B); +} + +void EQ(const std::vector &A, const std::vector &B) { + std::set a(A.begin(), A.end()); + std::set b(B.begin(), B.end()); + EXPECT_EQ(a, b); +} + +static void Merge(const std::string &Input, + const std::vector Result, + size_t NumNewFeatures) { + Merger M; + std::vector NewFiles; + EXPECT_TRUE(M.Parse(Input, true)); + EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles)); + EQ(NewFiles, Result); +} + +TEST(Merge, Good) { + Merger M; + + EXPECT_TRUE(M.Parse("1\n0\nAA\n", false)); + 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); + + EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false)); + 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); + + EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n" + "STARTED 0 1000\n" + "DONE 0 1 2 3\n" + "STARTED 1 1001\n" + "DONE 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); + EXPECT_EQ(M.Files[1].Name, "BB"); + EXPECT_EQ(M.Files[1].Size, 1001U); + EXPECT_EQ(M.Files[2].Name, "C"); + EXPECT_EQ(M.Files[2].Size, 1002U); + EXPECT_EQ(M.LastFailure, "C"); + EXPECT_EQ(M.FirstNotProcessedFile, 3U); + EQ(M.Files[0].Features, {1, 2, 3}); + EQ(M.Files[1].Features, {4, 5, 6}); + + + std::vector NewFiles; + + EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n" + "STARTED 0 1000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 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); + 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(&NewFiles)); + EQ(NewFiles, {}); + + EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n" + "STARTED 0 1000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 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(&NewFiles)); + EQ(NewFiles, {"B"}); +} + +TEST(Merge, Merge) { + + Merge("3\n1\nA\nB\nC\n" + "STARTED 0 1000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 2 6 1 3 \n", + {"B"}, 3); + + Merge("3\n0\nA\nB\nC\n" + "STARTED 0 2000\nDONE 0 1 2 3\n" + "STARTED 1 1001\nDONE 1 4 5 6 \n" + "STARTED 2 1002\nDONE 2 6 1 3 \n", + {"A", "B", "C"}, 6); + + Merge("4\n0\nA\nB\nC\nD\n" + "STARTED 0 2000\nDONE 0 1 2 3\n" + "STARTED 1 1101\nDONE 1 4 5 6 \n" + "STARTED 2 1102\nDONE 2 6 1 3 100 \n" + "STARTED 3 1000\nDONE 3 1 \n", + {"A", "B", "C", "D"}, 7); + + Merge("4\n1\nA\nB\nC\nD\n" + "STARTED 0 2000\nDONE 0 4 5 6 7 8\n" + "STARTED 1 1100\nDONE 1 1 2 3 \n" + "STARTED 2 1100\nDONE 2 2 3 \n" + "STARTED 3 1000\nDONE 3 1 \n", + {"B", "D"}, 3); +} diff --git a/test/fuzz_test/Fuzzer/test/InitializeTest.cpp b/test/fuzz_test/Fuzzer/test/InitializeTest.cpp new file mode 100644 index 00000000..0d6a0fda --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/InitializeTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Make sure LLVMFuzzerInitialize is called. +#include +#include +#include +#include +#include +#include + +static char *argv0; + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + assert(*argc > 0); + argv0 = **argv; + fprintf(stderr, "LLVMFuzzerInitialize: %s\n", argv0); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == strlen(argv0) && + !strncmp(reinterpret_cast(Data), argv0, Size)) { + fprintf(stderr, "BINGO %s\n", argv0); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/LeakTest.cpp b/test/fuzz_test/Fuzzer/test/LeakTest.cpp new file mode 100644 index 00000000..22e51640 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/LeakTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test with a leak. +#include +#include + +static volatile void *Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && *Data == 'H') { + Sink = new int; + Sink = nullptr; + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp b/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp new file mode 100644 index 00000000..4f31b3e5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test with a leak. +#include +#include + +static volatile int *Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (!Size) return 0; + Sink = new int; + Sink = new int; + while (Sink) *Sink = 0; // Infinite loop. + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/LoadTest.cpp b/test/fuzz_test/Fuzzer/test/LoadTest.cpp new file mode 100644 index 00000000..c1780d5c --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/LoadTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer: find interesting value of array index. +#include +#include +#include +#include +#include + +static volatile int Sink; +const int kArraySize = 1234567; +int array[kArraySize]; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 8) return 0; + size_t a = 0; + memcpy(&a, Data, 8); + Sink = array[a % (kArraySize + 1)]; + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp b/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp new file mode 100644 index 00000000..fdbf9468 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp @@ -0,0 +1,31 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + // TODO: check other sizes. + if (Size >= 8 && memcmp(Data, "01234567", 8) == 0) { + if (Size >= 12 && memcmp(Data + 8, "ABCD", 4) == 0) { + if (Size >= 14 && memcmp(Data + 12, "XY", 2) == 0) { + if (Size >= 17 && memcmp(Data + 14, "KLM", 3) == 0) { + if (Size >= 27 && memcmp(Data + 17, "ABCDE-GHIJ", 10) == 0){ + fprintf(stderr, "BINGO %zd\n", Size); + for (size_t i = 0; i < Size; i++) { + uint8_t C = Data[i]; + if (C >= 32 && C < 127) + fprintf(stderr, "%c", C); + } + fprintf(stderr, "\n"); + exit(1); + } + } + } + } + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp b/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp new file mode 100644 index 00000000..b43e69e5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Crash on the N-th execution. +#include +#include +#include + +static int Counter; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Counter++ == 1000) { + std::cout << "BINGO; Found the target, exiting\n"; + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp b/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp new file mode 100644 index 00000000..15371092 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp @@ -0,0 +1,19 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the empty string. +#include +#include +#include +#include + +static volatile int *Null = 0; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == 0) { + std::cout << "Found the target, dereferencing NULL\n"; + *Null = 1; + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp b/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp new file mode 100644 index 00000000..3f03d249 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp @@ -0,0 +1,26 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include +#include + +static volatile int Sink; +static volatile int *Null = 0; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + std::cout << "Found the target, dereferencing NULL\n"; + *Null = 1; + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp b/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp new file mode 100644 index 00000000..8d3d1d6d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests OOM handling when there is a single large allocation. +#include +#include +#include +#include +#include +#include + +static volatile char *SinkPtr; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'H') { + if (Size > 1 && Data[1] == 'i') { + if (Size > 2 && Data[2] == '!') { + size_t kSize = (size_t)1 << 31; + char *p = new char[kSize]; + memset(p, 0, kSize); + SinkPtr = p; + delete [] p; + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp new file mode 100644 index 00000000..ea23a601 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests OOM handling. +#include +#include +#include +#include +#include +#include + +static volatile char *SinkPtr; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'H') { + if (Size > 1 && Data[1] == 'i') { + if (Size > 2 && Data[2] == '!') { + size_t kSize = 0xff000000U; + char *p = new char[kSize]; + SinkPtr = p; + delete [] p; + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp b/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp new file mode 100644 index 00000000..078a39ee --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp @@ -0,0 +1,31 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests OOM handling. +#include +#include +#include +#include +#include +#include +#include + +static volatile char *SinkPtr; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'H') { + if (Size > 1 && Data[1] == 'i') { + if (Size > 2 && Data[2] == '!') { + while (true) { + size_t kSize = 1 << 28; + char *p = new char[kSize]; + memset(p, 0, kSize); + SinkPtr = p; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp b/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp new file mode 100644 index 00000000..2fa6c78c --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp @@ -0,0 +1,29 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find repeated bytes. +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + // Looking for AAAAAAAAAAAAAAAAAAAAAA or some such. + size_t CurA = 0, MaxA = 0; + for (size_t i = 0; i < Size; i++) { + // Make sure there are no conditionals in the loop so that + // coverage can't help the fuzzer. + int EQ = Data[i] == 'A'; + CurA = EQ * (CurA + 1); + int GT = CurA > MaxA; + MaxA = GT * CurA + (!GT) * MaxA; + } + if (MaxA >= 20) { + std::cout << "BINGO; Found the target (Max: " << MaxA << "), exiting\n"; + exit(0); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp b/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp new file mode 100644 index 00000000..a327bbee --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + + +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + int Matches = 0; + for (size_t i = 0; i + 2 < Size; i += 3) { + const char *Pat = i % 2 ? "foo" : "bar"; + if (!memcmp(Data + i, Pat, 3)) + Matches++; + } + if (Matches > 20) { + fprintf(stderr, "BINGO!\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp b/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp new file mode 100644 index 00000000..0fd7c5e9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). +#include +#include +#include +#include +#include + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + int8_t Ids[256]; + memset(Ids, -1, sizeof(Ids)); + for (size_t i = 0; i < Size; i++) + if (Ids[Data[i]] == -1) + Ids[Data[i]] = i; + int F = Ids[(unsigned char)'F']; + int U = Ids[(unsigned char)'U']; + int Z = Ids[(unsigned char)'Z']; + if (F >= 0 && U > F && Z > U) { + Sink++; + //fprintf(stderr, "IDS: %d %d %d\n", F, U, Z); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp b/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp new file mode 100644 index 00000000..026b8ce2 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). +#include +#include +#include +#include +#include + +static volatile uint32_t Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < sizeof(uint32_t)) return 0; + uint32_t X, Y; + size_t Offset = Size < 8 ? 0 : Size / 2; + memcpy(&X, Data + Offset, sizeof(uint32_t)); + memcpy(&Y, "FUZZ", sizeof(uint32_t)); + Sink = X == Y; + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp b/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp new file mode 100644 index 00000000..7df32ad5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test for signed-integer-overflow. +#include +#include +#include +#include +#include +#include + +static volatile int Sink; +static int Large = INT_MAX; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + Large++; // int overflow. + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp new file mode 100644 index 00000000..0220c30f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp @@ -0,0 +1,46 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find several narrow ranges. +#include +#include +#include +#include + +extern int AllLines[]; + +bool PrintOnce(int Line) { + if (!AllLines[Line]) + fprintf(stderr, "Seen line %d\n", Line); + AllLines[Line] = 1; + return true; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size != 22) return 0; + uint64_t x = 0; + int64_t y = 0; + int32_t z = 0; + uint16_t a = 0; + memcpy(&x, Data, 8); // 8 + memcpy(&y, Data + 8, 8); // 16 + memcpy(&z, Data + 16, sizeof(z)); // 20 + memcpy(&a, Data + 20, sizeof(a)); // 22 + + if (x > 1234567890 && PrintOnce(__LINE__) && + x < 1234567895 && PrintOnce(__LINE__) && + a == 0x4242 && PrintOnce(__LINE__) && + y >= 987654321 && PrintOnce(__LINE__) && + y <= 987654325 && PrintOnce(__LINE__) && + z < -10000 && PrintOnce(__LINE__) && + z >= -10005 && PrintOnce(__LINE__) && + z != -10003 && PrintOnce(__LINE__) && + true) { + fprintf(stderr, "BINGO; Found the target: size %zd (%zd, %zd, %d, %d), exiting.\n", + Size, x, y, z, a); + exit(1); + } + return 0; +} + +int AllLines[__LINE__ + 1]; // Must be the last line. diff --git a/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp new file mode 100644 index 00000000..cd7292bd --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp @@ -0,0 +1,29 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. +// The fuzzer must find a string based on dictionary words: +// "Elvis" +// "Presley" +#include +#include +#include +#include +#include + +static volatile int Zero = 0; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + const char *Expected = "ElvisPresley"; + if (Size < strlen(Expected)) return 0; + size_t Match = 0; + for (size_t i = 0; Expected[i]; i++) + if (Expected[i] + Zero == Data[i]) + Match++; + if (Match == strlen(Expected)) { + std::cout << "BINGO; Found the target, exiting\n"; + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp new file mode 100644 index 00000000..00599de7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp @@ -0,0 +1,40 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// This test computes a checksum of the data (all but the last 4 bytes), +// and then compares the last 4 bytes with the computed value. +// A fuzzer with cmp traces is expected to defeat this check. +#include +#include +#include +#include + +// A modified jenkins_one_at_a_time_hash initialized by non-zero, +// so that simple_hash(0) != 0. See also +// https://en.wikipedia.org/wiki/Jenkins_hash_function +static uint32_t simple_hash(const uint8_t *Data, size_t Size) { + uint32_t Hash = 0x12039854; + for (uint32_t i = 0; i < Size; i++) { + Hash += Data[i]; + Hash += (Hash << 10); + Hash ^= (Hash >> 6); + } + Hash += (Hash << 3); + Hash ^= (Hash >> 11); + Hash += (Hash << 15); + return Hash; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 14) + return 0; + + uint32_t Hash = simple_hash(&Data[0], Size - 4); + uint32_t Want = reinterpret_cast(&Data[Size - 4])[0]; + if (Hash != Want) + return 0; + fprintf(stderr, "BINGO; simple_hash defeated: %x == %x\n", (unsigned int)Hash, + (unsigned int)Want); + exit(1); + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/SimpleTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleTest.cpp new file mode 100644 index 00000000..e53ea160 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include +#include +#include + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + std::cout << "BINGO; Found the target, exiting\n"; + exit(0); + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp b/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp new file mode 100644 index 00000000..5f02d3f8 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp @@ -0,0 +1,25 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Threaded test for a fuzzer. The fuzzer should find "H" +#include +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + auto C = [&] { + if (Size >= 2 && Data[0] == 'H') { + std::cout << "BINGO; Found the target, exiting\n"; + abort(); + } + }; + std::thread T[] = {std::thread(C), std::thread(C), std::thread(C), + std::thread(C), std::thread(C), std::thread(C)}; + for (auto &X : T) + X.join(); + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp b/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp new file mode 100644 index 00000000..c73f68a7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 6 && !memcmp(S, "qwerty", 6)) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp b/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp new file mode 100644 index 00000000..73470b52 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 7 && !strcmp(S, "qwerty")) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp b/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp new file mode 100644 index 00000000..dbcc464b --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + char *S = (char*)Data; + if (Size >= 6 && !strncmp(S, "qwerty", 6)) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/SpamyTest.cpp b/test/fuzz_test/Fuzzer/test/SpamyTest.cpp new file mode 100644 index 00000000..d294d4dc --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SpamyTest.cpp @@ -0,0 +1,21 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// The test spams to stderr and stdout. +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + printf("PRINTF_STDOUT\n"); + fflush(stdout); + fprintf(stderr, "PRINTF_STDERR\n"); + std::cout << "STREAM_COUT\n"; + std::cout.flush(); + std::cerr << "STREAM_CERR\n"; + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp b/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp new file mode 100644 index 00000000..cd91dda7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp @@ -0,0 +1,32 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Break through a series of strcmp. +#include +#include +#include +#include +#include + +bool Eq(const uint8_t *Data, size_t Size, const char *Str) { + char Buff[1024]; + size_t Len = strlen(Str); + if (Size < Len) return false; + if (Len >= sizeof(Buff)) return false; + memcpy(Buff, (char*)Data, Len); + Buff[Len] = 0; + int res = strcmp(Buff, Str); + return res == 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Eq(Data, Size, "ABC") && + Size >= 3 && Eq(Data + 3, Size - 3, "QWER") && + Size >= 7 && Eq(Data + 7, Size - 7, "ZXCVN") && + Size >= 14 && Data[13] == 42 + ) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp b/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp new file mode 100644 index 00000000..f70b003a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp @@ -0,0 +1,21 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that libFuzzer itself does not read out of bounds. +#include +#include +#include +#include +#include +#include + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 5) return 0; + const char *Ch = reinterpret_cast(Data); + if (Ch[Size - 3] == 'a') + Sink = strncmp(Ch + Size - 3, "abcdefg", 6); + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp b/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp new file mode 100644 index 00000000..5ffd011d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include +#include +#include +#include + +static volatile int sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + // TODO: check other sizes. + char *S = (char*)Data; + if (Size >= 8 && strncmp(S, "123", 8)) + sink = 1; + if (Size >= 8 && strncmp(S, "01234567", 8) == 0) { + if (Size >= 12 && strncmp(S + 8, "ABCD", 4) == 0) { + if (Size >= 14 && strncmp(S + 12, "XY", 2) == 0) { + if (Size >= 17 && strncmp(S + 14, "KLM", 3) == 0) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + } + } + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/StrstrTest.cpp b/test/fuzz_test/Fuzzer/test/StrstrTest.cpp new file mode 100644 index 00000000..f021e75e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/StrstrTest.cpp @@ -0,0 +1,28 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test strstr and strcasestr hooks. +#include +#include +#include +#include +#include + +// Windows does not have strcasestr and memmem, so we are not testing them. +#ifdef _WIN32 +#define strcasestr strstr +#define memmem(a, b, c, d) true +#endif + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 4) return 0; + std::string s(reinterpret_cast(Data), Size); + if (strstr(s.c_str(), "FUZZ") && + strcasestr(s.c_str(), "aBcD") && + memmem(s.data(), s.size(), "kuku", 4) + ) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp b/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp new file mode 100644 index 00000000..f79db4cc --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp @@ -0,0 +1,34 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// The fuzzer must find several constants with swapped bytes. +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 14) return 0; + uint64_t x = 0; + uint32_t y = 0; + uint16_t z = 0; + memcpy(&x, Data, sizeof(x)); + memcpy(&y, Data + Size / 2, sizeof(y)); + memcpy(&z, Data + Size - sizeof(z), sizeof(z)); + + x = __builtin_bswap64(x); + y = __builtin_bswap32(y); + z = __builtin_bswap16(z); + + if (x == 0x46555A5A5A5A5546ULL && + z == 0x4F4B && + y == 0x66757A7A && + true + ) { + if (Data[Size - 3] == 'z') { + fprintf(stderr, "BINGO; Found the target\n"); + exit(1); + } + } + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/Switch2Test.cpp b/test/fuzz_test/Fuzzer/test/Switch2Test.cpp new file mode 100644 index 00000000..3c6a3004 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/Switch2Test.cpp @@ -0,0 +1,35 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the interesting switch value. +#include +#include +#include +#include +#include + +int Switch(int a) { + switch(a) { + case 100001: return 1; + case 100002: return 2; + case 100003: return 4; + } + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + const int N = 3; + if (Size < N * sizeof(int)) return 0; + int Res = 0; + for (int i = 0; i < N; i++) { + int X; + memcpy(&X, Data + i * sizeof(int), sizeof(int)); + Res += Switch(X); + } + if (Res == 5 || Res == 3 || Res == 6 || Res == 7) { + fprintf(stderr, "BINGO; Found the target, exiting; Res=%d\n", Res); + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/SwitchTest.cpp b/test/fuzz_test/Fuzzer/test/SwitchTest.cpp new file mode 100644 index 00000000..74e86c06 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/SwitchTest.cpp @@ -0,0 +1,58 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the interesting switch value. +#include +#include +#include +#include +#include + +static volatile int Sink; + +template +bool Switch(const uint8_t *Data, size_t Size) { + T X; + if (Size < sizeof(X)) return false; + memcpy(&X, Data, sizeof(X)); + switch (X) { + case 1: Sink = __LINE__; break; + case 101: Sink = __LINE__; break; + case 1001: Sink = __LINE__; break; + case 10001: Sink = __LINE__; break; +// case 100001: Sink = __LINE__; break; +// case 1000001: Sink = __LINE__; break; + case 10000001: Sink = __LINE__; break; + case 100000001: return true; + } + return false; +} + +bool ShortSwitch(const uint8_t *Data, size_t Size) { + short X; + if (Size < sizeof(short)) return false; + memcpy(&X, Data, sizeof(short)); + switch(X) { + case 42: Sink = __LINE__; break; + case 402: Sink = __LINE__; break; + case 4002: Sink = __LINE__; break; + case 5002: Sink = __LINE__; break; + case 7002: Sink = __LINE__; break; + case 9002: Sink = __LINE__; break; + case 14002: Sink = __LINE__; break; + case 21402: return true; + } + return false; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size >= 4 && Switch(Data, Size) && + Size >= 12 && Switch(Data + 4, Size - 4) && + Size >= 14 && ShortSwitch(Data + 12, 2) + ) { + fprintf(stderr, "BINGO; Found the target, exiting\n"); + exit(1); + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp b/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp new file mode 100644 index 00000000..75110711 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// The fuzzer should find a leak in a non-main thread. +#include +#include +#include + +static volatile int *Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size == 0) return 0; + if (Data[0] != 'F') return 0; + std::thread T([&] { Sink = new int; }); + T.join(); + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp b/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp new file mode 100644 index 00000000..09137a9a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp @@ -0,0 +1,26 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Threaded test for a fuzzer. The fuzzer should not crash. +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 8) return 0; + assert(Data); + auto C = [&] { + size_t Res = 0; + for (size_t i = 0; i < Size / 2; i++) + Res += memcmp(Data, Data + Size / 2, 4); + return Res; + }; + std::thread T[] = {std::thread(C), std::thread(C), std::thread(C), + std::thread(C), std::thread(C), std::thread(C)}; + for (auto &X : T) + X.join(); + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp b/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp new file mode 100644 index 00000000..8066f480 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp @@ -0,0 +1,14 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the empty string. +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static volatile int Zero = 0; + if (!Size) + while(!Zero) + ; + return 0; +} diff --git a/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp b/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp new file mode 100644 index 00000000..f8107012 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp @@ -0,0 +1,26 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include +#include + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + Sink = 2; + while (Sink) + ; + } + } + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp b/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp new file mode 100644 index 00000000..43e6950e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Tests -trace_malloc +#include +#include +#include +#include +#include + +int *Ptr; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (!Size) return 0; + if (*Data == 1) { + delete Ptr; + Ptr = nullptr; + } else if (*Data == 2) { + delete Ptr; + Ptr = new int; + } else if (*Data == 3) { + if (!Ptr) + Ptr = new int; + } + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp b/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp new file mode 100644 index 00000000..ffe952c7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp @@ -0,0 +1,11 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// This test should not be instrumented. +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return 0; +} + diff --git a/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test b/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test new file mode 100644 index 00000000..81e384e7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test @@ -0,0 +1,28 @@ +; Test that not specifying an extra stats file isn't broken. +RUN: unset AFL_DRIVER_EXTRA_STATS_FILENAME +RUN: AFLDriverTest + +; Test that specifying an invalid extra stats file causes a crash. +RUN: ASAN_OPTIONS= AFL_DRIVER_EXTRA_STATS_FILENAME=%T not --crash AFLDriverTest + +; Test that specifying a corrupted stats file causes a crash. +echo "peak_rss_mb :0" > %t +ASAN_OPTIONS= AFL_DRIVER_EXTRA_STATS_FILENAME=%t not --crash AFLDriverTest + +; Test that specifying a valid nonexistent stats file works. +RUN: rm -f %t +RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest +RUN: [[ $(grep "peak_rss_mb\|slowest_unit_time_sec" %t | wc -l) -eq 2 ]] + +; Test that specifying a valid preexisting stats file works. +RUN: printf "peak_rss_mb : 0\nslowest_unit_time_sec: 0\n" > %t +RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest +; Check that both lines were printed. +RUN: [[ $(grep "peak_rss_mb\|slowest_unit_time_sec" %t | wc -l) -eq 2 ]] + +; Test that peak_rss_mb and slowest_unit_time_in_secs are only updated when necessary. +; Check that both lines have 9999 since there's no way we have exceeded that +; amount of time or virtual memory. +RUN: printf "peak_rss_mb : 9999\nslowest_unit_time_sec: 9999\n" > %t +RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest +RUN: [[ $(grep "9999" %t | wc -l) -eq 2 ]] diff --git a/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test b/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test new file mode 100644 index 00000000..c0f9c839 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test @@ -0,0 +1,10 @@ +; Test that not specifying a stderr file isn't broken. +RUN: unset AFL_DRIVER_STDERR_DUPLICATE_FILENAME +RUN: AFLDriverTest + +; Test that specifying an invalid file causes a crash. +RUN: ASAN_OPTIONS= AFL_DRIVER_STDERR_DUPLICATE_FILENAME="%T" not --crash AFLDriverTest + +; Test that a file is created when specified as the duplicate stderr. +RUN: AFL_DRIVER_STDERR_DUPLICATE_FILENAME=%t AFLDriverTest +RUN: stat %t diff --git a/test/fuzz_test/Fuzzer/test/caller-callee.test b/test/fuzz_test/Fuzzer/test/caller-callee.test new file mode 100644 index 00000000..76a951c5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/caller-callee.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-CallerCalleeTest -use_value_profile=1 -cross_over=0 -max_len=6 -seed=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/coverage.test b/test/fuzz_test/Fuzzer/test/coverage.test new file mode 100644 index 00000000..fa11be50 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/coverage.test @@ -0,0 +1,19 @@ +CHECK: COVERAGE: +CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:13 +CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:14 +CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16 +CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19 +CHECK: COVERED_DIRS: {{.*}}lib/Fuzzer/test +RUN: not LLVMFuzzer-NullDerefTest -print_coverage=1 2>&1 | FileCheck %s + +RUN: LLVMFuzzer-DSOTest -print_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO +DSO: COVERAGE: +DSO-DAG: COVERED:{{.*}}DSO1{{.*}}DSO1.cpp +DSO-DAG: COVERED:{{.*}}DSO2{{.*}}DSO2.cpp +DSO-DAG: COVERED:{{.*}}LLVMFuzzerTestOneInput{{.*}}DSOTestMain +DSO-DAG: UNCOVERED_LINE:{{.*}}DSO1{{.*}}DSO1.cpp +DSO-DAG: UNCOVERED_LINE:{{.*}}DSO2{{.*}}DSO2.cpp +DSO-DAG: UNCOVERED_FUNC: in Uncovered1 +DSO-DAG: UNCOVERED_FUNC: in Uncovered2 +DSO-DAG: UNCOVERED_LINE: in LLVMFuzzerTestOneInput +DSO-DAG: UNCOVERED_FILE:{{.*}}DSOTestExtra.cpp diff --git a/test/fuzz_test/Fuzzer/test/dict1.txt b/test/fuzz_test/Fuzzer/test/dict1.txt new file mode 100644 index 00000000..520d0cc7 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/dict1.txt @@ -0,0 +1,4 @@ +# Dictionary for SimpleDictionaryTest + +a="Elvis" +b="Presley" diff --git a/test/fuzz_test/Fuzzer/test/dump_coverage.test b/test/fuzz_test/Fuzzer/test/dump_coverage.test new file mode 100644 index 00000000..9bd98daa --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/dump_coverage.test @@ -0,0 +1,16 @@ +RUN: DIR=%t_workdir +RUN: BUILD_DIR=$(pwd) +RUN: rm -rf $DIR && mkdir -p $DIR && cd $DIR +RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s +RUN: $BUILD_DIR/LLVMFuzzer-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO +RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV +RUN: rm -rf $DIR + + +CHECK: SanitizerCoverage: ./LLVMFuzzer-NullDerefTest.{{.*}}.sancov {{.*}} PCs written + +DSO: SanitizerCoverage: ./LLVMFuzzer-DSOTest.{{.*}}.sancov {{.*}} PCs written +DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO1.{{.*}}.sancov {{.*}} PCs written +DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO2.{{.*}}.sancov {{.*}} PCs written + +NOCOV-NOT: SanitizerCoverage: {{.*}} PCs written diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test b/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test new file mode 100644 index 00000000..28d39ce3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test @@ -0,0 +1,10 @@ +RUN: rm -rf %t/CustomCrossover +RUN: mkdir -p %t/CustomCrossover +RUN: echo "0123456789" > %t/CustomCrossover/digits +RUN: echo "abcdefghij" > %t/CustomCrossover/chars +RUN: not LLVMFuzzer-CustomCrossOverTest -seed=1 -use_memcmp=0 -runs=100000 %t/CustomCrossover 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomCrossover +RUN: rm -rf %t/CustomCrossover + +LLVMFuzzerCustomCrossover: In LLVMFuzzerCustomCrossover +LLVMFuzzerCustomCrossover: BINGO + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test b/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test new file mode 100644 index 00000000..fcd740bf --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test @@ -0,0 +1,4 @@ +RUN: not LLVMFuzzer-CustomMutatorTest 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomMutator +LLVMFuzzerCustomMutator: In LLVMFuzzerCustomMutator +LLVMFuzzerCustomMutator: BINGO + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dict.test b/test/fuzz_test/Fuzzer/test/fuzzer-dict.test new file mode 100644 index 00000000..dec002f6 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-dict.test @@ -0,0 +1,6 @@ +CHECK: BINGO +Done1000000: Done 1000000 runs in + +RUN: not LLVMFuzzer-SimpleDictionaryTest -dict=%S/dict1.txt -seed=1 -runs=1000003 2>&1 | FileCheck %s +RUN: LLVMFuzzer-SimpleDictionaryTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test b/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test new file mode 100644 index 00000000..63afe8df --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test @@ -0,0 +1,15 @@ +RUN: rm -rf %t/SUB1 +RUN: mkdir -p %t/SUB1/SUB2/SUB3 +RUN: echo a > %t/SUB1/a +RUN: echo b > %t/SUB1/SUB2/b +RUN: echo c > %t/SUB1/SUB2/SUB3/c +RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=SUBDIRS +SUBDIRS: READ units: 3 +RUN: echo zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz > %t/SUB1/long +RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=LONG +LONG: INFO: -max_len is not provided, using 94 +RUN: rm -rf %t/SUB1 + +RUN: not LLVMFuzzer-SimpleTest NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR +NONEXISTENT_DIR: No such directory: NONEXISTENT_DIR; exiting + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test b/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test new file mode 100644 index 00000000..abbc4bd6 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test @@ -0,0 +1,30 @@ +RUN: LLVMFuzzer-SpamyTest -runs=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_0 +RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=0 2>&1 | FileCheck %s --check-prefix=FD_MASK_0 +RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_1 +RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=2 2>&1 | FileCheck %s --check-prefix=FD_MASK_2 +RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=FD_MASK_3 + +FD_MASK_0: PRINTF_STDOUT +FD_MASK_0: PRINTF_STDERR +FD_MASK_0: STREAM_COUT +FD_MASK_0: STREAM_CERR +FD_MASK_0: INITED + +FD_MASK_1-NOT: PRINTF_STDOUT +FD_MASK_1: PRINTF_STDERR +FD_MASK_1-NOT: STREAM_COUT +FD_MASK_1: STREAM_CERR +FD_MASK_1: INITED + +FD_MASK_2: PRINTF_STDOUT +FD_MASK_2-NOT: PRINTF_STDERR +FD_MASK_2: STREAM_COUT +FD_MASK_2-NOTE: STREAM_CERR +FD_MASK_2: INITED + +FD_MASK_3-NOT: PRINTF_STDOUT +FD_MASK_3-NOT: PRINTF_STDERR +FD_MASK_3-NOT: STREAM_COUT +FD_MASK_3-NOT: STREAM_CERR +FD_MASK_3: INITED + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test b/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test new file mode 100644 index 00000000..1cbcd10f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test @@ -0,0 +1,11 @@ +RUN: LLVMFuzzer-SimpleTest -seed=1 -runs=77 -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=FINAL_STATS +FINAL_STATS: stat::number_of_executed_units: 77 +FINAL_STATS: stat::average_exec_per_sec: 0 +FINAL_STATS: stat::new_units_added: +FINAL_STATS: stat::slowest_unit_time_sec: 0 +FINAL_STATS: stat::peak_rss_mb: + +RUN: LLVMFuzzer-SimpleTest %S/dict1.txt -runs=33 -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=FINAL_STATS1 +FINAL_STATS1: stat::number_of_executed_units: 33 +FINAL_STATS1: stat::peak_rss_mb: + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-flags.test b/test/fuzz_test/Fuzzer/test/fuzzer-flags.test new file mode 100644 index 00000000..76ea2770 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-flags.test @@ -0,0 +1,10 @@ +RUN: LLVMFuzzer-SimpleTest -foo_bar=1 2>&1 | FileCheck %s --check-prefix=FOO_BAR +FOO_BAR: WARNING: unrecognized flag '-foo_bar=1'; use -help=1 to list all flags +FOO_BAR: BINGO + +RUN: LLVMFuzzer-SimpleTest -runs=10 --max_len=100 2>&1 | FileCheck %s --check-prefix=DASH_DASH +DASH_DASH: WARNING: did you mean '-max_len=100' (single dash)? +DASH_DASH: INFO: A corpus is not provided, starting from an empty corpus + +RUN: LLVMFuzzer-SimpleTest -help=1 2>&1 | FileCheck %s --check-prefix=NO_INTERNAL +NO_INTERNAL-NOT: internal flag diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test b/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test new file mode 100644 index 00000000..5bf8cfad --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test @@ -0,0 +1,29 @@ +RUN: rm -rf %tmp +RUN: mkdir %tmp && cd %tmp +# Create a shared corpus directory +RUN: rm -rf FuzzerJobsTestCORPUS +RUN: mkdir FuzzerJobsTestCORPUS +RUN: rm -f fuzz-{0,1}.log +# Start fuzzer and in parallel check that the output files +# that should be created exist. +RUN: LLVMFuzzer-EmptyTest -max_total_time=4 -jobs=2 -workers=2 FuzzerJobsTestCORPUS > %t-fuzzer-jobs-test.log 2>&1 & export FUZZER_PID=$! +# Wait a short while to give time for the child processes +# to start fuzzing +RUN: sleep 2 +# If the instances are running in parallel they should have created their log +# files by now. +RUN: ls fuzz-0.log +RUN: ls fuzz-1.log +# Wait for libfuzzer to finish. +# This probably isn't portable but we need a way to block until +# the fuzzer is done otherwise we might remove the files while +# they are being used. +RUN: while kill -0 ${FUZZER_PID}; do : ; done +RUN: rm -f fuzz-{0,1}.log +RUN: rm -rf FuzzerJobsTestCORPUS +RUN: FileCheck -input-file=%t-fuzzer-jobs-test.log %s +RUN: rm %t-fuzzer-jobs-test.log +RUN: cd ../ + +CHECK-DAG: Job 0 exited with exit code 0 +CHECK-DAG: Job 1 exited with exit code 0 diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-leak.test b/test/fuzz_test/Fuzzer/test/fuzzer-leak.test new file mode 100644 index 00000000..9cf5c743 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-leak.test @@ -0,0 +1,35 @@ +REQUIRES: lsan +RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=1 2>&1 | FileCheck %s --check-prefix=LEAK_DURING +LEAK_DURING: ERROR: LeakSanitizer: detected memory leaks +LEAK_DURING: Direct leak of 4 byte(s) in 1 object(s) allocated from: +LEAK_DURING: INFO: to ignore leaks on libFuzzer side use -detect_leaks=0 +LEAK_DURING: Test unit written to ./leak- +LEAK_DURING-NOT: DONE +LEAK_DURING-NOT: Done + +RUN: not LLVMFuzzer-LeakTest -runs=0 -detect_leaks=1 %S 2>&1 | FileCheck %s --check-prefix=LEAK_IN_CORPUS +LEAK_IN_CORPUS: ERROR: LeakSanitizer: detected memory leaks +LEAK_IN_CORPUS: INFO: a leak has been found in the initial corpus. + +RUN: not LLVMFuzzer-LeakTest -runs=100000000 %S/hi.txt 2>&1 | FileCheck %s --check-prefix=MULTI_RUN_LEAK +MULTI_RUN_LEAK-NOT: pulse +MULTI_RUN_LEAK: LeakSanitizer: detected memory leaks + +RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER +RUN: not LLVMFuzzer-LeakTest -runs=100000 2>&1 | FileCheck %s --check-prefix=LEAK_DURING +RUN: not LLVMFuzzer-ThreadedLeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER +RUN: not LLVMFuzzer-ThreadedLeakTest -runs=100000 2>&1 | FileCheck %s --check-prefix=LEAK_DURING +LEAK_AFTER: Done 100000 runs in +LEAK_AFTER: ERROR: LeakSanitizer: detected memory leaks + +RUN: not LLVMFuzzer-LeakTest -runs=100000 -max_len=1 2>&1 | FileCheck %s --check-prefix=MAX_LEN_1 +MAX_LEN_1: Test unit written to ./leak-7cf184f4c67ad58283ecb19349720b0cae756829 + +RUN: not LLVMFuzzer-LeakTimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefix=LEAK_TIMEOUT +LEAK_TIMEOUT: ERROR: libFuzzer: timeout after +LEAK_TIMEOUT-NOT: LeakSanitizer + +RUN: LLVMFuzzer-AccumulateAllocationsTest -detect_leaks=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=ACCUMULATE_ALLOCS +ACCUMULATE_ALLOCS: INFO: libFuzzer disabled leak detection after every mutation + +RUN: LLVMFuzzer-LeakTest -error_exitcode=0 diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test b/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test new file mode 100644 index 00000000..2b2b0b9d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test @@ -0,0 +1,6 @@ +REQUIRES: linux +RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s +CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb) +CHECK: Live Heap Allocations +CHECK: Test unit written to ./oom- +SUMMARY: libFuzzer: out-of-memory diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-oom.test b/test/fuzz_test/Fuzzer/test/fuzzer-oom.test new file mode 100644 index 00000000..8caf649e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-oom.test @@ -0,0 +1,11 @@ +RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s +CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb) +CHECK: Test unit written to ./oom- +SUMMARY: libFuzzer: out-of-memory + +RUN: not LLVMFuzzer-OutOfMemorySingleLargeMallocTest 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC +SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory (malloc(42{{.*}})) +SINGLE_LARGE_MALLOC: in LLVMFuzzerTestOneInput + +# Check that -rss_limit_mb=0 means no limit. +RUN: LLVMFuzzer-AccumulateAllocationsTest -runs=1000 -rss_limit_mb=0 diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test b/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test new file mode 100644 index 00000000..e4c6f0ed --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test @@ -0,0 +1,8 @@ +RUN: LLVMFuzzer-SimpleTest -print_pcs=1 -seed=1 2>&1 | FileCheck %s --check-prefix=PCS +PCS-NOT: NEW_PC +PCS:INITED +PCS:NEW_PC: {{0x[a-f0-9]+}} +PCS:NEW_PC: {{0x[a-f0-9]+}} +PCS:NEW +PCS:BINGO + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-runs.test b/test/fuzz_test/Fuzzer/test/fuzzer-runs.test new file mode 100644 index 00000000..056c4478 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-runs.test @@ -0,0 +1,8 @@ +RUN: mkdir -p %t +RUN: echo abcd > %t/NthRunCrashTest.in +RUN: LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in +RUN: LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -runs=10 +RUN: not LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -runs=10000 2>&1 | FileCheck %s +RUN: rm %t/NthRunCrashTest.in +CHECK: BINGO + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-seed.test b/test/fuzz_test/Fuzzer/test/fuzzer-seed.test new file mode 100644 index 00000000..f1bdf9e4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-seed.test @@ -0,0 +1,3 @@ +RUN: LLVMFuzzer-SimpleCmpTest -seed=-1 -runs=0 2>&1 | FileCheck %s --check-prefix=CHECK_SEED_MINUS_ONE +CHECK_SEED_MINUS_ONE: Seed: 4294967295 + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-segv.test b/test/fuzz_test/Fuzzer/test/fuzzer-segv.test new file mode 100644 index 00000000..330f03bc --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-segv.test @@ -0,0 +1,5 @@ +RUN: ASAN_OPTIONS=handle_segv=0 not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=LIBFUZZER_OWN_SEGV_HANDLER +LIBFUZZER_OWN_SEGV_HANDLER: == ERROR: libFuzzer: deadly signal +LIBFUZZER_OWN_SEGV_HANDLER: SUMMARY: libFuzzer: deadly signal +LIBFUZZER_OWN_SEGV_HANDLER: Test unit written to ./crash- + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test b/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test new file mode 100644 index 00000000..ca8403bf --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test @@ -0,0 +1,16 @@ +RUN: not LLVMFuzzer-NullDerefTest %S/hi.txt 2>&1 | FileCheck %s --check-prefix=SingleInput +SingleInput-NOT: Test unit written to ./crash- + +RUN: rm -rf %tmp/SINGLE_INPUTS +RUN: mkdir -p %tmp/SINGLE_INPUTS +RUN: echo aaa > %tmp/SINGLE_INPUTS/aaa +RUN: echo bbb > %tmp/SINGLE_INPUTS/bbb +RUN: LLVMFuzzer-SimpleTest %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS +RUN: LLVMFuzzer-SimpleTest -max_len=2 %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS +RUN: rm -rf %tmp/SINGLE_INPUTS +SINGLE_INPUTS: LLVMFuzzer-SimpleTest: Running 2 inputs 1 time(s) each. +SINGLE_INPUTS: aaa in +SINGLE_INPUTS: bbb in +SINGLE_INPUTS: NOTE: fuzzing was not performed, you have only +SINGLE_INPUTS: executed the target code on a fixed set of inputs. + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test b/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test new file mode 100644 index 00000000..c58a3345 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test @@ -0,0 +1,7 @@ +CHECK: Done 1000 runs in + +RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test b/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test new file mode 100644 index 00000000..beb08671 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test @@ -0,0 +1,19 @@ +RUN: not LLVMFuzzer-TimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefix=TimeoutTest +TimeoutTest: ALARM: working on the last Unit for +TimeoutTest: Test unit written to ./timeout- +TimeoutTest: == ERROR: libFuzzer: timeout after +TimeoutTest: #0 +TimeoutTest: #1 +TimeoutTest: #2 +TimeoutTest: SUMMARY: libFuzzer: timeout + +RUN: not LLVMFuzzer-TimeoutTest -timeout=1 %S/hi.txt 2>&1 | FileCheck %s --check-prefix=SingleInputTimeoutTest +SingleInputTimeoutTest: ALARM: working on the last Unit for {{[1-3]}} seconds +SingleInputTimeoutTest-NOT: Test unit written to ./timeout- + +RUN: LLVMFuzzer-TimeoutTest -timeout=1 -timeout_exitcode=0 + +RUN: not LLVMFuzzer-TimeoutEmptyTest -timeout=1 2>&1 | FileCheck %s --check-prefix=TimeoutEmptyTest +TimeoutEmptyTest: ALARM: working on the last Unit for +TimeoutEmptyTest: == ERROR: libFuzzer: timeout after +TimeoutEmptyTest: SUMMARY: libFuzzer: timeout diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test b/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test new file mode 100644 index 00000000..71fe6f2d --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test @@ -0,0 +1,25 @@ +// FIXME: Support sanitizer hooks for memcmp and strcmp need +// to be implemented in the sanitizer runtime for platforms other +// than linux +REQUIRES: linux +CHECK: BINGO +Done1000000: Done 1000000 runs in + +RUN: not LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=100000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-MemcmpTest -use_memcmp=0 -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: not LLVMFuzzer-StrncmpTest -seed=2 -runs=100000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=3 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: not LLVMFuzzer-StrcmpTest -seed=4 -runs=200000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrcmpTest -use_memcmp=0 -seed=5 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: not LLVMFuzzer-StrstrTest -seed=6 -runs=200000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrstrTest -use_memmem=0 -seed=7 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 + +RUN: LLVMFuzzer-RepeatedMemcmp -seed=10 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT +RECOMMENDED_DICT:###### Recommended dictionary. ###### +RECOMMENDED_DICT-DAG: "foo" +RECOMMENDED_DICT-DAG: "bar" +RECOMMENDED_DICT:###### End of recommended dictionary. ###### + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test b/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test new file mode 100644 index 00000000..0e8ad6c9 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test @@ -0,0 +1,4 @@ +RUN: not LLVMFuzzer-SignedIntOverflowTest-Ubsan 2>&1 | FileCheck %s +CHECK: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' +CHECK: Test unit written to ./crash- + diff --git a/test/fuzz_test/Fuzzer/test/fuzzer.test b/test/fuzz_test/Fuzzer/test/fuzzer.test new file mode 100644 index 00000000..2f91c219 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/fuzzer.test @@ -0,0 +1,57 @@ +CHECK: BINGO +Done1000000: Done 1000000 runs in + +RUN: LLVMFuzzer-SimpleTest 2>&1 | FileCheck %s + +# only_ascii mode. Will perform some minimal self-validation. +RUN: LLVMFuzzer-SimpleTest -only_ascii=1 2>&1 + +RUN: LLVMFuzzer-SimpleCmpTest -max_total_time=1 -use_cmp=0 2>&1 | FileCheck %s --check-prefix=MaxTotalTime +MaxTotalTime: Done {{.*}} runs in {{.}} second(s) + +RUN: not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=NullDerefTest +RUN: not LLVMFuzzer-NullDerefTest -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=NullDerefTest +NullDerefTest: ERROR: AddressSanitizer: SEGV on unknown address +NullDerefTest: Test unit written to ./crash- +RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ 2>&1 | FileCheck %s --check-prefix=NullDerefTestPrefix +NullDerefTestPrefix: Test unit written to ZZZcrash- +RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ -exact_artifact_path=FOOBAR 2>&1 | FileCheck %s --check-prefix=NullDerefTestExactPath +NullDerefTestExactPath: Test unit written to FOOBAR + +RUN: not LLVMFuzzer-NullDerefOnEmptyTest -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=NULL_DEREF_ON_EMPTY +NULL_DEREF_ON_EMPTY: stat::number_of_executed_units: + +#not LLVMFuzzer-FullCoverageSetTest -timeout=15 -seed=1 -mutate_depth=2 -use_full_coverage_set=1 2>&1 | FileCheck %s + +RUN: not LLVMFuzzer-CounterTest -max_len=6 -seed=1 -timeout=15 2>&1 | FileCheck %s --check-prefix=COUNTERS + +COUNTERS: INITED {{.*}} {{bits:|ft:}} +COUNTERS: NEW {{.*}} {{bits:|ft:}} {{[1-9]*}} +COUNTERS: NEW {{.*}} {{bits:|ft:}} {{[1-9]*}} +COUNTERS: BINGO + +# Don't run UninstrumentedTest for now since we build libFuzzer itself with asan. +DISABLED: not LLVMFuzzer-UninstrumentedTest-Uninstrumented 2>&1 | FileCheck %s --check-prefix=UNINSTRUMENTED +UNINSTRUMENTED: ERROR: __sanitizer_set_death_callback is not defined. Exiting. + +RUN: not LLVMFuzzer-UninstrumentedTest-NoCoverage 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE +NO_COVERAGE: ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting + +RUN: not LLVMFuzzer-BufferOverflowOnInput 2>&1 | FileCheck %s --check-prefix=OOB +OOB: AddressSanitizer: heap-buffer-overflow +OOB: is located 0 bytes to the right of 3-byte region + +RUN: not LLVMFuzzer-InitializeTest -use_value_profile=1 2>&1 | FileCheck %s + +RUN: not LLVMFuzzer-DSOTest 2>&1 | FileCheck %s --check-prefix=DSO +DSO: INFO: Loaded 3 modules +DSO: BINGO + +RUN: LLVMFuzzer-SimpleTest -exit_on_src_pos=SimpleTest.cpp:17 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS +RUN: LLVMFuzzer-ShrinkControlFlowTest -exit_on_src_pos=ShrinkControlFlowTest.cpp:23 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS +EXIT_ON_SRC_POS: INFO: found line matching '{{.*}}', exiting. + +RUN: ASAN_OPTIONS=strict_string_checks=1 not LLVMFuzzer-StrncmpOOBTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=STRNCMP +STRNCMP: AddressSanitizer: heap-buffer-overflow +STRNCMP-NOT: __sanitizer_weak_hook_strncmp +STRNCMP: in LLVMFuzzerTestOneInput diff --git a/test/fuzz_test/Fuzzer/test/hi.txt b/test/fuzz_test/Fuzzer/test/hi.txt new file mode 100644 index 00000000..2f9031f0 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/hi.txt @@ -0,0 +1 @@ +Hi! \ No newline at end of file diff --git a/test/fuzz_test/Fuzzer/test/lit.cfg b/test/fuzz_test/Fuzzer/test/lit.cfg new file mode 100644 index 00000000..745af0c3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/lit.cfg @@ -0,0 +1,29 @@ +import lit.formats +import sys + +config.name = "LLVMFuzzer" +config.test_format = lit.formats.ShTest(True) +config.suffixes = ['.test'] +config.test_source_root = os.path.dirname(__file__) + +# Tweak PATH to include llvm tools dir and current exec dir. +llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) +if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): + lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) +path = os.path.pathsep.join((llvm_tools_dir, config.test_exec_root, + config.environment['PATH'])) +config.environment['PATH'] = path + +if config.has_lsan: + lit_config.note('lsan feature available') + config.available_features.add('lsan') +else: + lit_config.note('lsan feature unavailable') + +if sys.platform.startswith('linux'): + # Note the value of ``sys.platform`` is not consistent + # between python 2 and 3, hence the use of ``.startswith()``. + lit_config.note('linux feature available') + config.available_features.add('linux') +else: + lit_config.note('linux feature unavailable') diff --git a/test/fuzz_test/Fuzzer/test/lit.site.cfg.in b/test/fuzz_test/Fuzzer/test/lit.site.cfg.in new file mode 100644 index 00000000..03e86c48 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/lit.site.cfg.in @@ -0,0 +1,4 @@ +config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.has_lsan = True if @HAS_LSAN@ == 1 else False +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/test/fuzz_test/Fuzzer/test/merge.test b/test/fuzz_test/Fuzzer/test/merge.test new file mode 100644 index 00000000..1f1810eb --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/merge.test @@ -0,0 +1,46 @@ +CHECK: BINGO + +RUN: rm -rf %tmp/T1 %tmp/T2 +RUN: mkdir -p %tmp/T1 %tmp/T2 +RUN: echo F..... > %tmp/T1/1 +RUN: echo .U.... > %tmp/T1/2 +RUN: echo ..Z... > %tmp/T1/3 + +# T1 has 3 elements, T2 is empty. +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK1 +CHECK1: MERGE-OUTER: 3 files, 3 in the initial corpus +CHECK1: MERGE-OUTER: 0 new files with 0 new features added + +RUN: echo ...Z.. > %tmp/T2/1 +RUN: echo ....E. > %tmp/T2/2 +RUN: echo .....R > %tmp/T2/3 +RUN: echo F..... > %tmp/T2/a +RUN: echo .U.... > %tmp/T2/b +RUN: echo ..Z... > %tmp/T2/c + +# T1 has 3 elements, T2 has 6 elements, only 3 are new. +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK2 +CHECK2: MERGE-OUTER: 9 files, 3 in the initial corpus +CHECK2: MERGE-OUTER: 3 new files with 3 new features added + +# Now, T1 has 6 units and T2 has no new interesting units. +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK3 +CHECK3: MERGE-OUTER: 12 files, 6 in the initial corpus +CHECK3: MERGE-OUTER: 0 new files with 0 new features added + +# Check that we respect max_len during the merge and don't crash. +RUN: rm %tmp/T1/??* +RUN: echo looooooooong > %tmp/T2/looooooooong +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=6 2>&1 | FileCheck %s --check-prefix=MAX_LEN +MAX_LEN: MERGE-OUTER: 3 new files + +# Check that merge tolerates failures. +RUN: rm %tmp/T1/??* +RUN: echo 'FUZZER' > %tmp/T2/FUZZER +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=MERGE_WITH_CRASH +MERGE_WITH_CRASH: MERGE-OUTER: succesfull in 2 attempt(s) +MERGE_WITH_CRASH: MERGE-OUTER: 3 new files + +# Check that we actually limit the size with max_len +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=5 2>&1 | FileCheck %s --check-prefix=MERGE_LEN5 +MERGE_LEN5: MERGE-OUTER: succesfull in 1 attempt(s) diff --git a/test/fuzz_test/Fuzzer/test/minimize_crash.test b/test/fuzz_test/Fuzzer/test/minimize_crash.test new file mode 100644 index 00000000..7e540659 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/minimize_crash.test @@ -0,0 +1,6 @@ +RUN: echo 'Hi!rv349f34t3gg' > not_minimal_crash +RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 2>&1 | FileCheck %s +CHECK: CRASH_MIN: failed to minimize beyond minimized-from-{{.*}} (3 bytes), exiting +RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 -exact_artifact_path=exact_minimized_path 2>&1 | FileCheck %s --check-prefix=CHECK_EXACT +CHECK_EXACT: CRASH_MIN: failed to minimize beyond exact_minimized_path (3 bytes), exiting +RUN: rm not_minimal_crash minimized-from-* exact_minimized_path diff --git a/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt new file mode 100644 index 00000000..d2f6f438 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt @@ -0,0 +1,29 @@ +# These tests are not instrumented with coverage, +# but have coverage rt in the binary. + +set(CMAKE_CXX_FLAGS + "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard") + +set(NoCoverageTests + UninstrumentedTest + ) + +foreach(Test ${NoCoverageTests}) + add_libfuzzer_test(${Test}-NoCoverage SOURCES ../${Test}.cpp) +endforeach() + + +############################################################################### +# AFL Driver test +############################################################################### + +add_executable(AFLDriverTest + ../AFLDriverTest.cpp ../../afl/afl_driver.cpp) + +set_target_properties(AFLDriverTest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" + ) + +# Propagate value into parent directory +set(TestBinaries ${TestBinaries} AFLDriverTest PARENT_SCOPE) diff --git a/test/fuzz_test/Fuzzer/test/repeated-bytes.test b/test/fuzz_test/Fuzzer/test/repeated-bytes.test new file mode 100644 index 00000000..71394087 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/repeated-bytes.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: LLVMFuzzer-RepeatedBytesTest -seed=1 -runs=1000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/shrink.test b/test/fuzz_test/Fuzzer/test/shrink.test new file mode 100644 index 00000000..edb86cb1 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/shrink.test @@ -0,0 +1,7 @@ +RUN: LLVMFuzzer-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=1 2>&1 | FileCheck %s --check-prefix=SHRINK1 +RUN: LLVMFuzzer-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=0 2>&1 | FileCheck %s --check-prefix=SHRINK0 +RUN: LLVMFuzzer-ShrinkValueProfileTest -seed=1 -exit_on_item=aea2e3923af219a8956f626558ef32f30a914ebc -runs=100000 -shrink=1 -use_value_profile=1 2>&1 | FileCheck %s --check-prefix=SHRINK1_VP + +SHRINK0: Done 1000000 runs in +SHRINK1: INFO: found item with checksum '0eb8e4ed029b774d80f2b66408203801cb982a60', exiting. +SHRINK1_VP: INFO: found item with checksum 'aea2e3923af219a8956f626558ef32f30a914ebc', exiting diff --git a/test/fuzz_test/Fuzzer/test/simple-cmp.test b/test/fuzz_test/Fuzzer/test/simple-cmp.test new file mode 100644 index 00000000..145a0366 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/simple-cmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/standalone.test b/test/fuzz_test/Fuzzer/test/standalone.test new file mode 100644 index 00000000..3097b3ff --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/standalone.test @@ -0,0 +1,4 @@ +RUN: LLVMFuzzer-StandaloneInitializeTest %S/hi.txt %S/dict1.txt 2>&1 | FileCheck %s +CHECK: StandaloneFuzzTargetMain: running 2 inputs +CHECK: Done: {{.*}}hi.txt: (3 bytes) +CHECK: Done: {{.*}}dict1.txt: (61 bytes) diff --git a/test/fuzz_test/Fuzzer/test/swap-cmp.test b/test/fuzz_test/Fuzzer/test/swap-cmp.test new file mode 100644 index 00000000..908b7986 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/swap-cmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SwapCmpTest -seed=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/trace-malloc.test b/test/fuzz_test/Fuzzer/test/trace-malloc.test new file mode 100644 index 00000000..c9514790 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/trace-malloc.test @@ -0,0 +1,10 @@ +RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=1 -runs=10000 2>&1 | FileCheck %s +CHECK-DAG: MallocFreeTracer: STOP 0 0 (same) +CHECK-DAG: MallocFreeTracer: STOP 0 1 (DIFFERENT) +CHECK-DAG: MallocFreeTracer: STOP 1 0 (DIFFERENT) +CHECK-DAG: MallocFreeTracer: STOP 1 1 (same) + +RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=2 -runs=1000 2>&1 | FileCheck %s --check-prefix=TRACE2 +TRACE2-DAG: FREE[0] +TRACE2-DAG: MALLOC[0] +TRACE2-DAG: in LLVMFuzzerTestOneInput diff --git a/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt new file mode 100644 index 00000000..7a9eacdb --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt @@ -0,0 +1,15 @@ +# These tests are instrumented with ubsan in non-recovery mode. + +set(CMAKE_CXX_FLAGS + "${LIBFUZZER_FLAGS_BASE} -fsanitize=undefined -fno-sanitize-recover=all") + +set(UbsanTests + SignedIntOverflowTest + ) + +foreach(Test ${UbsanTests}) + add_libfuzzer_test(${Test}-Ubsan SOURCES ../${Test}.cpp) +endforeach() + +# Propagate value into parent directory +set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/test/fuzz_test/Fuzzer/test/ulimit.test b/test/fuzz_test/Fuzzer/test/ulimit.test new file mode 100644 index 00000000..a60636c3 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/ulimit.test @@ -0,0 +1,2 @@ +RUN: ulimit -s 1000 +RUN: LLVMFuzzer-SimpleTest diff --git a/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt b/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt new file mode 100644 index 00000000..29b66e6e --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt @@ -0,0 +1,16 @@ +# These tests are not instrumented with coverage and don't +# have coverage rt in the binary. + +set(CMAKE_CXX_FLAGS + "${LIBFUZZER_FLAGS_BASE} -fno-sanitize=all -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard") + +set(UninstrumentedTests + UninstrumentedTest + ) + +foreach(Test ${UninstrumentedTests}) + add_libfuzzer_test(${Test}-Uninstrumented SOURCES ../${Test}.cpp) +endforeach() + +# Propagate value into parent directory +set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.cfg b/test/fuzz_test/Fuzzer/test/unit/lit.cfg new file mode 100644 index 00000000..0cc31939 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/unit/lit.cfg @@ -0,0 +1,7 @@ +import lit.formats + +config.name = "LLVMFuzzer-Unittest" +print config.test_exec_root +config.test_format = lit.formats.GoogleTest(".", "Unittest") +config.suffixes = [] +config.test_source_root = config.test_exec_root diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in b/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in new file mode 100644 index 00000000..114daf47 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in @@ -0,0 +1,2 @@ +config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/unit/lit.cfg") diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp.test new file mode 100644 index 00000000..48edba4f --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test new file mode 100644 index 00000000..43d62400 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SimpleHashTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test new file mode 100644 index 00000000..8a962763 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-AbsNegAndConstantTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test b/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test new file mode 100644 index 00000000..1e7131e5 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-AbsNegAndConstant64Test -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-div.test b/test/fuzz_test/Fuzzer/test/value-profile-div.test new file mode 100644 index 00000000..ba45e412 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-div.test @@ -0,0 +1,3 @@ +CHECK: AddressSanitizer: FPE +RUN: not LLVMFuzzer-DivTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s + diff --git a/test/fuzz_test/Fuzzer/test/value-profile-load.test b/test/fuzz_test/Fuzzer/test/value-profile-load.test new file mode 100644 index 00000000..14d3109a --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-load.test @@ -0,0 +1,3 @@ +CHECK: AddressSanitizer: global-buffer-overflow +RUN: not LLVMFuzzer-LoadTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s + diff --git a/test/fuzz_test/Fuzzer/test/value-profile-mem.test b/test/fuzz_test/Fuzzer/test/value-profile-mem.test new file mode 100644 index 00000000..09d737db --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-mem.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SingleMemcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-set.test b/test/fuzz_test/Fuzzer/test/value-profile-set.test new file mode 100644 index 00000000..9d06c365 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-set.test @@ -0,0 +1,3 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-FourIndependentBranchesTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s + diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test b/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test new file mode 100644 index 00000000..1e7ef9b4 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SingleStrcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test b/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test new file mode 100644 index 00000000..65097318 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SingleStrncmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/test/fuzz_test/Fuzzer/test/value-profile-switch.test b/test/fuzz_test/Fuzzer/test/value-profile-switch.test new file mode 100644 index 00000000..1947f568 --- /dev/null +++ b/test/fuzz_test/Fuzzer/test/value-profile-switch.test @@ -0,0 +1,3 @@ +CHECK: BINGO +RUN: not LLVMFuzzer-SwitchTest -use_cmp=0 -use_value_profile=1 -runs=100000000 -seed=1 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-Switch2Test -use_cmp=0 -use_value_profile=1 -runs=100000000 -seed=1 2>&1 | FileCheck %s From 6e8791912ff4b3480acaa350c628404f04aefa47 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:09:40 +0100 Subject: [PATCH 33/71] :construction: added fuzzer from OSS-Fuzz --- test/fuzz_test/parse_fuzzer.cc | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/fuzz_test/parse_fuzzer.cc diff --git a/test/fuzz_test/parse_fuzzer.cc b/test/fuzz_test/parse_fuzzer.cc new file mode 100644 index 00000000..bb8b3d37 --- /dev/null +++ b/test/fuzz_test/parse_fuzzer.cc @@ -0,0 +1,36 @@ +// Copyright 2016 Google Inc. +// +// 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. + +#include +#include +#include + +using json = nlohmann::json; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + try { + std::stringstream s; + s << json::parse(data, data + size); + try { + auto j = json::parse(s.str()); + std::stringstream s2; + s2 << j; + assert(s.str() == s2.str()); + assert(j == json::parse(s.str())); + } catch (const std::invalid_argument&) { + assert(0); + } + } catch (const std::invalid_argument&) { } + return 0; +} From ad241a2260ed33fa0b0aa40ec65e5a68650eb5f7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:35:53 +0100 Subject: [PATCH 34/71] :hammer: moved third-party code into separate folder --- .gitignore | 2 +- test/CMakeLists.txt | 4 +-- test/Makefile | 8 ++--- .../fuzzer-parse_json.cpp} | 34 +++++++++++-------- .../Fuzzer/CMakeLists.txt | 0 .../Fuzzer/FuzzerCorpus.h | 0 .../Fuzzer/FuzzerCrossOver.cpp | 0 .../Fuzzer/FuzzerDefs.h | 0 .../Fuzzer/FuzzerDictionary.h | 0 .../Fuzzer/FuzzerDriver.cpp | 0 .../Fuzzer/FuzzerExtFunctions.def | 0 .../Fuzzer/FuzzerExtFunctions.h | 0 .../Fuzzer/FuzzerExtFunctionsDlsym.cpp | 0 .../Fuzzer/FuzzerExtFunctionsWeak.cpp | 0 .../Fuzzer/FuzzerExtFunctionsWeakAlias.cpp | 0 .../Fuzzer/FuzzerFlags.def | 0 .../Fuzzer/FuzzerIO.cpp | 0 .../Fuzzer/FuzzerIO.h | 0 .../Fuzzer/FuzzerIOPosix.cpp | 0 .../Fuzzer/FuzzerIOWindows.cpp | 0 .../Fuzzer/FuzzerInterface.h | 0 .../Fuzzer/FuzzerInternal.h | 0 .../Fuzzer/FuzzerLoop.cpp | 0 .../Fuzzer/FuzzerMain.cpp | 0 .../Fuzzer/FuzzerMerge.cpp | 0 .../Fuzzer/FuzzerMerge.h | 0 .../Fuzzer/FuzzerMutate.cpp | 0 .../Fuzzer/FuzzerMutate.h | 0 .../Fuzzer/FuzzerOptions.h | 0 .../Fuzzer/FuzzerRandom.h | 0 .../Fuzzer/FuzzerSHA1.cpp | 0 .../Fuzzer/FuzzerSHA1.h | 0 .../Fuzzer/FuzzerTracePC.cpp | 0 .../Fuzzer/FuzzerTracePC.h | 0 .../Fuzzer/FuzzerTraceState.cpp | 0 .../Fuzzer/FuzzerUtil.cpp | 0 .../Fuzzer/FuzzerUtil.h | 0 .../Fuzzer/FuzzerUtilDarwin.cpp | 0 .../Fuzzer/FuzzerUtilLinux.cpp | 0 .../Fuzzer/FuzzerUtilPosix.cpp | 0 .../Fuzzer/FuzzerUtilWindows.cpp | 0 .../Fuzzer/FuzzerValueBitMap.h | 0 .../Fuzzer/README.txt | 0 .../Fuzzer/afl/afl_driver.cpp | 0 .../{fuzz_test => thirdparty}/Fuzzer/build.sh | 0 .../{fuzz_test => thirdparty}/Fuzzer/cxx.dict | 0 .../standalone/StandaloneFuzzTargetMain.c | 0 .../Fuzzer/test/AFLDriverTest.cpp | 0 .../Fuzzer/test/AbsNegAndConstant64Test.cpp | 0 .../Fuzzer/test/AbsNegAndConstantTest.cpp | 0 .../Fuzzer/test/AccumulateAllocationsTest.cpp | 0 .../Fuzzer/test/BufferOverflowOnInput.cpp | 0 .../Fuzzer/test/CMakeLists.txt | 0 .../Fuzzer/test/CallerCalleeTest.cpp | 0 .../Fuzzer/test/CounterTest.cpp | 0 .../Fuzzer/test/CustomCrossOverTest.cpp | 0 .../Fuzzer/test/CustomMutatorTest.cpp | 0 .../Fuzzer/test/DSO1.cpp | 0 .../Fuzzer/test/DSO2.cpp | 0 .../Fuzzer/test/DSOTestExtra.cpp | 0 .../Fuzzer/test/DSOTestMain.cpp | 0 .../Fuzzer/test/DivTest.cpp | 0 .../Fuzzer/test/EmptyTest.cpp | 0 .../test/FourIndependentBranchesTest.cpp | 0 .../Fuzzer/test/FullCoverageSetTest.cpp | 0 .../Fuzzer/test/FuzzerUnittest.cpp | 0 .../Fuzzer/test/InitializeTest.cpp | 0 .../Fuzzer/test/LeakTest.cpp | 0 .../Fuzzer/test/LeakTimeoutTest.cpp | 0 .../Fuzzer/test/LoadTest.cpp | 0 .../Fuzzer/test/MemcmpTest.cpp | 0 .../Fuzzer/test/NthRunCrashTest.cpp | 0 .../Fuzzer/test/NullDerefOnEmptyTest.cpp | 0 .../Fuzzer/test/NullDerefTest.cpp | 0 .../Fuzzer/test/OneHugeAllocTest.cpp | 0 .../test/OutOfMemorySingleLargeMallocTest.cpp | 0 .../Fuzzer/test/OutOfMemoryTest.cpp | 0 .../Fuzzer/test/RepeatedBytesTest.cpp | 0 .../Fuzzer/test/RepeatedMemcmp.cpp | 0 .../Fuzzer/test/ShrinkControlFlowTest.cpp | 0 .../Fuzzer/test/ShrinkValueProfileTest.cpp | 0 .../Fuzzer/test/SignedIntOverflowTest.cpp | 0 .../Fuzzer/test/SimpleCmpTest.cpp | 0 .../Fuzzer/test/SimpleDictionaryTest.cpp | 0 .../Fuzzer/test/SimpleHashTest.cpp | 0 .../Fuzzer/test/SimpleTest.cpp | 0 .../Fuzzer/test/SimpleThreadedTest.cpp | 0 .../Fuzzer/test/SingleMemcmpTest.cpp | 0 .../Fuzzer/test/SingleStrcmpTest.cpp | 0 .../Fuzzer/test/SingleStrncmpTest.cpp | 0 .../Fuzzer/test/SpamyTest.cpp | 0 .../Fuzzer/test/StrcmpTest.cpp | 0 .../Fuzzer/test/StrncmpOOBTest.cpp | 0 .../Fuzzer/test/StrncmpTest.cpp | 0 .../Fuzzer/test/StrstrTest.cpp | 0 .../Fuzzer/test/SwapCmpTest.cpp | 0 .../Fuzzer/test/Switch2Test.cpp | 0 .../Fuzzer/test/SwitchTest.cpp | 0 .../Fuzzer/test/ThreadedLeakTest.cpp | 0 .../Fuzzer/test/ThreadedTest.cpp | 0 .../Fuzzer/test/TimeoutEmptyTest.cpp | 0 .../Fuzzer/test/TimeoutTest.cpp | 0 .../Fuzzer/test/TraceMallocTest.cpp | 0 .../Fuzzer/test/UninstrumentedTest.cpp | 0 .../Fuzzer/test/afl-driver-extra-stats.test | 0 .../Fuzzer/test/afl-driver-stderr.test | 0 .../Fuzzer/test/caller-callee.test | 0 .../Fuzzer/test/coverage.test | 0 .../Fuzzer/test/dict1.txt | 0 .../Fuzzer/test/dump_coverage.test | 0 .../Fuzzer/test/fuzzer-customcrossover.test | 0 .../Fuzzer/test/fuzzer-custommutator.test | 0 .../Fuzzer/test/fuzzer-dict.test | 0 .../Fuzzer/test/fuzzer-dirs.test | 0 .../Fuzzer/test/fuzzer-fdmask.test | 0 .../Fuzzer/test/fuzzer-finalstats.test | 0 .../Fuzzer/test/fuzzer-flags.test | 0 .../Fuzzer/test/fuzzer-jobs.test | 0 .../Fuzzer/test/fuzzer-leak.test | 0 .../Fuzzer/test/fuzzer-oom-with-profile.test | 0 .../Fuzzer/test/fuzzer-oom.test | 0 .../Fuzzer/test/fuzzer-printcovpcs.test | 0 .../Fuzzer/test/fuzzer-runs.test | 0 .../Fuzzer/test/fuzzer-seed.test | 0 .../Fuzzer/test/fuzzer-segv.test | 0 .../Fuzzer/test/fuzzer-singleinputs.test | 0 .../Fuzzer/test/fuzzer-threaded.test | 0 .../Fuzzer/test/fuzzer-timeout.test | 0 .../Fuzzer/test/fuzzer-traces-hooks.test | 0 .../Fuzzer/test/fuzzer-ubsan.test | 0 .../Fuzzer/test/fuzzer.test | 0 .../Fuzzer/test/hi.txt | 0 .../Fuzzer/test/lit.cfg | 0 .../Fuzzer/test/lit.site.cfg.in | 0 .../Fuzzer/test/merge.test | 0 .../Fuzzer/test/minimize_crash.test | 0 .../Fuzzer/test/no-coverage/CMakeLists.txt | 0 .../Fuzzer/test/repeated-bytes.test | 0 .../Fuzzer/test/shrink.test | 0 .../Fuzzer/test/simple-cmp.test | 0 .../Fuzzer/test/standalone.test | 0 .../Fuzzer/test/swap-cmp.test | 0 .../Fuzzer/test/trace-malloc.test | 0 .../Fuzzer/test/ubsan/CMakeLists.txt | 0 .../Fuzzer/test/ulimit.test | 0 .../Fuzzer/test/uninstrumented/CMakeLists.txt | 0 .../Fuzzer/test/unit/lit.cfg | 0 .../Fuzzer/test/unit/lit.site.cfg.in | 0 .../Fuzzer/test/value-profile-cmp.test | 0 .../Fuzzer/test/value-profile-cmp2.test | 0 .../Fuzzer/test/value-profile-cmp3.test | 0 .../Fuzzer/test/value-profile-cmp4.test | 0 .../Fuzzer/test/value-profile-div.test | 0 .../Fuzzer/test/value-profile-load.test | 0 .../Fuzzer/test/value-profile-mem.test | 0 .../Fuzzer/test/value-profile-set.test | 0 .../Fuzzer/test/value-profile-strcmp.test | 0 .../Fuzzer/test/value-profile-strncmp.test | 0 .../Fuzzer/test/value-profile-switch.test | 0 test/{src => thirdparty/catch}/catch.hpp | 0 160 files changed, 27 insertions(+), 21 deletions(-) rename test/{fuzz_test/parse_fuzzer.cc => src/fuzzer-parse_json.cpp} (61%) rename test/{fuzz_test => thirdparty}/Fuzzer/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerCorpus.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerCrossOver.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerDefs.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerDictionary.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerDriver.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctions.def (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctions.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctionsDlsym.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctionsWeak.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerFlags.def (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIO.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIO.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIOPosix.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerIOWindows.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerInterface.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerInternal.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerLoop.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMain.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMerge.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMerge.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMutate.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerMutate.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerOptions.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerRandom.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerSHA1.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerSHA1.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerTracePC.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerTracePC.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerTraceState.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtil.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtil.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilDarwin.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilLinux.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilPosix.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerUtilWindows.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/FuzzerValueBitMap.h (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/README.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/afl/afl_driver.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/build.sh (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/cxx.dict (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/standalone/StandaloneFuzzTargetMain.c (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AFLDriverTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AbsNegAndConstant64Test.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AbsNegAndConstantTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/AccumulateAllocationsTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/BufferOverflowOnInput.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CallerCalleeTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CounterTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CustomCrossOverTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/CustomMutatorTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSO1.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSO2.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSOTestExtra.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DSOTestMain.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/DivTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/EmptyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/FourIndependentBranchesTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/FullCoverageSetTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/FuzzerUnittest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/InitializeTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/LeakTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/LeakTimeoutTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/LoadTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/MemcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/NthRunCrashTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/NullDerefOnEmptyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/NullDerefTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/OneHugeAllocTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/OutOfMemoryTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/RepeatedBytesTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/RepeatedMemcmp.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ShrinkControlFlowTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ShrinkValueProfileTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SignedIntOverflowTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleCmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleDictionaryTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleHashTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SimpleThreadedTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SingleMemcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SingleStrcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SingleStrncmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SpamyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrcmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrncmpOOBTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrncmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/StrstrTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SwapCmpTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/Switch2Test.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/SwitchTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ThreadedLeakTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ThreadedTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/TimeoutEmptyTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/TimeoutTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/TraceMallocTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/UninstrumentedTest.cpp (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/afl-driver-extra-stats.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/afl-driver-stderr.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/caller-callee.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/coverage.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/dict1.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/dump_coverage.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-customcrossover.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-custommutator.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-dict.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-dirs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-fdmask.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-finalstats.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-flags.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-jobs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-leak.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-oom-with-profile.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-oom.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-printcovpcs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-runs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-seed.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-segv.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-singleinputs.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-threaded.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-timeout.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-traces-hooks.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer-ubsan.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/fuzzer.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/hi.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/lit.cfg (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/lit.site.cfg.in (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/merge.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/minimize_crash.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/no-coverage/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/repeated-bytes.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/shrink.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/simple-cmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/standalone.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/swap-cmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/trace-malloc.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ubsan/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/ulimit.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/uninstrumented/CMakeLists.txt (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/unit/lit.cfg (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/unit/lit.site.cfg.in (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp2.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp3.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-cmp4.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-div.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-load.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-mem.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-set.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-strcmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-strncmp.test (100%) rename test/{fuzz_test => thirdparty}/Fuzzer/test/value-profile-switch.test (100%) rename test/{src => thirdparty/catch}/catch.hpp (100%) diff --git a/.gitignore b/.gitignore index 099cad5c..104befbe 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ cmake-build-debug test/test-* -test/fuzz_test/Fuzzer/.svn +.svn diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f57ddf71..47785aee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ # The unit test executable. set(JSON_UNITTEST_TARGET_NAME "json_unit") add_executable(${JSON_UNITTEST_TARGET_NAME} - "src/catch.hpp" + "thirdparty/catch/catch.hpp" "src/unit.cpp" "src/unit-algorithms.cpp" "src/unit-allocator.cpp" @@ -44,7 +44,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" ) -target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") +target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src" "thirdparty/catch") target_link_libraries(${JSON_UNITTEST_TARGET_NAME} ${JSON_TARGET_NAME}) add_test(NAME "${JSON_UNITTEST_TARGET_NAME}_default" diff --git a/test/Makefile b/test/Makefile index 488cec89..c1fb33f4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ # additional flags CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal -CPPFLAGS += -I ../src -I . +CPPFLAGS += -I ../src -I . -I thirdparty/catch SOURCES = src/unit.cpp \ src/unit-algorithms.cpp \ @@ -57,11 +57,11 @@ clean: # single test file ############################################################################## -json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp +json_unit: $(OBJECTS) ../src/json.hpp thirdparty/catch/catch.hpp @echo "[CXXLD] $@" @$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ -%.o: %.cpp ../src/json.hpp src/catch.hpp +%.o: %.cpp ../src/json.hpp thirdparty/catch/catch.hpp @echo "[CXX] $@" @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ @@ -70,7 +70,7 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp # individual test cases ############################################################################## -test-%: src/unit-%.cpp ../src/json.hpp src/catch.hpp +test-%: src/unit-%.cpp ../src/json.hpp thirdparty/catch/catch.hpp @echo "[CXXLD] $@" @$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DCATCH_CONFIG_MAIN $< -o $@ diff --git a/test/fuzz_test/parse_fuzzer.cc b/test/src/fuzzer-parse_json.cpp similarity index 61% rename from test/fuzz_test/parse_fuzzer.cc rename to test/src/fuzzer-parse_json.cpp index bb8b3d37..20a824db 100644 --- a/test/fuzz_test/parse_fuzzer.cc +++ b/test/src/fuzzer-parse_json.cpp @@ -18,19 +18,25 @@ using json = nlohmann::json; -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - try { - std::stringstream s; - s << json::parse(data, data + size); - try { - auto j = json::parse(s.str()); - std::stringstream s2; - s2 << j; - assert(s.str() == s2.str()); - assert(j == json::parse(s.str())); - } catch (const std::invalid_argument&) { - assert(0); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + std::stringstream s; + s << json::parse(data, data + size); + try + { + auto j = json::parse(s.str()); + std::stringstream s2; + s2 << j; + assert(s.str() == s2.str()); + assert(j == json::parse(s.str())); + } + catch (const std::invalid_argument&) + { + assert(0); + } } - } catch (const std::invalid_argument&) { } - return 0; + catch (const std::invalid_argument&) { } + return 0; } diff --git a/test/fuzz_test/Fuzzer/CMakeLists.txt b/test/thirdparty/Fuzzer/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/CMakeLists.txt rename to test/thirdparty/Fuzzer/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/FuzzerCorpus.h b/test/thirdparty/Fuzzer/FuzzerCorpus.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerCorpus.h rename to test/thirdparty/Fuzzer/FuzzerCorpus.h diff --git a/test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp b/test/thirdparty/Fuzzer/FuzzerCrossOver.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerCrossOver.cpp rename to test/thirdparty/Fuzzer/FuzzerCrossOver.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerDefs.h b/test/thirdparty/Fuzzer/FuzzerDefs.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerDefs.h rename to test/thirdparty/Fuzzer/FuzzerDefs.h diff --git a/test/fuzz_test/Fuzzer/FuzzerDictionary.h b/test/thirdparty/Fuzzer/FuzzerDictionary.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerDictionary.h rename to test/thirdparty/Fuzzer/FuzzerDictionary.h diff --git a/test/fuzz_test/Fuzzer/FuzzerDriver.cpp b/test/thirdparty/Fuzzer/FuzzerDriver.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerDriver.cpp rename to test/thirdparty/Fuzzer/FuzzerDriver.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.def b/test/thirdparty/Fuzzer/FuzzerExtFunctions.def similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctions.def rename to test/thirdparty/Fuzzer/FuzzerExtFunctions.def diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctions.h b/test/thirdparty/Fuzzer/FuzzerExtFunctions.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctions.h rename to test/thirdparty/Fuzzer/FuzzerExtFunctions.h diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp b/test/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctionsDlsym.cpp rename to test/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp b/test/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeak.cpp rename to test/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/test/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp rename to test/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerFlags.def b/test/thirdparty/Fuzzer/FuzzerFlags.def similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerFlags.def rename to test/thirdparty/Fuzzer/FuzzerFlags.def diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.cpp b/test/thirdparty/Fuzzer/FuzzerIO.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIO.cpp rename to test/thirdparty/Fuzzer/FuzzerIO.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerIO.h b/test/thirdparty/Fuzzer/FuzzerIO.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIO.h rename to test/thirdparty/Fuzzer/FuzzerIO.h diff --git a/test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp b/test/thirdparty/Fuzzer/FuzzerIOPosix.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIOPosix.cpp rename to test/thirdparty/Fuzzer/FuzzerIOPosix.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp b/test/thirdparty/Fuzzer/FuzzerIOWindows.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerIOWindows.cpp rename to test/thirdparty/Fuzzer/FuzzerIOWindows.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerInterface.h b/test/thirdparty/Fuzzer/FuzzerInterface.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerInterface.h rename to test/thirdparty/Fuzzer/FuzzerInterface.h diff --git a/test/fuzz_test/Fuzzer/FuzzerInternal.h b/test/thirdparty/Fuzzer/FuzzerInternal.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerInternal.h rename to test/thirdparty/Fuzzer/FuzzerInternal.h diff --git a/test/fuzz_test/Fuzzer/FuzzerLoop.cpp b/test/thirdparty/Fuzzer/FuzzerLoop.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerLoop.cpp rename to test/thirdparty/Fuzzer/FuzzerLoop.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMain.cpp b/test/thirdparty/Fuzzer/FuzzerMain.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMain.cpp rename to test/thirdparty/Fuzzer/FuzzerMain.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.cpp b/test/thirdparty/Fuzzer/FuzzerMerge.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMerge.cpp rename to test/thirdparty/Fuzzer/FuzzerMerge.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMerge.h b/test/thirdparty/Fuzzer/FuzzerMerge.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMerge.h rename to test/thirdparty/Fuzzer/FuzzerMerge.h diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.cpp b/test/thirdparty/Fuzzer/FuzzerMutate.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMutate.cpp rename to test/thirdparty/Fuzzer/FuzzerMutate.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerMutate.h b/test/thirdparty/Fuzzer/FuzzerMutate.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerMutate.h rename to test/thirdparty/Fuzzer/FuzzerMutate.h diff --git a/test/fuzz_test/Fuzzer/FuzzerOptions.h b/test/thirdparty/Fuzzer/FuzzerOptions.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerOptions.h rename to test/thirdparty/Fuzzer/FuzzerOptions.h diff --git a/test/fuzz_test/Fuzzer/FuzzerRandom.h b/test/thirdparty/Fuzzer/FuzzerRandom.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerRandom.h rename to test/thirdparty/Fuzzer/FuzzerRandom.h diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.cpp b/test/thirdparty/Fuzzer/FuzzerSHA1.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerSHA1.cpp rename to test/thirdparty/Fuzzer/FuzzerSHA1.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerSHA1.h b/test/thirdparty/Fuzzer/FuzzerSHA1.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerSHA1.h rename to test/thirdparty/Fuzzer/FuzzerSHA1.h diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.cpp b/test/thirdparty/Fuzzer/FuzzerTracePC.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerTracePC.cpp rename to test/thirdparty/Fuzzer/FuzzerTracePC.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerTracePC.h b/test/thirdparty/Fuzzer/FuzzerTracePC.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerTracePC.h rename to test/thirdparty/Fuzzer/FuzzerTracePC.h diff --git a/test/fuzz_test/Fuzzer/FuzzerTraceState.cpp b/test/thirdparty/Fuzzer/FuzzerTraceState.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerTraceState.cpp rename to test/thirdparty/Fuzzer/FuzzerTraceState.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.cpp b/test/thirdparty/Fuzzer/FuzzerUtil.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtil.cpp rename to test/thirdparty/Fuzzer/FuzzerUtil.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtil.h b/test/thirdparty/Fuzzer/FuzzerUtil.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtil.h rename to test/thirdparty/Fuzzer/FuzzerUtil.h diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp b/test/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilDarwin.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp b/test/thirdparty/Fuzzer/FuzzerUtilLinux.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilLinux.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilLinux.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp b/test/thirdparty/Fuzzer/FuzzerUtilPosix.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilPosix.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilPosix.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp b/test/thirdparty/Fuzzer/FuzzerUtilWindows.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerUtilWindows.cpp rename to test/thirdparty/Fuzzer/FuzzerUtilWindows.cpp diff --git a/test/fuzz_test/Fuzzer/FuzzerValueBitMap.h b/test/thirdparty/Fuzzer/FuzzerValueBitMap.h similarity index 100% rename from test/fuzz_test/Fuzzer/FuzzerValueBitMap.h rename to test/thirdparty/Fuzzer/FuzzerValueBitMap.h diff --git a/test/fuzz_test/Fuzzer/README.txt b/test/thirdparty/Fuzzer/README.txt similarity index 100% rename from test/fuzz_test/Fuzzer/README.txt rename to test/thirdparty/Fuzzer/README.txt diff --git a/test/fuzz_test/Fuzzer/afl/afl_driver.cpp b/test/thirdparty/Fuzzer/afl/afl_driver.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/afl/afl_driver.cpp rename to test/thirdparty/Fuzzer/afl/afl_driver.cpp diff --git a/test/fuzz_test/Fuzzer/build.sh b/test/thirdparty/Fuzzer/build.sh similarity index 100% rename from test/fuzz_test/Fuzzer/build.sh rename to test/thirdparty/Fuzzer/build.sh diff --git a/test/fuzz_test/Fuzzer/cxx.dict b/test/thirdparty/Fuzzer/cxx.dict similarity index 100% rename from test/fuzz_test/Fuzzer/cxx.dict rename to test/thirdparty/Fuzzer/cxx.dict diff --git a/test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c b/test/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c similarity index 100% rename from test/fuzz_test/Fuzzer/standalone/StandaloneFuzzTargetMain.c rename to test/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c diff --git a/test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp b/test/thirdparty/Fuzzer/test/AFLDriverTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AFLDriverTest.cpp rename to test/thirdparty/Fuzzer/test/AFLDriverTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp b/test/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AbsNegAndConstant64Test.cpp rename to test/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp diff --git a/test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp b/test/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AbsNegAndConstantTest.cpp rename to test/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp b/test/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/AccumulateAllocationsTest.cpp rename to test/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp b/test/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/BufferOverflowOnInput.cpp rename to test/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp diff --git a/test/fuzz_test/Fuzzer/test/CMakeLists.txt b/test/thirdparty/Fuzzer/test/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp b/test/thirdparty/Fuzzer/test/CallerCalleeTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CallerCalleeTest.cpp rename to test/thirdparty/Fuzzer/test/CallerCalleeTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/CounterTest.cpp b/test/thirdparty/Fuzzer/test/CounterTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CounterTest.cpp rename to test/thirdparty/Fuzzer/test/CounterTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp b/test/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CustomCrossOverTest.cpp rename to test/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp b/test/thirdparty/Fuzzer/test/CustomMutatorTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/CustomMutatorTest.cpp rename to test/thirdparty/Fuzzer/test/CustomMutatorTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSO1.cpp b/test/thirdparty/Fuzzer/test/DSO1.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSO1.cpp rename to test/thirdparty/Fuzzer/test/DSO1.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSO2.cpp b/test/thirdparty/Fuzzer/test/DSO2.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSO2.cpp rename to test/thirdparty/Fuzzer/test/DSO2.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp b/test/thirdparty/Fuzzer/test/DSOTestExtra.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSOTestExtra.cpp rename to test/thirdparty/Fuzzer/test/DSOTestExtra.cpp diff --git a/test/fuzz_test/Fuzzer/test/DSOTestMain.cpp b/test/thirdparty/Fuzzer/test/DSOTestMain.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DSOTestMain.cpp rename to test/thirdparty/Fuzzer/test/DSOTestMain.cpp diff --git a/test/fuzz_test/Fuzzer/test/DivTest.cpp b/test/thirdparty/Fuzzer/test/DivTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/DivTest.cpp rename to test/thirdparty/Fuzzer/test/DivTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/EmptyTest.cpp b/test/thirdparty/Fuzzer/test/EmptyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/EmptyTest.cpp rename to test/thirdparty/Fuzzer/test/EmptyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp b/test/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/FourIndependentBranchesTest.cpp rename to test/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp b/test/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/FullCoverageSetTest.cpp rename to test/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp b/test/thirdparty/Fuzzer/test/FuzzerUnittest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/FuzzerUnittest.cpp rename to test/thirdparty/Fuzzer/test/FuzzerUnittest.cpp diff --git a/test/fuzz_test/Fuzzer/test/InitializeTest.cpp b/test/thirdparty/Fuzzer/test/InitializeTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/InitializeTest.cpp rename to test/thirdparty/Fuzzer/test/InitializeTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/LeakTest.cpp b/test/thirdparty/Fuzzer/test/LeakTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/LeakTest.cpp rename to test/thirdparty/Fuzzer/test/LeakTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp b/test/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/LeakTimeoutTest.cpp rename to test/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/LoadTest.cpp b/test/thirdparty/Fuzzer/test/LoadTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/LoadTest.cpp rename to test/thirdparty/Fuzzer/test/LoadTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/MemcmpTest.cpp b/test/thirdparty/Fuzzer/test/MemcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/MemcmpTest.cpp rename to test/thirdparty/Fuzzer/test/MemcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp b/test/thirdparty/Fuzzer/test/NthRunCrashTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/NthRunCrashTest.cpp rename to test/thirdparty/Fuzzer/test/NthRunCrashTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp b/test/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/NullDerefOnEmptyTest.cpp rename to test/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/NullDerefTest.cpp b/test/thirdparty/Fuzzer/test/NullDerefTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/NullDerefTest.cpp rename to test/thirdparty/Fuzzer/test/NullDerefTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp b/test/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/OneHugeAllocTest.cpp rename to test/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/test/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp rename to test/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp b/test/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/OutOfMemoryTest.cpp rename to test/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp b/test/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/RepeatedBytesTest.cpp rename to test/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp b/test/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/RepeatedMemcmp.cpp rename to test/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp diff --git a/test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp b/test/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ShrinkControlFlowTest.cpp rename to test/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp b/test/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ShrinkValueProfileTest.cpp rename to test/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp b/test/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SignedIntOverflowTest.cpp rename to test/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp b/test/thirdparty/Fuzzer/test/SimpleCmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleCmpTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleCmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp b/test/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleDictionaryTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp b/test/thirdparty/Fuzzer/test/SimpleHashTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleHashTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleHashTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleTest.cpp b/test/thirdparty/Fuzzer/test/SimpleTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp b/test/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SimpleThreadedTest.cpp rename to test/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp b/test/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SingleMemcmpTest.cpp rename to test/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp b/test/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SingleStrcmpTest.cpp rename to test/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp b/test/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SingleStrncmpTest.cpp rename to test/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SpamyTest.cpp b/test/thirdparty/Fuzzer/test/SpamyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SpamyTest.cpp rename to test/thirdparty/Fuzzer/test/SpamyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrcmpTest.cpp b/test/thirdparty/Fuzzer/test/StrcmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrcmpTest.cpp rename to test/thirdparty/Fuzzer/test/StrcmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp b/test/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrncmpOOBTest.cpp rename to test/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrncmpTest.cpp b/test/thirdparty/Fuzzer/test/StrncmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrncmpTest.cpp rename to test/thirdparty/Fuzzer/test/StrncmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/StrstrTest.cpp b/test/thirdparty/Fuzzer/test/StrstrTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/StrstrTest.cpp rename to test/thirdparty/Fuzzer/test/StrstrTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp b/test/thirdparty/Fuzzer/test/SwapCmpTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SwapCmpTest.cpp rename to test/thirdparty/Fuzzer/test/SwapCmpTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/Switch2Test.cpp b/test/thirdparty/Fuzzer/test/Switch2Test.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/Switch2Test.cpp rename to test/thirdparty/Fuzzer/test/Switch2Test.cpp diff --git a/test/fuzz_test/Fuzzer/test/SwitchTest.cpp b/test/thirdparty/Fuzzer/test/SwitchTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/SwitchTest.cpp rename to test/thirdparty/Fuzzer/test/SwitchTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp b/test/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ThreadedLeakTest.cpp rename to test/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/ThreadedTest.cpp b/test/thirdparty/Fuzzer/test/ThreadedTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/ThreadedTest.cpp rename to test/thirdparty/Fuzzer/test/ThreadedTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp b/test/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/TimeoutEmptyTest.cpp rename to test/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/TimeoutTest.cpp b/test/thirdparty/Fuzzer/test/TimeoutTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/TimeoutTest.cpp rename to test/thirdparty/Fuzzer/test/TimeoutTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp b/test/thirdparty/Fuzzer/test/TraceMallocTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/TraceMallocTest.cpp rename to test/thirdparty/Fuzzer/test/TraceMallocTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp b/test/thirdparty/Fuzzer/test/UninstrumentedTest.cpp similarity index 100% rename from test/fuzz_test/Fuzzer/test/UninstrumentedTest.cpp rename to test/thirdparty/Fuzzer/test/UninstrumentedTest.cpp diff --git a/test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test b/test/thirdparty/Fuzzer/test/afl-driver-extra-stats.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/afl-driver-extra-stats.test rename to test/thirdparty/Fuzzer/test/afl-driver-extra-stats.test diff --git a/test/fuzz_test/Fuzzer/test/afl-driver-stderr.test b/test/thirdparty/Fuzzer/test/afl-driver-stderr.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/afl-driver-stderr.test rename to test/thirdparty/Fuzzer/test/afl-driver-stderr.test diff --git a/test/fuzz_test/Fuzzer/test/caller-callee.test b/test/thirdparty/Fuzzer/test/caller-callee.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/caller-callee.test rename to test/thirdparty/Fuzzer/test/caller-callee.test diff --git a/test/fuzz_test/Fuzzer/test/coverage.test b/test/thirdparty/Fuzzer/test/coverage.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/coverage.test rename to test/thirdparty/Fuzzer/test/coverage.test diff --git a/test/fuzz_test/Fuzzer/test/dict1.txt b/test/thirdparty/Fuzzer/test/dict1.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/dict1.txt rename to test/thirdparty/Fuzzer/test/dict1.txt diff --git a/test/fuzz_test/Fuzzer/test/dump_coverage.test b/test/thirdparty/Fuzzer/test/dump_coverage.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/dump_coverage.test rename to test/thirdparty/Fuzzer/test/dump_coverage.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test b/test/thirdparty/Fuzzer/test/fuzzer-customcrossover.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-customcrossover.test rename to test/thirdparty/Fuzzer/test/fuzzer-customcrossover.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test b/test/thirdparty/Fuzzer/test/fuzzer-custommutator.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-custommutator.test rename to test/thirdparty/Fuzzer/test/fuzzer-custommutator.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dict.test b/test/thirdparty/Fuzzer/test/fuzzer-dict.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-dict.test rename to test/thirdparty/Fuzzer/test/fuzzer-dict.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-dirs.test b/test/thirdparty/Fuzzer/test/fuzzer-dirs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-dirs.test rename to test/thirdparty/Fuzzer/test/fuzzer-dirs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test b/test/thirdparty/Fuzzer/test/fuzzer-fdmask.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-fdmask.test rename to test/thirdparty/Fuzzer/test/fuzzer-fdmask.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test b/test/thirdparty/Fuzzer/test/fuzzer-finalstats.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-finalstats.test rename to test/thirdparty/Fuzzer/test/fuzzer-finalstats.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-flags.test b/test/thirdparty/Fuzzer/test/fuzzer-flags.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-flags.test rename to test/thirdparty/Fuzzer/test/fuzzer-flags.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-jobs.test b/test/thirdparty/Fuzzer/test/fuzzer-jobs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-jobs.test rename to test/thirdparty/Fuzzer/test/fuzzer-jobs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-leak.test b/test/thirdparty/Fuzzer/test/fuzzer-leak.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-leak.test rename to test/thirdparty/Fuzzer/test/fuzzer-leak.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test b/test/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-oom-with-profile.test rename to test/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-oom.test b/test/thirdparty/Fuzzer/test/fuzzer-oom.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-oom.test rename to test/thirdparty/Fuzzer/test/fuzzer-oom.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test b/test/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-printcovpcs.test rename to test/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-runs.test b/test/thirdparty/Fuzzer/test/fuzzer-runs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-runs.test rename to test/thirdparty/Fuzzer/test/fuzzer-runs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-seed.test b/test/thirdparty/Fuzzer/test/fuzzer-seed.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-seed.test rename to test/thirdparty/Fuzzer/test/fuzzer-seed.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-segv.test b/test/thirdparty/Fuzzer/test/fuzzer-segv.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-segv.test rename to test/thirdparty/Fuzzer/test/fuzzer-segv.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test b/test/thirdparty/Fuzzer/test/fuzzer-singleinputs.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-singleinputs.test rename to test/thirdparty/Fuzzer/test/fuzzer-singleinputs.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-threaded.test b/test/thirdparty/Fuzzer/test/fuzzer-threaded.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-threaded.test rename to test/thirdparty/Fuzzer/test/fuzzer-threaded.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-timeout.test b/test/thirdparty/Fuzzer/test/fuzzer-timeout.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-timeout.test rename to test/thirdparty/Fuzzer/test/fuzzer-timeout.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test b/test/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-traces-hooks.test rename to test/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test b/test/thirdparty/Fuzzer/test/fuzzer-ubsan.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer-ubsan.test rename to test/thirdparty/Fuzzer/test/fuzzer-ubsan.test diff --git a/test/fuzz_test/Fuzzer/test/fuzzer.test b/test/thirdparty/Fuzzer/test/fuzzer.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/fuzzer.test rename to test/thirdparty/Fuzzer/test/fuzzer.test diff --git a/test/fuzz_test/Fuzzer/test/hi.txt b/test/thirdparty/Fuzzer/test/hi.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/hi.txt rename to test/thirdparty/Fuzzer/test/hi.txt diff --git a/test/fuzz_test/Fuzzer/test/lit.cfg b/test/thirdparty/Fuzzer/test/lit.cfg similarity index 100% rename from test/fuzz_test/Fuzzer/test/lit.cfg rename to test/thirdparty/Fuzzer/test/lit.cfg diff --git a/test/fuzz_test/Fuzzer/test/lit.site.cfg.in b/test/thirdparty/Fuzzer/test/lit.site.cfg.in similarity index 100% rename from test/fuzz_test/Fuzzer/test/lit.site.cfg.in rename to test/thirdparty/Fuzzer/test/lit.site.cfg.in diff --git a/test/fuzz_test/Fuzzer/test/merge.test b/test/thirdparty/Fuzzer/test/merge.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/merge.test rename to test/thirdparty/Fuzzer/test/merge.test diff --git a/test/fuzz_test/Fuzzer/test/minimize_crash.test b/test/thirdparty/Fuzzer/test/minimize_crash.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/minimize_crash.test rename to test/thirdparty/Fuzzer/test/minimize_crash.test diff --git a/test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt b/test/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/no-coverage/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/repeated-bytes.test b/test/thirdparty/Fuzzer/test/repeated-bytes.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/repeated-bytes.test rename to test/thirdparty/Fuzzer/test/repeated-bytes.test diff --git a/test/fuzz_test/Fuzzer/test/shrink.test b/test/thirdparty/Fuzzer/test/shrink.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/shrink.test rename to test/thirdparty/Fuzzer/test/shrink.test diff --git a/test/fuzz_test/Fuzzer/test/simple-cmp.test b/test/thirdparty/Fuzzer/test/simple-cmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/simple-cmp.test rename to test/thirdparty/Fuzzer/test/simple-cmp.test diff --git a/test/fuzz_test/Fuzzer/test/standalone.test b/test/thirdparty/Fuzzer/test/standalone.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/standalone.test rename to test/thirdparty/Fuzzer/test/standalone.test diff --git a/test/fuzz_test/Fuzzer/test/swap-cmp.test b/test/thirdparty/Fuzzer/test/swap-cmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/swap-cmp.test rename to test/thirdparty/Fuzzer/test/swap-cmp.test diff --git a/test/fuzz_test/Fuzzer/test/trace-malloc.test b/test/thirdparty/Fuzzer/test/trace-malloc.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/trace-malloc.test rename to test/thirdparty/Fuzzer/test/trace-malloc.test diff --git a/test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt b/test/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/ubsan/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/ulimit.test b/test/thirdparty/Fuzzer/test/ulimit.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/ulimit.test rename to test/thirdparty/Fuzzer/test/ulimit.test diff --git a/test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt b/test/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt similarity index 100% rename from test/fuzz_test/Fuzzer/test/uninstrumented/CMakeLists.txt rename to test/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.cfg b/test/thirdparty/Fuzzer/test/unit/lit.cfg similarity index 100% rename from test/fuzz_test/Fuzzer/test/unit/lit.cfg rename to test/thirdparty/Fuzzer/test/unit/lit.cfg diff --git a/test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in b/test/thirdparty/Fuzzer/test/unit/lit.site.cfg.in similarity index 100% rename from test/fuzz_test/Fuzzer/test/unit/lit.site.cfg.in rename to test/thirdparty/Fuzzer/test/unit/lit.site.cfg.in diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp.test b/test/thirdparty/Fuzzer/test/value-profile-cmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp2.test b/test/thirdparty/Fuzzer/test/value-profile-cmp2.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp2.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp2.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp3.test b/test/thirdparty/Fuzzer/test/value-profile-cmp3.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp3.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp3.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-cmp4.test b/test/thirdparty/Fuzzer/test/value-profile-cmp4.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-cmp4.test rename to test/thirdparty/Fuzzer/test/value-profile-cmp4.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-div.test b/test/thirdparty/Fuzzer/test/value-profile-div.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-div.test rename to test/thirdparty/Fuzzer/test/value-profile-div.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-load.test b/test/thirdparty/Fuzzer/test/value-profile-load.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-load.test rename to test/thirdparty/Fuzzer/test/value-profile-load.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-mem.test b/test/thirdparty/Fuzzer/test/value-profile-mem.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-mem.test rename to test/thirdparty/Fuzzer/test/value-profile-mem.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-set.test b/test/thirdparty/Fuzzer/test/value-profile-set.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-set.test rename to test/thirdparty/Fuzzer/test/value-profile-set.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strcmp.test b/test/thirdparty/Fuzzer/test/value-profile-strcmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-strcmp.test rename to test/thirdparty/Fuzzer/test/value-profile-strcmp.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-strncmp.test b/test/thirdparty/Fuzzer/test/value-profile-strncmp.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-strncmp.test rename to test/thirdparty/Fuzzer/test/value-profile-strncmp.test diff --git a/test/fuzz_test/Fuzzer/test/value-profile-switch.test b/test/thirdparty/Fuzzer/test/value-profile-switch.test similarity index 100% rename from test/fuzz_test/Fuzzer/test/value-profile-switch.test rename to test/thirdparty/Fuzzer/test/value-profile-switch.test diff --git a/test/src/catch.hpp b/test/thirdparty/catch/catch.hpp similarity index 100% rename from test/src/catch.hpp rename to test/thirdparty/catch/catch.hpp From 22a6b956e89d3468b28a46b11ad242ebee0c4954 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:36:34 +0100 Subject: [PATCH 35/71] :page_facing_up: added license for Catch --- test/thirdparty/catch/LICENSE_1_0.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 test/thirdparty/catch/LICENSE_1_0.txt diff --git a/test/thirdparty/catch/LICENSE_1_0.txt b/test/thirdparty/catch/LICENSE_1_0.txt new file mode 100755 index 00000000..36b7cd93 --- /dev/null +++ b/test/thirdparty/catch/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 048330b14b019d93e4f05437915fdd01d9861c81 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 09:37:15 +0100 Subject: [PATCH 36/71] :arrow_up: Catch v1.5.9 --- test/thirdparty/catch/catch.hpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) mode change 100644 => 100755 test/thirdparty/catch/catch.hpp diff --git a/test/thirdparty/catch/catch.hpp b/test/thirdparty/catch/catch.hpp old mode 100644 new mode 100755 index 2e6fe8d3..3d18eadb --- a/test/thirdparty/catch/catch.hpp +++ b/test/thirdparty/catch/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.5.8 - * Generated: 2016-10-26 12:07:30.938259 + * Catch v1.5.9 + * Generated: 2016-11-29 12:14:38.049276 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -3428,6 +3428,7 @@ namespace Catch { #include #include #include +#include namespace Catch { @@ -3995,9 +3996,12 @@ namespace Clara { inline void convertInto( std::string const& _source, std::string& _dest ) { _dest = _source; } + char toLowerCh(char c) { + return static_cast( ::tolower( c ) ); + } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) _dest = true; else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) @@ -7578,7 +7582,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 5, 8, "", 0 ); + Version libraryVersion( 1, 5, 9, "", 0 ); } @@ -9166,6 +9170,7 @@ namespace Catch { public: XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), + m_xml(_config.stream()), m_sectionDepth( 0 ) { m_reporterPrefs.shouldRedirectStdOut = true; @@ -9185,7 +9190,6 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); - m_xml.setStream( stream ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); From a084e90f392b6cbc4facb2bc993d21a88fe25ec9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 11:09:26 +0100 Subject: [PATCH 37/71] :hammer: split AFL test in driver and test file --- .gitignore | 2 + Makefile | 10 ++--- test/Makefile | 8 ++++ test/src/fuzz.cpp | 34 ----------------- test/src/fuzzer-driver_afl.cpp | 33 +++++++++++++++++ test/src/fuzzer-parse_json.cpp | 67 +++++++++++++++++++++++----------- 6 files changed, 91 insertions(+), 63 deletions(-) delete mode 100644 test/src/fuzz.cpp create mode 100644 test/src/fuzzer-driver_afl.cpp diff --git a/.gitignore b/.gitignore index 104befbe..eedc9641 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ cmake-build-debug test/test-* .svn + +test/thirdparty/Fuzzer/libFuzzer.a diff --git a/Makefile b/Makefile index fcce453e..8278dbf2 100644 --- a/Makefile +++ b/Makefile @@ -49,14 +49,10 @@ doctest: fuzz_testing: rm -fr fuzz-testing mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out - $(MAKE) fuzz CXX=afl-clang++ - mv fuzz fuzz-testing + $(MAKE) parse_afl_fuzzer -C test CXX=afl-clang++ + mv test/fuzzer parse_afl_fuzzer find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases - @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzz" - -# the fuzzer binary -fuzz: test/src/fuzz.cpp src/json.hpp - $(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src $< $(LDFLAGS) -o $@ + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" ########################################################################## diff --git a/test/Makefile b/test/Makefile index c1fb33f4..da679998 100644 --- a/test/Makefile +++ b/test/Makefile @@ -78,3 +78,11 @@ TEST_PATTERN = "*" TEST_PREFIX = "" check: $(TESTCASES) @cd .. ; for testcase in $(TESTCASES); do echo "Executing $$testcase..."; $(TEST_PREFIX)test/$$testcase $(TEST_PATTERN) || exit 1; done + + +############################################################################## +# fuzzer +############################################################################## + +parse_afl_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp deleted file mode 100644 index ef403ea8..00000000 --- a/test/src/fuzz.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Run "make fuzz_testing" and follow the instructions. - -Licensed under the MIT License . -*/ - -#include - -using json = nlohmann::json; - -int main() -{ -#ifdef __AFL_HAVE_MANUAL_CONTROL - while (__AFL_LOOP(1000)) - { -#endif - try - { - json j(std::cin); - std::cout << j << std::endl; - } - catch (std::invalid_argument& e) - { - std::cout << "Invalid argument in parsing" << e.what() << '\n'; - } -#ifdef __AFL_HAVE_MANUAL_CONTROL - } -#endif -} diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp new file mode 100644 index 00000000..e386033a --- /dev/null +++ b/test/src/fuzzer-driver_afl.cpp @@ -0,0 +1,33 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on +an implementation of the `LLVMFuzzerTestOneInput` function which processes a +passed byte array. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); + +int main() +{ +#ifdef __AFL_HAVE_MANUAL_CONTROL + while (__AFL_LOOP(1000)) + { +#endif + // copy stdin to stringstream to pass it to fuzzer as byte array + std::stringstream ss; + ss << std::cin.rdbuf(); + LLVMFuzzerTestOneInput(reinterpret_cast(ss.str().c_str()), ss.str().size()); +#ifdef __AFL_HAVE_MANUAL_CONTROL + } +#endif +} diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp index 20a824db..51ac440d 100644 --- a/test/src/fuzzer-parse_json.cpp +++ b/test/src/fuzzer-parse_json.cpp @@ -1,16 +1,23 @@ -// Copyright 2016 Google Inc. -// -// 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. +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = parse(data) +- s1 = serialize(j1) +- j2 = parse(s1) +- s2 = serialize(j2) +- assert(s1 == s2) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ #include #include @@ -18,25 +25,41 @@ using json = nlohmann::json; +// see http://llvm.org/docs/LibFuzzer.html extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { try { - std::stringstream s; - s << json::parse(data, data + size); + // step 1: parse input + json j1 = json::parse(data, data + size); + try { - auto j = json::parse(s.str()); - std::stringstream s2; - s2 << j; - assert(s.str() == s2.str()); - assert(j == json::parse(s.str())); + // step 2: round trip + + // first serialization + std::string s1 = j1.dump(); + + // parse serialization + json j2 = json::parse(s1); + + // second serialization + std::string s2 = j2.dump(); + + // serializations must match + assert(s1 == s2); } catch (const std::invalid_argument&) { - assert(0); + // parsing a JSON serialization must not fail + assert(false); } } - catch (const std::invalid_argument&) { } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + + // return 0 - non-zero return values are reserved for future use return 0; } From 33be4e7f1f19fea00279aa1733a3c72f475b1a12 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 12:08:36 +0100 Subject: [PATCH 38/71] :construction: cleanup --- Makefile | 10 ++++- test/Makefile | 3 ++ test/src/fuzzer-parse_cbor.cpp | 68 ++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/src/fuzzer-parse_cbor.cpp diff --git a/Makefile b/Makefile index 8278dbf2..3e873cbc 100644 --- a/Makefile +++ b/Makefile @@ -50,10 +50,18 @@ fuzz_testing: rm -fr fuzz-testing mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out $(MAKE) parse_afl_fuzzer -C test CXX=afl-clang++ - mv test/fuzzer parse_afl_fuzzer + mv test/parse_afl_fuzzer fuzz-testing/fuzzer find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" +fuzz_testing_cbor: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_cbor_fuzzer -C test CXX=afl-clang++ + mv test/parse_cbor_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *cbor | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + ########################################################################## # static analyzer diff --git a/test/Makefile b/test/Makefile index da679998..a063221a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -86,3 +86,6 @@ check: $(TESTCASES) parse_afl_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ + +parse_cbor_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@ diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp new file mode 100644 index 00000000..42ce679c --- /dev/null +++ b/test/src/fuzzer-parse_cbor.cpp @@ -0,0 +1,68 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = from_cbor(data) +- vec = to_cbor(j1) +- j2 = from_cbor(vec) +- assert(j1 == j2) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +using json = nlohmann::json; + +// see http://llvm.org/docs/LibFuzzer.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + // step 1: parse input + std::vector vec1(data, data + size); + json j1 = json::from_cbor(vec1); + + try + { + // step 2: round trip + std::vector vec2 = json::to_cbor(j1); + + // parse serialization + json j2 = json::from_cbor(vec2); + + // deserializations must match + assert(j1 == j2); + } + catch (const std::invalid_argument&) + { + // parsing a CBOR serialization must not fail + assert(false); + } + } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::out_of_range&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::domain_error&) + { + // parse errors are ok, because input may be random bytes + } + + // return 0 - non-zero return values are reserved for future use + return 0; +} From f87f4c06f9dc3b3813fa859479d321508675a5c0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 22 Dec 2016 12:10:52 +0100 Subject: [PATCH 39/71] :construction: cleanup --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3e873cbc..c99778af 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ all: # clean up clean: - rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM + rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM test/*.dSYM rm -fr benchmarks/files/numbers/*.json $(MAKE) clean -Cdoc $(MAKE) clean -Ctest From 6e129fbfb7a09856659763a13193d6ae1ee6ae0f Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 14:39:08 +0100 Subject: [PATCH 40/71] :memo: some small documentation fixes --- doc/Makefile | 1 + src/json.hpp | 16 ++++++++++------ src/json.hpp.re2c | 16 ++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 56198923..04a5cc7b 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -57,6 +57,7 @@ doxygen: create_output create_links $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html + $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html upload: clean doxygen check_output cd html ; ../scripts/git-update-ghpages nlohmann/json diff --git a/src/json.hpp b/src/json.hpp index 23058bee..cb301643 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -3793,7 +3793,7 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -3836,7 +3836,7 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -4187,10 +4187,14 @@ class basic_json element is not found or the JSON value is not an object, end() is returned. + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @@ -4233,6 +4237,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -4792,9 +4799,6 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of `clear()` to different diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ac9a3315..6723e75a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3793,7 +3793,7 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -3836,7 +3836,7 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @@ -4187,10 +4187,14 @@ class basic_json element is not found or the JSON value is not an object, end() is returned. + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @@ -4233,6 +4237,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -4792,9 +4799,6 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of `clear()` to different From dbdcb3fec8f4ac47d99ab283a5c60034b3297539 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:06:36 +0100 Subject: [PATCH 41/71] :bug: fixed bug in CBOR/MessagePack deserialization --- src/json.hpp | 30 ++++++++++++++++++++++++++++++ src/json.hpp.re2c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 23058bee..1d92b51a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6831,6 +6831,27 @@ class basic_json } } + /*! + @brief checks if a given length does not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size of the given vector @a vec. + + @param[in] vec byte vector + @param[in] len length + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const std::vector& vec, const size_t& len) + { + if (len > vec.size()) + { + throw std::out_of_range("len out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6882,6 +6903,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6985,6 +7007,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6993,6 +7016,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7001,6 +7025,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7219,6 +7244,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7227,6 +7253,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7235,6 +7262,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7243,6 +7271,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7251,6 +7280,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ac9a3315..d5b05c01 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6831,6 +6831,27 @@ class basic_json } } + /*! + @brief checks if a given length does not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size of the given vector @a vec. + + @param[in] vec byte vector + @param[in] len length + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const std::vector& vec, const size_t& len) + { + if (len > vec.size()) + { + throw std::out_of_range("len out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6882,6 +6903,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6985,6 +7007,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6993,6 +7016,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7001,6 +7025,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7219,6 +7244,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7227,6 +7253,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7235,6 +7262,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7243,6 +7271,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7251,6 +7280,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v, len + offset); return std::string(reinterpret_cast(v.data()) + offset, len); } From 6de9d4035a3becaa7d92812ca3ededfaf9a0cb29 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:06:56 +0100 Subject: [PATCH 42/71] :bug: fixed AFL driver to also read binary data --- test/src/fuzzer-driver_afl.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp index e386033a..eddeae41 100644 --- a/test/src/fuzzer-driver_afl.cpp +++ b/test/src/fuzzer-driver_afl.cpp @@ -11,9 +11,9 @@ passed byte array. Licensed under the MIT License . */ -#include -#include -#include +#include // for vector +#include // for uint8_t +#include // for cin extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); @@ -23,10 +23,15 @@ int main() while (__AFL_LOOP(1000)) { #endif - // copy stdin to stringstream to pass it to fuzzer as byte array - std::stringstream ss; - ss << std::cin.rdbuf(); - LLVMFuzzerTestOneInput(reinterpret_cast(ss.str().c_str()), ss.str().size()); + // copy stdin to byte vector + std::vector vec; + char c; + while (std::cin.get(c)) + { + vec.push_back(static_cast(c)); + } + + LLVMFuzzerTestOneInput(vec.data(), vec.size()); #ifdef __AFL_HAVE_MANUAL_CONTROL } #endif From e4cc62e6538662e0526eb81689a0d55334c1ac33 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:07:10 +0100 Subject: [PATCH 43/71] :bug: fixed Makefile to work with proper CBOR files --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c99778af..76e60170 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ fuzz_testing_cbor: mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out $(MAKE) parse_cbor_fuzzer -C test CXX=afl-clang++ mv test/parse_cbor_fuzzer fuzz-testing/fuzzer - find test/data -size -5k -name *cbor | xargs -I{} cp "{}" fuzz-testing/testcases + find test/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" From 1399abc58371978814e62b5eadc281ccbeb8dc96 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:18:56 +0100 Subject: [PATCH 44/71] :construction: added MessagePack fuzz target --- Makefile | 8 ++++ test/Makefile | 3 ++ test/src/fuzzer-parse_msgpack.cpp | 68 +++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 test/src/fuzzer-parse_msgpack.cpp diff --git a/Makefile b/Makefile index 76e60170..8f196950 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,14 @@ fuzz_testing_cbor: find test/data -size -5k -name *.cbor | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" +fuzz_testing_msgpack: + rm -fr fuzz-testing + mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out + $(MAKE) parse_msgpack_fuzzer -C test CXX=afl-clang++ + mv test/parse_msgpack_fuzzer fuzz-testing/fuzzer + find test/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases + @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" + ########################################################################## # static analyzer diff --git a/test/Makefile b/test/Makefile index a063221a..556ab0d7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -89,3 +89,6 @@ parse_afl_fuzzer: parse_cbor_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@ + +parse_msgpack_fuzzer: + $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_msgpack.cpp -o $@ diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp new file mode 100644 index 00000000..992697c2 --- /dev/null +++ b/test/src/fuzzer-parse_msgpack.cpp @@ -0,0 +1,68 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (fuzz test support) +| | |__ | | | | | | version 2.0.9 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +This file implements a parser test suitable for fuzz testing. Given a byte +array data, it performs the following steps: + +- j1 = from_msgpack(data) +- vec = to_msgpack(j1) +- j2 = from_msgpack(vec) +- assert(j1 == j2) + +The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer +drivers. + +Licensed under the MIT License . +*/ + +#include +#include +#include + +using json = nlohmann::json; + +// see http://llvm.org/docs/LibFuzzer.html +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + try + { + // step 1: parse input + std::vector vec1(data, data + size); + json j1 = json::from_msgpack(vec1); + + try + { + // step 2: round trip + std::vector vec2 = json::to_msgpack(j1); + + // parse serialization + json j2 = json::from_msgpack(vec2); + + // deserializations must match + assert(j1 == j2); + } + catch (const std::invalid_argument&) + { + // parsing a MessagePack serialization must not fail + assert(false); + } + } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::out_of_range&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::domain_error&) + { + // parse errors are ok, because input may be random bytes + } + + // return 0 - non-zero return values are reserved for future use + return 0; +} From e3b036348b16c1cbfb41e0d746160a99cfee989c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 25 Dec 2016 16:36:43 +0100 Subject: [PATCH 45/71] :construction: added targets for parallel AFL fuzzing --- Makefile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile b/Makefile index 8f196950..e2c120db 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,19 @@ fuzz_testing_msgpack: find test/data -size -5k -name *.msgpack | xargs -I{} cp "{}" fuzz-testing/testcases @echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer" +fuzzing-start: + afl-fuzz -S fuzzer1 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer2 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer3 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer4 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer5 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer6 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -S fuzzer7 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer > /dev/null & + afl-fuzz -M fuzzer0 -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzzer + +fuzzing-stop: + -killall fuzzer + -killall afl-fuzz ########################################################################## # static analyzer From acb7e0558d2337ad42261ed81e67d37385679285 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Dec 2016 12:09:15 +0100 Subject: [PATCH 46/71] :ambulance: fixed bugs detected by AFL-Fuzz --- src/json.hpp | 57 +++++++++++++ src/json.hpp.re2c | 57 +++++++++++++ ...0,sig:06,src:000223+000677,op:splice,rep:2 | 1 + ...id:000000,sig:06,src:000787,op:havoc,rep:8 | Bin 0 -> 19 bytes ...d:000000,sig:06,src:000833,op:havoc,rep:32 | Bin 0 -> 38 bytes ...d:000000,sig:06,src:000838,op:havoc,rep:64 | Bin 0 -> 842 bytes ...,sig:06,src:000846+001064,op:splice,rep:16 | Bin 0 -> 201 bytes ...id:000000,sig:06,src:000848,op:flip1,pos:0 | Bin 0 -> 263 bytes ...d:000000,sig:06,src:001435,op:havoc,rep:32 | Bin 0 -> 2488 bytes ...id:000000,sig:06,src:001436,op:havoc,rep:4 | Bin 0 -> 2579 bytes ...1,sig:06,src:000864+000903,op:splice,rep:2 | Bin 0 -> 33 bytes ...,sig:06,src:001310+001138,op:splice,rep:32 | 1 + ...,sig:06,src:001330+000569,op:splice,rep:64 | Bin 0 -> 1932 bytes ...d:000001,sig:06,src:001413,op:havoc,rep:32 | Bin 0 -> 1396 bytes ...id:000001,sig:06,src:001447,op:havoc,rep:4 | Bin 0 -> 1839 bytes ...1,sig:06,src:001465+000325,op:splice,rep:4 | Bin 0 -> 475 bytes ...id:000002,sig:06,src:000539,op:havoc,rep:8 | Bin 0 -> 140 bytes ...d:000002,sig:06,src:001301,op:havoc,rep:16 | Bin 0 -> 16 bytes ...2,sig:06,src:001317+000850,op:splice,rep:8 | Bin 0 -> 349 bytes ...:000002,sig:06,src:001382,op:havoc,rep:128 | Bin 0 -> 3116 bytes ...2,sig:06,src:001413+001036,op:splice,rep:4 | Bin 0 -> 395 bytes ...,sig:06,src:000846+000155,op:splice,rep:16 | Bin 0 -> 222 bytes ...:000004,sig:06,src:001445,op:havoc,rep:128 | Bin 0 -> 4382 bytes test/src/unit-cbor.cpp | 78 ++++++++++++++++++ 24 files changed, 194 insertions(+) create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32 create mode 100644 test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 create mode 100644 test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 create mode 100644 test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 create mode 100644 test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 create mode 100644 test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 diff --git a/src/json.hpp b/src/json.hpp index cb301643..913f1966 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6835,6 +6835,44 @@ class basic_json } } + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size @s size of the vector. Additionally, an @a offset is given from where + to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is a + valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + throw std::out_of_range("len out of range"); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits::max() - offset))) + { + throw std::out_of_range("len+offset out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6886,6 +6924,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6989,6 +7028,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6997,6 +7037,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7005,6 +7046,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7223,6 +7265,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7231,6 +7274,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7239,6 +7283,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7247,6 +7292,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7255,6 +7301,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7595,6 +7642,11 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_msgpack_internal(v, i); } @@ -7652,6 +7704,11 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_cbor_internal(v, i); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 6723e75a..3b598542 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6835,6 +6835,44 @@ class basic_json } } + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size @s size of the vector. Additionally, an @a offset is given from where + to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is a + valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + throw std::out_of_range("len out of range"); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits::max() - offset))) + { + throw std::out_of_range("len+offset out of range"); + } + } + /*! @brief create a JSON value from a given MessagePack vector @@ -6886,6 +6924,7 @@ class basic_json const size_t len = v[current_idx] & 0x1f; const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } } @@ -6989,6 +7028,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -6997,6 +7037,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7005,6 +7046,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7223,6 +7265,7 @@ class basic_json const auto len = static_cast(v[current_idx] - 0x60); const size_t offset = current_idx + 1; idx += len; // skip content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7231,6 +7274,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 2; idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7239,6 +7283,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 3; idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7247,6 +7292,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 5; idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7255,6 +7301,7 @@ class basic_json const auto len = static_cast(get_from_vector(v, current_idx)); const size_t offset = current_idx + 9; idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); return std::string(reinterpret_cast(v.data()) + offset, len); } @@ -7595,6 +7642,11 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_msgpack_internal(v, i); } @@ -7652,6 +7704,11 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { + if (v.empty()) + { + throw std::invalid_argument("empty vector"); + } + size_t i = 0; return from_cbor_internal(v, i); } diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 b/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 new file mode 100644 index 00000000..e30ed176 --- /dev/null +++ b/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 @@ -0,0 +1 @@ +{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 b/test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 new file mode 100644 index 0000000000000000000000000000000000000000..8de7c9e50d18d64f7807ba158718d1f71895dcbb GIT binary patch literal 19 Ucmb>a4*~zI8JHLtY#4w50DwOT>;M1& literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 b/test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 new file mode 100644 index 0000000000000000000000000000000000000000..d2ae80c771595f1dd59d700664577524eb5b6c78 GIT binary patch literal 38 ncmb>a4+Yz{ZCko+8v~FsY*hH0R#}`{B9NAmvAB3qiNr4e3C$Nr literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 b/test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 new file mode 100644 index 0000000000000000000000000000000000000000..d1ef19b507738ecf9c8e939347afc9087aad6876 GIT binary patch literal 842 zcmb>a4+Yz{$)Db~EwwbYcH+c|^OvrjxCRUqz~oj20SI@^|MwFoPMEl8B2ZVup>5l? zIcx@MO4*SF)C2^Ltq}Tri3G#{2FAoJ=bXmcYz2?hq6UUU28Hyzs?@xav}6VbCWay_ zl7JCb)j?QAQ3Z>D76NTeElSBt%uP)xF3p3=V>c32GkLs{%#ywm?bHf|U`fU=_3}V# zvvU(O^Ww`h8Gsa4+Y!6fB{6-9|Do5w{1(!O|6{>0&6F(K>}OX#DIW3n4CBfs(v$2eM)K)SpCFC u5Oo#;oa4+Y!6fB{6-9|Do5w{1%;O|6{>0&6F(K>}OhY_KGVfN9zc)RdB%1QBn9N}n&0 z_?41al9=V3lbM{Y;E`I?z>vtGke-*DnpcvR3?x#EQi?OHQYBOpbMsS*OY;mHGyKz9 zi&IM^(lRniBpJWd%O~chX6Ghm=Eaw1=B31xr}G8RB&QwU2&>{H!go0b6(hUX^LPGdafk>dNZ4(a0gz3!N-GSMi?aY+!s*&o| zo6*D@#Df?A0sjR42Tz_fF~JLHjL|x~Eu}PqH3ISAoaTG;_RV|W+q`d{)#i&!d9y^2 zLJQpOGYzRs8z}`MFheBe@Rs7`x`CDX!mLr{nZqV9@L@-7)kg>Sc0a!{SH3OX*tsx9 zc`&I$+oKE-VBuqmu9^0fl7IyEW&)Q6P~c^*_)UZ?J4^p@?Z=W(VN{3G1=R`d7c+(bM^11&Rp@+(O?DYiAyH7DcOGCx$~*tstnsV-&uSY<#-Ut3~9=uty?K?Qla7k zAktqsD6ZLDufIz^lbY8uq(*4nFbplFI$VZCm(xV@bF5EW5B;C5zoavemSvDA%3@z; Gb3Xwc-m0Vk literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 b/test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 new file mode 100644 index 0000000000000000000000000000000000000000..bb32f698895c0584b1476a95e8db9b6272bef2f5 GIT binary patch literal 2579 zcmdT`&uZFeee0V(Mq?y>hw4> ztfj)L0yIvC83Q_TQieGH&?v7DzHvs*NSMf(cR)f6&&|W_i)UWG`1?<1{onS5=ifGj zN=}-z6A8&U$TSk{2dA@W5SaF(Ye^^)SW})dc-kA>ymn>or`07_=Z-q}QppT-wDJK_ z>89M;eeUlzr`Du6cV?G8pa1Oe?3ZDOFWqtf>eqXp9l@_<#^3)~=nmu>aG|NI)HLe# z#k~COP0(6!p!P@W=rccp9>n=q*fmN+?X0vsxn=J^Ynff@bI)(Y!q*B_U>Z1(2%54q zT({M3F1CJbcYBTb^XJZ8x=L{#TN1KNX#%CYJGg=M$iyoD)eZ|`*wf74s681L9)E+J zJr)*7p2QYOj%w9c0m}wc8}Pt5p2uy$t~h#8huA(+n(H!ikHm0e2zne05mvgL7sw%C zLupzOnptU5Dc&0P4Oz`sNg%>ysSueSH#G1oV`y5GQ`CHTzPc={l}d$>i-crJ6j}UeU2mF#L8Z2zN~&}Aozh|5SAT6! z>Nm$IM&r=d^DdigO(vFj9cq5j@ac2kmJoBm;?Y z_eS}YtQuPWwCpUMUMkh9Me{7lPRw%7&{V29nj*kLq1hRWWH~!i{z~p}HNwF&rp2~W`Zh_GW^aNuJ{{+F_u*Bc&3FyiVZ_75HVtih%R!_3 NefxuDbw9>>^9`wVbX@=d literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 b/test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 new file mode 100644 index 0000000000000000000000000000000000000000..e94858106169ac59d0330ea1f59fcc42f09d2f2e GIT binary patch literal 33 ecmZ3OQa^bX!-NUe4Q2jmtlVHwUteGS9|i#A<`C`x literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 b/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 new file mode 100644 index 00000000..92079a80 --- /dev/null +++ b/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 @@ -0,0 +1 @@ +”{ÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿÿÿÿúúúúúúúúúúúúetú \ No newline at end of file diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 b/test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 new file mode 100644 index 0000000000000000000000000000000000000000..0e3f141f863bb079b063807cf5accd2f27fadc2f GIT binary patch literal 1932 zcmeHIy-ve05VlYsfHz=bth4X}s8Xg@0yR4v$tAe8!z&SihA-D}NXYksfh zZ^r%gVUQ~6EERDR(#aTem2`T8LYV`trRBMlnnK3|1F4PC_*zD2vZJ4(dxIA*e_#<1 z40SSsY%Ff^ob`~Pm<*bCNvLM>l<)-TQd7?NEh#cUXua0h4s`Y7D9fuGmQ(%$hfzmd literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 b/test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 new file mode 100644 index 0000000000000000000000000000000000000000..0ce68701c4269554399e96dd393c063e3493f186 GIT binary patch literal 1396 zcmc(fy>1gh5XaBBA{C_@q^ppSz$i!{WQ0UwLcj|4VH=@9LDsi(zBPBZ=bf?dtRQSU zBpM1T8dA~l0z3;(Kt&ah5QW)W8!P^Z3kuk3=Qp!Eqn-cGyQI>nRqZBc#P(!6CjrTJ z7!fGzcFYhrk4UyU;F6u#i6GzJv>|{HzR}U>#>MZ)KR(+BziQXMoGl3%ZBgv_B48YX z*ca@Q?Q9VOQ?9==Xp0EyvLy*Gw7QRXwlCkW-o2s>DDqYuFak$O7Z9Z?2gCXGYTh)~ z;dpKt_IgF!49$g4jh`prEKqOWo;0R=SY&6cMRL0RY&h(%TbbkdnSfFsTvb8M41?{=&yT_zxKUYdGh*7`tRU}EY<$Hd{5Rn`?h6SYXkm^ zlbOC(UFos3oAutBABi#cnZGByVr{{HTo~Z5yIZbsT XBGS3D8Q8;xiw>uMgr!9{#QfLaIWB|R literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 b/test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 new file mode 100644 index 0000000000000000000000000000000000000000..35f3cb597438e5ac05686abe6588f769b8e233a4 GIT binary patch literal 1839 zcmcJQ&ubGw6vq>LvLJZOMO*|61)HU|LP;r7(;pgQ5|fCCm(K3I%}zSIlgvz-je?DM z^y0yj7kly|UOf3H^l$Jl@F1RAK`hp{n@vp8h+1?mnfJ}in>X)$-p-45u2L)*RZ29; zi&|2E(ng7B4bnFnM5*~bEv!1&+GrbXrd?y+01h5})_dKh*~5b`?~Sc*#cLnVW|;6- zb))1mPAKq(%g7a@w5k;lX}jy48uOtnETPqm)x5WUWA0XA!<3PbrdER!4J9dTP)f)w z-yI!KM$O22bUZR9tQeCN!vyVrNslKxo|-J5P}?Vz-Dd;o^_!JS zlb3-qiFL)YVYQc{0u*;ZN=5+zaJQ6AY!|BFMSrP^77|idc}U9raDG?vV7X2$JL551 z3MCw!QBAHu?pQKon)zaOHGi{Msb=OE7Z#S+aGyms7$3OQj*yie9ezlMO1s(<(Wxa; zgH}}%dpot?iWPes#VVM9k?DHwxYr7CXh&Vj?tAm?z&Ba3VR4!e(b4l*Da{vg+U&2 z+|#2NlP9zgT=f-YdSO;L=sBBXPX!l+tfg(?{Je9{#iZvzO}O*Bcy#-dv0pmf9OO)u z!I7Zc>9OMV+|u^Z??&JEKXrO^gc%=BFw&D1eu@Th#-#9wB;MO?%yb@$7eX0a94Zc{;wuN_hTs%ru-M=EDyBf2ZLpk1Lk((va+i(HlC`+JNijke zMstF!IGR(FHFFBGo@zI7{D)-G(Qb;!Vw1ZG`+DD_ZNL9}$kyOZ=n9C0|A}^(6ngipP Xw8GTP^qdk&qc16WiMcu1h7!L3r4T|{ literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 b/test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 new file mode 100644 index 0000000000000000000000000000000000000000..0bde9e5dafffd9b12824980438be7148cc5b4cbd GIT binary patch literal 16 OcmeC4QvDwR7=!?C;0&Sw literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 b/test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 new file mode 100644 index 0000000000000000000000000000000000000000..0a004d9e5d88a9143366bb06355f4802b8978194 GIT binary patch literal 349 zcmbPI{hvs1k4SY?SM~;I;o-dL3m6BMJnB|<4nVhZQky_NikjS8r zo|l`NSCWw50=N G%P#;ZUH69o literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 b/test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 new file mode 100644 index 0000000000000000000000000000000000000000..6ff4823ae78f780f81f09637fa08249459c2338e GIT binary patch literal 3116 zcmZ3OGHdcIh6xj_8_N9CSaOQxfC&fTihD=^ zmJ%^tfy3DRUvO8cB0Cy~a;PgFg01&Mcv=CJu6MMh2i|+M{4J z1V%$(Gz11o2!IMPXxWP>{NN>izN8&e5ucl8EdW*zD$+f`HGmqDG@=Gk0GH(m^Pr6c zgaS}$>mQ$5VwJKZBm+`8LTYUXNUaU4$cV10vcb(2j7s7$j#3}exv8Y(RH1(cqw1~%i68hNn1;TEs0`+oC0P`eK(1T%FDknsW7PGcy~EXhzXRLD)u zO-e0NvWiwpEH7fH)Bp=xDtP6j=B8$pD8<(1DikXeD6p`yvAa3?xVd|HdU^Z!`uPV4 Q1_g(N&g}dzz`*bU07#X%a{vGU literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 b/test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 new file mode 100644 index 0000000000000000000000000000000000000000..d32904abc2805ce51238f7023c52ab0f6152b0f2 GIT binary patch literal 395 zcmbPI?iK9srx22oSd!?IpU#+{T3n(KkXTZZT9l_ykXT%7xS%9IDJwO(M4?=vJhLQ2 z!B8PJCp9;Hdb)f138V78{F0|P^1PKA7eVe0gX!Xt~-fC3^Y z0%|$G4;=P#ZGV0s&o?DAJ+masz|hDzH!nRiF()%UFE=HxB(3IYDllwQN)vNaQ;JI& Q8PYTS)6$AlOC){)06NLQRsaA1 literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 b/test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 new file mode 100644 index 0000000000000000000000000000000000000000..156afe0f177baf655b54b410a2128abd6b8002f9 GIT binary patch literal 222 zcmb>a4+XUofnf65i7(bnYy`5-mq`3dNi0dsa?Z(2&Q|b9Eoxv$WKc-Y%T3KINh{Ax zDap`f{F0WDnwjpDotsf2$@nEDFEJO$PRz`UFVD%%q_?{qTrpM zmzJ1Rnv+cz05x?4To@XD$phMKu6|zg48zX2k*_(E67XF hOw7rIx+WOtq|_pi^{FYvrFn)r89?KTQ+p(S0RTe0Vu%0$ literal 0 HcmV?d00001 diff --git a/test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 b/test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 new file mode 100644 index 0000000000000000000000000000000000000000..e342979eb1b875aadac159e722c4259527aec25c GIT binary patch literal 4382 zcmeHKPmkL~6rTvhp{E|h0V$VqKs3?P3Zbl!*zTXgDrCbZ)p9}Ei9NPwGw~!dlg6Q? zUdoOCLWm>ni33)23Cp)x75c{Zah@Hb3zPN8kI&=IVC@qk7I47+?hN{{GDee5ZbDL zuV)2`R-K(Sc_Ygi`q1g}vHwTH9l+i`=S-Fvw}nL+X$kB~i-BhXT#(|Uo{9g)b%R^jGjyg0?gnJ{hznCho>~i%|2>n4u{D&t`_0Cm(j*oh zGBx>PX;z;nMrLK;-%aR|`i<|Oot>GN2Gb}%Ao7q{;(h5{l7Xv5dVPjfxSVd$9Q~D) zuaZVuH|ca;+uSXh-b)5|En2_H3J4)Fu2L7iOu4(I`VEINY!cRYD1Non*fJ%-!fZngQ8s(G6}k7R4zD4&L8Rp34+dN4y~u@KHu!I+lk(A6 z($Uz~>$r<2{$#Rn9D2D#+BX@O5<^f@K}-g~jl(OJ*U|@kLS3q^_OZadpSp0^HEslP zdsiw$DJl$1R#ChMUNCG~+196;&W#dEk{Ej0S&yF4BT0 zME`y0NN$6nXv2k;_J*#m!oh7K1T_IBlyjUYyicI;LcDm5X-L@A*l|lRZ}Y_L7}Xv% z1ee@WRXJdi2>431Qm7qlZ`IyDXtt^w*RNgM+<|!=yr=PLTFc7fqM4{KS$0$pk~qLA zn+!E_b&EgeAlJ04L--o#$e1E`T0&QISQWn}ssd+JMLQ2Mn65ceMf=`oG%}8e-gB=QAYu$Yw*)b7jG;a(45*xvUs9eV zbVJLTLH7)c*T>++Vo9`#xqs=riIy02y^CuTL5Q!XWT8!TpiP8bYjgNDBu%p=VI7{MDt;{nKi-4=G z{p-qFXq)@9ObW5{EV&3m1(Zc`5jsT?#m(X(c@!3*=QXcRQP@}3q_6?|&A8q$xR7|) zBl7zoAirOdV^%GSlb)jUJei!IBea-sCLCCN6?zNQEfqBJg&*w! N4a8pAxG&4w{{SA`b1?t_ literal 0 HcmV?d00001 diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 14944cff..5769ac98 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1186,6 +1186,84 @@ TEST_CASE("single CBOR roundtrip") } } +TEST_CASE("CBOR regressions") +{ + SECTION("fuzz test results") + { + /* + The following test cases were found during a two-day session with + AFL-Fuzz. As a result, empty byte vectors and excessive lengths are + detected. + */ + for (std::string filename : + { + "test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2", + "test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8", + "test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32", + "test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64", + "test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16", + "test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0", + "test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32", + "test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4v", + "test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2", + "test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32", + "test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64", + "test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32", + "test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4", + "test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4", + "test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8", + "test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16", + "test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8", + "test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128", + "test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4", + "test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16", + "test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128" + }) + { + CAPTURE(filename); + + try + { + // parse CBOR file + std::ifstream f_cbor(filename, std::ios::binary); + std::vector vec1( + (std::istreambuf_iterator(f_cbor)), + std::istreambuf_iterator()); + json j1 = json::from_cbor(vec1); + + try + { + // step 2: round trip + std::vector vec2 = json::to_cbor(j1); + + // parse serialization + json j2 = json::from_cbor(vec2); + + // deserializations must match + CHECK(j1 == j2); + } + catch (const std::invalid_argument&) + { + // parsing a CBOR serialization must not fail + CHECK(false); + } + } + catch (const std::invalid_argument&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::out_of_range&) + { + // parse errors are ok, because input may be random bytes + } + catch (const std::domain_error&) + { + // parse errors are ok, because input may be random bytes + } + } + } +} + TEST_CASE("CBOR roundtrips", "[hide]") { SECTION("input from flynn") From 888f5b9f60591579f72339dc653e614562eb6e31 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Dec 2016 12:18:48 +0100 Subject: [PATCH 47/71] :truck: renamed test files to allow windows build --- ...c:000223+000677,op:splice,rep:2 => test01} | 0 ...ig:06,src:000787,op:havoc,rep:8 => test02} | Bin ...g:06,src:000833,op:havoc,rep:32 => test03} | Bin ...g:06,src:000838,op:havoc,rep:64 => test04} | Bin ...:000846+001064,op:splice,rep:16 => test05} | Bin ...ig:06,src:000848,op:flip1,pos:0 => test06} | Bin ...g:06,src:001435,op:havoc,rep:32 => test07} | Bin ...ig:06,src:001436,op:havoc,rep:4 => test08} | Bin ...c:000864+000903,op:splice,rep:2 => test09} | Bin ...:001310+001138,op:splice,rep:32 => test10} | 0 ...:001330+000569,op:splice,rep:64 => test11} | Bin ...g:06,src:001413,op:havoc,rep:32 => test12} | Bin ...ig:06,src:001447,op:havoc,rep:4 => test13} | Bin ...c:001465+000325,op:splice,rep:4 => test14} | Bin ...ig:06,src:000539,op:havoc,rep:8 => test15} | Bin ...:06,src:001445,op:havoc,rep:128 => test16} | Bin ...g:06,src:001301,op:havoc,rep:16 => test17} | Bin ...c:001317+000850,op:splice,rep:8 => test18} | Bin ...:06,src:001382,op:havoc,rep:128 => test19} | Bin ...c:001413+001036,op:splice,rep:4 => test20} | Bin ...:000846+000155,op:splice,rep:16 => test21} | Bin test/src/unit-cbor.cpp | 42 +++++++++--------- 22 files changed, 21 insertions(+), 21 deletions(-) rename test/data/cbor_regression/{id:000000,sig:06,src:000223+000677,op:splice,rep:2 => test01} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000787,op:havoc,rep:8 => test02} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000833,op:havoc,rep:32 => test03} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000838,op:havoc,rep:64 => test04} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000846+001064,op:splice,rep:16 => test05} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:000848,op:flip1,pos:0 => test06} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:001435,op:havoc,rep:32 => test07} (100%) rename test/data/cbor_regression/{id:000000,sig:06,src:001436,op:havoc,rep:4 => test08} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:000864+000903,op:splice,rep:2 => test09} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001310+001138,op:splice,rep:32 => test10} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001330+000569,op:splice,rep:64 => test11} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001413,op:havoc,rep:32 => test12} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001447,op:havoc,rep:4 => test13} (100%) rename test/data/cbor_regression/{id:000001,sig:06,src:001465+000325,op:splice,rep:4 => test14} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:000539,op:havoc,rep:8 => test15} (100%) rename test/data/cbor_regression/{id:000004,sig:06,src:001445,op:havoc,rep:128 => test16} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001301,op:havoc,rep:16 => test17} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001317+000850,op:splice,rep:8 => test18} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001382,op:havoc,rep:128 => test19} (100%) rename test/data/cbor_regression/{id:000002,sig:06,src:001413+001036,op:splice,rep:4 => test20} (100%) rename test/data/cbor_regression/{id:000003,sig:06,src:000846+000155,op:splice,rep:16 => test21} (100%) diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 b/test/data/cbor_regression/test01 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2 rename to test/data/cbor_regression/test01 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 b/test/data/cbor_regression/test02 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8 rename to test/data/cbor_regression/test02 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 b/test/data/cbor_regression/test03 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32 rename to test/data/cbor_regression/test03 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 b/test/data/cbor_regression/test04 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64 rename to test/data/cbor_regression/test04 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16 b/test/data/cbor_regression/test05 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16 rename to test/data/cbor_regression/test05 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0 b/test/data/cbor_regression/test06 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0 rename to test/data/cbor_regression/test06 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32 b/test/data/cbor_regression/test07 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32 rename to test/data/cbor_regression/test07 diff --git a/test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 b/test/data/cbor_regression/test08 similarity index 100% rename from test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4 rename to test/data/cbor_regression/test08 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 b/test/data/cbor_regression/test09 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2 rename to test/data/cbor_regression/test09 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 b/test/data/cbor_regression/test10 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32 rename to test/data/cbor_regression/test10 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 b/test/data/cbor_regression/test11 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64 rename to test/data/cbor_regression/test11 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 b/test/data/cbor_regression/test12 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32 rename to test/data/cbor_regression/test12 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 b/test/data/cbor_regression/test13 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4 rename to test/data/cbor_regression/test13 diff --git a/test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4 b/test/data/cbor_regression/test14 similarity index 100% rename from test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4 rename to test/data/cbor_regression/test14 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8 b/test/data/cbor_regression/test15 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8 rename to test/data/cbor_regression/test15 diff --git a/test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 b/test/data/cbor_regression/test16 similarity index 100% rename from test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128 rename to test/data/cbor_regression/test16 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 b/test/data/cbor_regression/test17 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16 rename to test/data/cbor_regression/test17 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 b/test/data/cbor_regression/test18 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8 rename to test/data/cbor_regression/test18 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 b/test/data/cbor_regression/test19 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128 rename to test/data/cbor_regression/test19 diff --git a/test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 b/test/data/cbor_regression/test20 similarity index 100% rename from test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4 rename to test/data/cbor_regression/test20 diff --git a/test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 b/test/data/cbor_regression/test21 similarity index 100% rename from test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16 rename to test/data/cbor_regression/test21 diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 5769ac98..9c41dc2f 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1197,27 +1197,27 @@ TEST_CASE("CBOR regressions") */ for (std::string filename : { - "test/data/cbor_regression/id:000000,sig:06,src:000223+000677,op:splice,rep:2", - "test/data/cbor_regression/id:000000,sig:06,src:000787,op:havoc,rep:8", - "test/data/cbor_regression/id:000000,sig:06,src:000833,op:havoc,rep:32", - "test/data/cbor_regression/id:000000,sig:06,src:000838,op:havoc,rep:64", - "test/data/cbor_regression/id:000000,sig:06,src:000846+001064,op:splice,rep:16", - "test/data/cbor_regression/id:000000,sig:06,src:000848,op:flip1,pos:0", - "test/data/cbor_regression/id:000000,sig:06,src:001435,op:havoc,rep:32", - "test/data/cbor_regression/id:000000,sig:06,src:001436,op:havoc,rep:4v", - "test/data/cbor_regression/id:000001,sig:06,src:000864+000903,op:splice,rep:2", - "test/data/cbor_regression/id:000001,sig:06,src:001310+001138,op:splice,rep:32", - "test/data/cbor_regression/id:000001,sig:06,src:001330+000569,op:splice,rep:64", - "test/data/cbor_regression/id:000001,sig:06,src:001413,op:havoc,rep:32", - "test/data/cbor_regression/id:000001,sig:06,src:001447,op:havoc,rep:4", - "test/data/cbor_regression/id:000001,sig:06,src:001465+000325,op:splice,rep:4", - "test/data/cbor_regression/id:000002,sig:06,src:000539,op:havoc,rep:8", - "test/data/cbor_regression/id:000002,sig:06,src:001301,op:havoc,rep:16", - "test/data/cbor_regression/id:000002,sig:06,src:001317+000850,op:splice,rep:8", - "test/data/cbor_regression/id:000002,sig:06,src:001382,op:havoc,rep:128", - "test/data/cbor_regression/id:000002,sig:06,src:001413+001036,op:splice,rep:4", - "test/data/cbor_regression/id:000003,sig:06,src:000846+000155,op:splice,rep:16", - "test/data/cbor_regression/id:000004,sig:06,src:001445,op:havoc,rep:128" + "test/data/cbor_regression/test01", + "test/data/cbor_regression/test02", + "test/data/cbor_regression/test03", + "test/data/cbor_regression/test04", + "test/data/cbor_regression/test05", + "test/data/cbor_regression/test06", + "test/data/cbor_regression/test07", + "test/data/cbor_regression/test08", + "test/data/cbor_regression/test09", + "test/data/cbor_regression/test10", + "test/data/cbor_regression/test11", + "test/data/cbor_regression/test12", + "test/data/cbor_regression/test13", + "test/data/cbor_regression/test14", + "test/data/cbor_regression/test15", + "test/data/cbor_regression/test16", + "test/data/cbor_regression/test17", + "test/data/cbor_regression/test18", + "test/data/cbor_regression/test19", + "test/data/cbor_regression/test20", + "test/data/cbor_regression/test21" }) { CAPTURE(filename); From 8381cd6020c57db59cf359ba4384424b93e51018 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 28 Dec 2016 14:19:08 +0100 Subject: [PATCH 48/71] :ambulance: removed unsafe call to strerror #403 --- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 67d05cf2..4515ca67 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9077,7 +9077,7 @@ class basic_json // immediately abort if stream is erroneous if (s.fail()) { - throw std::invalid_argument("stream error: " + std::string(strerror(errno))); + throw std::invalid_argument("stream error"); } // fill buffer diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index cbd8b220..6d649db3 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -9077,7 +9077,7 @@ class basic_json // immediately abort if stream is erroneous if (s.fail()) { - throw std::invalid_argument("stream error: " + std::string(strerror(errno))); + throw std::invalid_argument("stream error"); } // fill buffer From 871cebaf84d4f896bc730a92937f8d02e09b0023 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Dec 2016 15:39:16 +0100 Subject: [PATCH 49/71] :ambulance: fix for #405 --- src/json.hpp | 6 ++++++ src/json.hpp.re2c | 6 ++++++ test/src/unit-regression.cpp | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 4515ca67..76824cf8 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6871,6 +6871,12 @@ class basic_json { throw std::out_of_range("len+offset out of range"); } + + // last case: reading past the end of the vector + if (len + offset > size) + { + throw std::out_of_range("len+offset out of range"); + } } /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 6d649db3..10bfaf57 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6871,6 +6871,12 @@ class basic_json { throw std::out_of_range("len+offset out of range"); } + + // last case: reading past the end of the vector + if (len + offset > size) + { + throw std::out_of_range("len+offset out of range"); + } } /*! diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 1e720ddb..033041ab 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -540,4 +540,11 @@ TEST_CASE("regression tests") CHECK(j.is_number_float()); CHECK(j.dump() == "1.66020696663386e+20"); } + + SECTION("issue #405 - Heap-buffer-overflow (OSS-Fuzz issue 342)") + { + // original test case + std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; + CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + } } From 383a29a924af973e4cf9d5d2054a60f7d718f0d3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Dec 2016 16:14:15 +0100 Subject: [PATCH 50/71] :ambulance: fix for #407 --- src/json.hpp | 5 +++++ src/json.hpp.re2c | 5 +++++ test/src/unit-regression.cpp | 23 +++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/json.hpp b/src/json.hpp index 76824cf8..aab3b34f 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6981,6 +6981,7 @@ class basic_json case 0xca: // float 32 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -6993,6 +6994,7 @@ class basic_json case 0xcb: // float 64 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(double), 1); double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { @@ -7558,6 +7560,7 @@ class basic_json case 0xf9: // Half-Precision Float (two-byte IEEE 754) { + check_length(v.size(), 2, 1); idx += 2; // skip two content bytes // code from RFC 7049, Appendix D, Figure 3: @@ -7589,6 +7592,7 @@ class basic_json case 0xfa: // Single-Precision Float (four-byte IEEE 754) { // copy bytes in reverse order into the float variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -7600,6 +7604,7 @@ class basic_json case 0xfb: // Double-Precision Float (eight-byte IEEE 754) { + check_length(v.size(), sizeof(double), 1); // copy bytes in reverse order into the double variable double res; for (size_t byte = 0; byte < sizeof(double); ++byte) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 10bfaf57..7b6b7ec4 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6981,6 +6981,7 @@ class basic_json case 0xca: // float 32 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -6993,6 +6994,7 @@ class basic_json case 0xcb: // float 64 { // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(double), 1); double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { @@ -7558,6 +7560,7 @@ class basic_json case 0xf9: // Half-Precision Float (two-byte IEEE 754) { + check_length(v.size(), 2, 1); idx += 2; // skip two content bytes // code from RFC 7049, Appendix D, Figure 3: @@ -7589,6 +7592,7 @@ class basic_json case 0xfa: // Single-Precision Float (four-byte IEEE 754) { // copy bytes in reverse order into the float variable + check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { @@ -7600,6 +7604,7 @@ class basic_json case 0xfb: // Double-Precision Float (eight-byte IEEE 754) { + check_length(v.size(), sizeof(double), 1); // copy bytes in reverse order into the double variable double res; for (size_t byte = 0; byte < sizeof(double); ++byte) diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 033041ab..6123352d 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -547,4 +547,27 @@ TEST_CASE("regression tests") std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); } + + SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)") + { + // original test case: incomplete float64 + std::vector vec1 {0xcb, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range); + + // related test case: incomplete float32 + std::vector vec2 {0xca, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); + + // related test case: incomplete Half-Precision Float (CBOR) + std::vector vec3 {0xf9, 0x8f}; + CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + + // related test case: incomplete Single-Precision Float (CBOR) + std::vector vec4 {0xfa, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_cbor(vec4), std::out_of_range); + + // related test case: incomplete Double-Precision Float (CBOR) + std::vector vec5 {0xfb, 0x8f, 0x0a}; + CHECK_THROWS_AS(json::from_cbor(vec5), std::out_of_range); + } } From f0edab2363ae7dd50ee04bfacdb56f47ed6e7fb7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 29 Dec 2016 17:00:02 +0100 Subject: [PATCH 51/71] :ambulance: fix for #408 --- src/json.hpp | 37 ++++++--------------------------- src/json.hpp.re2c | 37 ++++++--------------------------- test/src/unit-regression.cpp | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index aab3b34f..f8d948f1 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6879,27 +6879,6 @@ class basic_json } } - /*! - @brief checks if a given length does not exceed the size of a given vector - - To secure the access to the byte vector during CBOR/MessagePack - deserialization, bytes are copied from the vector into buffers. This - function checks if the number of bytes to copy (@a len) does not exceed the - size of the given vector @a vec. - - @param[in] vec byte vector - @param[in] len length - - @throws out_of_range if `len > v.size()` - */ - static void check_length(const std::vector& vec, const size_t& len) - { - if (len > vec.size()) - { - throw std::out_of_range("len out of range"); - } - } - /*! @brief create a JSON value from a given MessagePack vector @@ -6916,6 +6895,9 @@ class basic_json */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7153,6 +7135,9 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7674,11 +7659,6 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_msgpack_internal(v, i); } @@ -7736,11 +7716,6 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_cbor_internal(v, i); } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7b6b7ec4..2faf3858 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6879,27 +6879,6 @@ class basic_json } } - /*! - @brief checks if a given length does not exceed the size of a given vector - - To secure the access to the byte vector during CBOR/MessagePack - deserialization, bytes are copied from the vector into buffers. This - function checks if the number of bytes to copy (@a len) does not exceed the - size of the given vector @a vec. - - @param[in] vec byte vector - @param[in] len length - - @throws out_of_range if `len > v.size()` - */ - static void check_length(const std::vector& vec, const size_t& len) - { - if (len > vec.size()) - { - throw std::out_of_range("len out of range"); - } - } - /*! @brief create a JSON value from a given MessagePack vector @@ -6916,6 +6895,9 @@ class basic_json */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7153,6 +7135,9 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + // store and increment index const size_t current_idx = idx++; @@ -7674,11 +7659,6 @@ class basic_json */ static basic_json from_msgpack(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_msgpack_internal(v, i); } @@ -7736,11 +7716,6 @@ class basic_json */ static basic_json from_cbor(const std::vector& v) { - if (v.empty()) - { - throw std::invalid_argument("empty vector"); - } - size_t i = 0; return from_cbor_internal(v, i); } diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 6123352d..421a386c 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -570,4 +570,44 @@ TEST_CASE("regression tests") std::vector vec5 {0xfb, 0x8f, 0x0a}; CHECK_THROWS_AS(json::from_cbor(vec5), std::out_of_range); } + + SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)") + { + // original test case + std::vector vec1 {0x87}; + CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range); + + // more test cases for MessagePack + for (uint8_t b : + { + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // fixmap + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // fixarray + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // fixstr + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf + }) + { + std::vector vec(1, b); + CHECK_THROWS_AS(json::from_msgpack(vec), std::out_of_range); + } + + // more test cases for CBOR + for (uint8_t b : + { + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // UTF-8 string + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // array + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7 // map + }) + { + std::vector vec(1, b); + CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + } + + // special case: empty input + std::vector vec2; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); + } } From d7029c37aad0e93033242123e7a7a05a1c76bed4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 30 Dec 2016 13:04:33 +0100 Subject: [PATCH 52/71] :white_check_mark: improved test coverage --- test/src/unit-cbor.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 9c41dc2f..49a54999 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -28,6 +28,7 @@ SOFTWARE. #include "catch.hpp" +#define private public #include "json.hpp" using nlohmann::json; @@ -1262,6 +1263,12 @@ TEST_CASE("CBOR regressions") } } } + + SECTION("improve code coverage") + { + // exotic edge case + CHECK_THROWS_AS(json::check_length(0xffffffffffffffff, 0xfffffffffffffff0, 0xff), std::out_of_range); + } } TEST_CASE("CBOR roundtrips", "[hide]") From 010ea126f3662c76e2d484e9710be2b8fcd53785 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Sun, 25 Dec 2016 14:04:04 +0200 Subject: [PATCH 53/71] going to try clang_sanitize with libstdc++ with g++-6 implementation Conflicts: .travis.yml Makefile --- .travis.yml | 22 ++++++++++++++-------- Makefile | 3 ++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 006647ed..6bf2297e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,14 +42,20 @@ matrix: - make check TEST_PREFIX="valgrind --error-exitcode=1 --leak-check=full " TEST_PATTERN="" # cLang sanitizer - - #- os: linux - # env: - # - LLVM_VERSION=3.8.1 - # - SPECIAL=sanitizer - # compiler: clang - # before_script: - # - make clang_sanitize + # note: sadly clang's libc++ has errors when running with sanitize, + # so we use clang with gcc's libstdc++ which doesn't give those error. + # that's why we need to install g++-6 to get the lastest version + - os: linux + env: + - LLVM_VERSION=3.8.1 + - SPECIAL=sanitizer + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: g++-6 + compiler: clang + before_script: + - make clang_sanitize # cppcheck diff --git a/Makefile b/Makefile index e2c120db..c16d9d9b 100644 --- a/Makefile +++ b/Makefile @@ -92,8 +92,9 @@ fuzzing-stop: cppcheck: cppcheck --enable=warning --inconclusive --force --std=c++11 src/json.hpp --error-exitcode=1 +# run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++) clang_sanitize: clean - CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) + CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) check -C test ########################################################################## From ff3221a37573e3313d52d46fd3b94ec658ff66e8 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Date: Sun, 25 Dec 2016 22:52:37 +0200 Subject: [PATCH 54/71] #394 fixed memory leak in unit-allocator, found by clang's fsanitize --- test/src/unit-allocator.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 25fd3349..c439c1c3 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -111,6 +111,16 @@ struct my_allocator : std::allocator } }; +// allows deletion of raw pointer, usually hold by json_value +template +void my_allocator_clean_up(T* p) +{ + assert(p != nullptr); + my_allocator alloc; + alloc.destroy(p); + alloc.deallocate(p, 1); +} + TEST_CASE("controlled bad_alloc") { // create JSON type using the throwing allocator @@ -131,7 +141,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::object; - CHECK_NOTHROW(my_json::json_value j(t)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.object); }; + CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); next_construct_fails = false; @@ -140,7 +151,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::array; - CHECK_NOTHROW(my_json::json_value j(t)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.array); }; + CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); next_construct_fails = false; @@ -149,7 +161,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::string; - CHECK_NOTHROW(my_json::json_value j(t)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); next_construct_fails = false; @@ -160,7 +173,8 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; my_json::string_t v("foo"); - CHECK_NOTHROW(my_json::json_value j(v)); + auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + CHECK_NOTHROW(my_json::json_value j(v); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); next_construct_fails = false; From cf9bf2d9136a9d1c2131f83aa493bb03f36849ab Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 15:28:01 +0100 Subject: [PATCH 55/71] :ambulance: fix for #411 and #412 --- src/json.hpp | 11 +++----- src/json.hpp.re2c | 11 +++----- test/src/unit-regression.cpp | 53 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index f8d948f1..5dc8e67c 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7135,13 +7135,10 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { - // make sure reading 1 byte is safe - check_length(v.size(), 1, idx); - // store and increment index const size_t current_idx = idx++; - switch (v[current_idx]) + switch (v.at(current_idx)) { // Integer 0x00..0x17 (0..23) case 0x00: @@ -7322,7 +7319,7 @@ class basic_json case 0x7f: // UTF-8 string (indefinite length) { std::string result; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { string_t s = from_cbor_internal(v, idx); result += s; @@ -7418,7 +7415,7 @@ class basic_json case 0x9f: // array (indefinite length) { basic_json result = value_t::array; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { result.push_back(from_cbor_internal(v, idx)); } @@ -7518,7 +7515,7 @@ class basic_json case 0xbf: // map (indefinite length) { basic_json result = value_t::object; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 2faf3858..49bccb02 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7135,13 +7135,10 @@ class basic_json */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { - // make sure reading 1 byte is safe - check_length(v.size(), 1, idx); - // store and increment index const size_t current_idx = idx++; - switch (v[current_idx]) + switch (v.at(current_idx)) { // Integer 0x00..0x17 (0..23) case 0x00: @@ -7322,7 +7319,7 @@ class basic_json case 0x7f: // UTF-8 string (indefinite length) { std::string result; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { string_t s = from_cbor_internal(v, idx); result += s; @@ -7418,7 +7415,7 @@ class basic_json case 0x9f: // array (indefinite length) { basic_json result = value_t::array; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { result.push_back(from_cbor_internal(v, idx)); } @@ -7518,7 +7515,7 @@ class basic_json case 0xbf: // map (indefinite length) { basic_json result = value_t::object; - while (v[idx] != 0xff) + while (v.at(idx) != 0xff) { std::string key = from_cbor_internal(v, idx); result[key] = from_cbor_internal(v, idx); diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 421a386c..89b27e0e 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -610,4 +610,57 @@ TEST_CASE("regression tests") CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); CHECK_THROWS_AS(json::from_msgpack(vec2), std::out_of_range); } + + SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)") + { + // original test case: empty UTF-8 string (indefinite length) + std::vector vec1 {0x7f}; + CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + + // related test case: empty array (indefinite length) + std::vector vec2 {0x9f}; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + + // related test case: empty map (indefinite length) + std::vector vec3 {0xbf}; + CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + } + + SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)") + { + // original test case + std::vector vec + { + 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00, + 0x60, 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xa0, 0x9f, + 0x9f, 0x97, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60 + }; + CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range); + + // related test case: nonempty UTF-8 string (indefinite length) + std::vector vec1 {0x7f, 0x61, 0x61}; + CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + + // related test case: nonempty array (indefinite length) + std::vector vec2 {0x9f, 0x01}; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + + // related test case: nonempty map (indefinite length) + std::vector vec3 {0xbf, 0x61, 0x61, 0x01}; + CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); + } } From ab7d55e7ae7c9fab666912876aaa9d9e20d1b618 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 19:38:54 +0100 Subject: [PATCH 56/71] :construction_worker: removed failing test suites --- .doozer.json | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/.doozer.json b/.doozer.json index 589dccc3..7dbaa5d2 100644 --- a/.doozer.json +++ b/.doozer.json @@ -1,30 +1,5 @@ { "targets": { - "jessie-i386": { - "buildenv": "jessie-i386", - "builddeps": ["build-essential", "cmake", "clang"], - "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, - "precise-i386": { - "buildenv": "precise-i386", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, - "precise-amd64": { - "buildenv": "precise-amd64", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, - "trusty-i386": { - "buildenv": "trusty-i386", - "builddeps": ["build-essential", "cmake", "clang"], - "buildcmd": ["make check CXX=clang++"] - }, - "trusty-amd64": { - "buildenv": "trusty-amd64", - "builddeps": ["build-essential", "cmake", "clang"], - "buildcmd": ["make check CXX=clang++"] - }, "xenial-i386": { "buildenv": "xenial-i386", "builddeps": ["build-essential", "cmake"], @@ -40,11 +15,6 @@ "builddeps": ["cmake", "make", "clang"], "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"] }, - "centos7-x86_64": { - "buildenv": "centos7-x86_64", - "builddeps": ["build-essential", "cmake"], - "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"] - }, "osx": { "buildenv": "osx", "builddeps": ["build-essential"], From db33629990c0125bde0fb61e894e00496e1e22b0 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 21:34:58 +0100 Subject: [PATCH 57/71] :lipstick: cleanup after #410 --- Makefile | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c16d9d9b..56cbdb0c 100644 --- a/Makefile +++ b/Makefile @@ -94,7 +94,7 @@ cppcheck: # run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++) clang_sanitize: clean - CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) check -C test + CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) ########################################################################## diff --git a/README.md b/README.md index 16c7eabf..06dca7ff 100644 --- a/README.md +++ b/README.md @@ -586,6 +586,7 @@ I deeply appreciate the help of the following people. - [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning. - [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. - [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. +- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers. Thanks a lot for helping out! From a3063c24194df831cc95b6429a648ea3a6739fcf Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 1 Jan 2017 21:51:36 +0100 Subject: [PATCH 58/71] :construction: added target to build all fuzzers --- .gitignore | 6 ++++++ test/Makefile | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index eedc9641..6e15abc4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,9 @@ test/test-* .svn test/thirdparty/Fuzzer/libFuzzer.a + +test/parse_afl_fuzzer + +test/parse_cbor_fuzzer + +test/parse_msgpack_fuzzer diff --git a/test/Makefile b/test/Makefile index 556ab0d7..68520bd3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -84,6 +84,8 @@ check: $(TESTCASES) # fuzzer ############################################################################## +fuzzers: parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer + parse_afl_fuzzer: $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ From d1735172127c37bfee7a816d96d34647cfff6494 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:21:26 +0100 Subject: [PATCH 59/71] :lipstick: clean up --- src/json.hpp | 9 +++++++++ src/json.hpp.re2c | 9 +++++++++ test/src/unit-allocator.cpp | 20 ++++++++++++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 5dc8e67c..afa35baa 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -873,8 +873,17 @@ class basic_json break; } + case value_t::null: + { + break; + } + default: { + if (t == value_t::null) + { + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + } break; } } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 49bccb02..db0b4c88 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -873,8 +873,17 @@ class basic_json break; } + case value_t::null: + { + break; + } + default: { + if (t == value_t::null) + { + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + } break; } } diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index c439c1c3..04f6ac9d 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -141,7 +141,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::object; - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.object); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.object); + }; CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); @@ -151,7 +154,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::array; - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.array); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.array); + }; CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); @@ -161,7 +167,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; auto t = my_json::value_t::string; - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.string); + }; CHECK_NOTHROW(my_json::json_value j(t); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); @@ -173,7 +182,10 @@ TEST_CASE("controlled bad_alloc") { next_construct_fails = false; my_json::string_t v("foo"); - auto clean_up = [](my_json::json_value& j){ my_allocator_clean_up(j.string); }; + auto clean_up = [](my_json::json_value & j) + { + my_allocator_clean_up(j.string); + }; CHECK_NOTHROW(my_json::json_value j(v); clean_up(j)); next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); From 60b3703c62bdc3740ca7564bd8c28abb5337a2b4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:35:57 +0100 Subject: [PATCH 60/71] :bookmark: version bump to 2.0.10 --- CMakeLists.txt | 2 +- doc/Doxyfile | 2 +- doc/index.md | 2 +- src/json.hpp | 4 ++-- src/json.hpp.re2c | 4 ++-- test/src/fuzzer-driver_afl.cpp | 2 +- test/src/fuzzer-parse_cbor.cpp | 2 +- test/src/fuzzer-parse_json.cpp | 2 +- test/src/fuzzer-parse_msgpack.cpp | 2 +- test/src/unit-algorithms.cpp | 2 +- test/src/unit-allocator.cpp | 2 +- test/src/unit-capacity.cpp | 2 +- test/src/unit-cbor.cpp | 2 +- test/src/unit-class_const_iterator.cpp | 2 +- test/src/unit-class_iterator.cpp | 2 +- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 2 +- test/src/unit-comparison.cpp | 2 +- test/src/unit-concepts.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- test/src/unit-constructor2.cpp | 2 +- test/src/unit-convenience.cpp | 2 +- test/src/unit-conversions.cpp | 2 +- test/src/unit-deserialization.cpp | 2 +- test/src/unit-element_access1.cpp | 2 +- test/src/unit-element_access2.cpp | 2 +- test/src/unit-inspection.cpp | 2 +- test/src/unit-iterator_wrapper.cpp | 2 +- test/src/unit-iterators1.cpp | 2 +- test/src/unit-iterators2.cpp | 2 +- test/src/unit-json_patch.cpp | 2 +- test/src/unit-json_pointer.cpp | 2 +- test/src/unit-modifiers.cpp | 2 +- test/src/unit-msgpack.cpp | 2 +- test/src/unit-pointer_access.cpp | 2 +- test/src/unit-readme.cpp | 2 +- test/src/unit-reference_access.cpp | 2 +- test/src/unit-regression.cpp | 2 +- test/src/unit-serialization.cpp | 2 +- test/src/unit-testsuites.cpp | 2 +- test/src/unit-unicode.cpp | 2 +- test/src/unit.cpp | 2 +- 42 files changed, 44 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80770367..9f9931c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) # define the project -project(nlohmann_json VERSION 2.0.9 LANGUAGES CXX) +project(nlohmann_json VERSION 2.0.10 LANGUAGES CXX) enable_testing() diff --git a/doc/Doxyfile b/doc/Doxyfile index 17c81d8b..5064a0a0 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "JSON for Modern C++" -PROJECT_NUMBER = 2.0.9 +PROJECT_NUMBER = 2.0.10 PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = . diff --git a/doc/index.md b/doc/index.md index 8292fe65..cc5cd793 100644 --- a/doc/index.md +++ b/doc/index.md @@ -277,4 +277,4 @@ The container functions known from STL have been extended to support the differe @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code -@version 2.0.9 +@version 2.0.10 diff --git a/src/json.hpp b/src/json.hpp index afa35baa..471b9ec7 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -882,7 +882,7 @@ class basic_json { if (t == value_t::null) { - throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE } break; } diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index db0b4c88..9a7c2043 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -882,7 +882,7 @@ class basic_json { if (t == value_t::null) { - throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21"); // LCOV_EXCL_LINE + throw std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.0.10"); // LCOV_EXCL_LINE } break; } diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp index eddeae41..0c173b45 100644 --- a/test/src/fuzzer-driver_afl.cpp +++ b/test/src/fuzzer-driver_afl.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp index 42ce679c..bba56747 100644 --- a/test/src/fuzzer-parse_cbor.cpp +++ b/test/src/fuzzer-parse_cbor.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a parser test suitable for fuzz testing. Given a byte diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp index 51ac440d..f61df56d 100644 --- a/test/src/fuzzer-parse_json.cpp +++ b/test/src/fuzzer-parse_json.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a parser test suitable for fuzz testing. Given a byte diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp index 992697c2..0355db3b 100644 --- a/test/src/fuzzer-parse_msgpack.cpp +++ b/test/src/fuzzer-parse_msgpack.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json This file implements a parser test suitable for fuzz testing. Given a byte diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 31be6556..04704847 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 04f6ac9d..00eb5b9a 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 7fc3d49c..4664f1bf 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 49a54999..d01019d9 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 2970b865..2097e830 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 14c6828b..bd64cbf8 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 6dec3f82..5656942f 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 7bf23d50..3e11d80c 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 73c31abc..81c3066d 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index dea6b510..cf60b5c1 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 23c0db38..114ae5b3 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index 0bbd13cf..ae5ba3b7 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 94341be1..1aedea44 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 72c3bf60..77d476bd 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 605c596b..f0cf2e15 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index 0a1715e7..04265244 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 29be17a1..f2758a59 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index 1660c719..1051d2f5 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index 96928eb4..784fa77a 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index 60f7c2fa..bf14435b 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index da0fc937..5c04826d 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 880bc46d..4983aab2 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 91d003ab..4268c3c0 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 02ffa6a5..2db3f291 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 46997e27..eab4dcac 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index e1c9caab..f5d934d3 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index 92b13222..bddcd0d6 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index 4a8047f8..321907a5 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 89b27e0e..0bfdc91a 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 8385a4a4..3375a1c4 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index 92b6e5aa..dfc75936 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 4cb51e21..69f4a9ee 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit.cpp b/test/src/unit.cpp index 1a278868..e7a7a799 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.9 +| | |__ | | | | | | version 2.0.10 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . From 6a207900562cededec7189c548c86743c65902cd Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:36:30 +0100 Subject: [PATCH 61/71] :memo: updated test count --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06dca7ff..a0534615 100644 --- a/README.md +++ b/README.md @@ -610,7 +610,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (11201893 assertions in 43 test cases) +All tests passed (11202040 assertions in 44 test cases) ``` Alternatively, you can use [CMake](https://cmake.org) and run From 8b46eb8ec0abd05535520fc25e2bdbd3a2c71820 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:40:00 +0100 Subject: [PATCH 62/71] :page_facing_up: it's 2017 already --- LICENSE.MIT | 2 +- doc/index.md | 2 +- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- test/src/unit-algorithms.cpp | 2 +- test/src/unit-allocator.cpp | 2 +- test/src/unit-capacity.cpp | 2 +- test/src/unit-cbor.cpp | 2 +- test/src/unit-class_const_iterator.cpp | 2 +- test/src/unit-class_iterator.cpp | 2 +- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 2 +- test/src/unit-comparison.cpp | 2 +- test/src/unit-concepts.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- test/src/unit-constructor2.cpp | 2 +- test/src/unit-convenience.cpp | 2 +- test/src/unit-conversions.cpp | 2 +- test/src/unit-deserialization.cpp | 2 +- test/src/unit-element_access1.cpp | 2 +- test/src/unit-element_access2.cpp | 2 +- test/src/unit-inspection.cpp | 2 +- test/src/unit-iterator_wrapper.cpp | 2 +- test/src/unit-iterators1.cpp | 2 +- test/src/unit-iterators2.cpp | 2 +- test/src/unit-json_patch.cpp | 2 +- test/src/unit-json_pointer.cpp | 2 +- test/src/unit-modifiers.cpp | 2 +- test/src/unit-msgpack.cpp | 2 +- test/src/unit-pointer_access.cpp | 2 +- test/src/unit-readme.cpp | 2 +- test/src/unit-reference_access.cpp | 2 +- test/src/unit-regression.cpp | 2 +- test/src/unit-serialization.cpp | 2 +- test/src/unit-testsuites.cpp | 2 +- test/src/unit-unicode.cpp | 2 +- test/src/unit.cpp | 2 +- 37 files changed, 37 insertions(+), 37 deletions(-) diff --git a/LICENSE.MIT b/LICENSE.MIT index 79145efa..c4ce40d1 100644 --- a/LICENSE.MIT +++ b/LICENSE.MIT @@ -1,7 +1,7 @@ JSON for Modern C++ is licensed under the MIT License : -Copyright (c) 2013-2016 Niels Lohmann +Copyright (c) 2013-2017 Niels Lohmann 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 diff --git a/doc/index.md b/doc/index.md index cc5cd793..f7ef3554 100644 --- a/doc/index.md +++ b/doc/index.md @@ -272,7 +272,7 @@ The container functions known from STL have been extended to support the differe -@copyright Copyright © 2013-2016 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). +@copyright Copyright © 2013-2017 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code diff --git a/src/json.hpp b/src/json.hpp index 471b9ec7..9d48e7a6 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 9a7c2043..e1a43b54 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp index 04704847..0905d05e 100644 --- a/test/src/unit-algorithms.cpp +++ b/test/src/unit-algorithms.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 00eb5b9a..f11d8538 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 4664f1bf..d3ee33d2 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index d01019d9..92238b79 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 2097e830..13ce7c3f 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index bd64cbf8..640bc816 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 5656942f..33ea610a 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 3e11d80c..09515042 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 81c3066d..7f89729e 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index cf60b5c1..1c04b62b 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 114ae5b3..6bfb4402 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index ae5ba3b7..ab1e43cc 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 1aedea44..cd78f83a 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 77d476bd..b82127bb 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index f0cf2e15..21e3bb44 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index 04265244..a596ac21 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index f2758a59..1ba6aa61 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index 1051d2f5..9e114718 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index 784fa77a..f4255f99 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index bf14435b..a6fd2df9 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index 5c04826d..6f1b6251 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index 4983aab2..b7987509 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 4268c3c0..67257943 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 2db3f291..3e8d9600 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index eab4dcac..89fa450d 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index f5d934d3..4771c508 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index bddcd0d6..5c62e850 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index 321907a5..54db1a86 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 0bfdc91a..401867c2 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 3375a1c4..72d9ae6c 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index dfc75936..a43e1997 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 69f4a9ee..0b1c0e5e 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/test/src/unit.cpp b/test/src/unit.cpp index e7a7a799..096a2973 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -5,7 +5,7 @@ |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . -Copyright (c) 2013-2016 Niels Lohmann . +Copyright (c) 2013-2017 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 4d3bf433b45d8f445536f51585ad0644611277f7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 09:56:21 +0100 Subject: [PATCH 63/71] :bookmark: updated ChangeLog --- ChangeLog.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 92545768..ce82d14b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,26 @@ # Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [v2.0.10](https://github.com/nlohmann/json/releases/tag/v2.0.10) (2017-01-02) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.9...v2.0.10) + +- Heap-buffer-overflow \(OSS-Fuzz issue 367\) [\#412](https://github.com/nlohmann/json/issues/412) +- Heap-buffer-overflow \(OSS-Fuzz issue 366\) [\#411](https://github.com/nlohmann/json/issues/411) +- Use-of-uninitialized-value \(OSS-Fuzz issue 347\) [\#409](https://github.com/nlohmann/json/issues/409) +- Heap-buffer-overflow \(OSS-Fuzz issue 344\) [\#408](https://github.com/nlohmann/json/issues/408) +- Heap-buffer-overflow \(OSS-Fuzz issue 343\) [\#407](https://github.com/nlohmann/json/issues/407) +- Heap-buffer-overflow \(OSS-Fuzz issue 342\) [\#405](https://github.com/nlohmann/json/issues/405) +- strerror throwing error in compiler VS2015 [\#403](https://github.com/nlohmann/json/issues/403) +- json::parse of std::string being underlined by Visual Studio [\#402](https://github.com/nlohmann/json/issues/402) +- Explicitly getting string without .dump\(\) [\#401](https://github.com/nlohmann/json/issues/401) +- Possible to speed up json::parse? [\#398](https://github.com/nlohmann/json/issues/398) +- the alphabetic order in the code influence console\_output. [\#396](https://github.com/nlohmann/json/issues/396) +- Execute tests with clang sanitizers [\#394](https://github.com/nlohmann/json/issues/394) +- Check if library can be used with ETL [\#361](https://github.com/nlohmann/json/issues/361) + +- Feature/clang sanitize [\#410](https://github.com/nlohmann/json/pull/410) ([Daniel599](https://github.com/Daniel599)) +- Add Doozer build badge [\#400](https://github.com/nlohmann/json/pull/400) ([andoma](https://github.com/andoma)) + ## [v2.0.9](https://github.com/nlohmann/json/releases/tag/v2.0.9) (2016-12-16) [Full Changelog](https://github.com/nlohmann/json/compare/v2.0.8...v2.0.9) From 69c615e1274ddb8ba07b3ba19eee951946bd5ddb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Jan 2017 10:00:44 +0100 Subject: [PATCH 64/71] :bookmark: version bump to 2.0.10 --- doc/json.gif | Bin 1325973 -> 1331326 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/json.gif b/doc/json.gif index 7d3bf81645496c9a72e4f187e21a3ce2ba9223ef..efd2ee634a0704653811c31714f7cc1757c02966 100644 GIT binary patch delta 50413 zcmWKWXE@sp7smfVhzJr}jmF-Js!_B-Y*nj@qOrHqs?ur`vx&X;-kYjDLu2nfirQO? z((3ZO@43#`^X=T%eVzOKl7AXr=NZMRU{mi)rm5b_(Ui^Amdn*u$T7U1r*$V!N4`K$ zvFzc!62p6CM)z|bqH>JYbIi1}E%mc(jEYRv%O9y1KGrU>)T?-+S7NSJ^whBOsbQ9# zS(c+^wu^O<-J@zdlR`&}N+~PgZbXLCD*x;McW5uR4QYehd%n3=3!{ylV*ZBffvrLwH9FeH9WC5)~Dd9Uhbw z5uOztkrWe^l$4Z}oRpcFnHv{f5FS)Oz=jmYMpj0KHAaSXLtWM`FSq&8)yl;!0#6=Zi5WEB<`Ru&ajR#ujml{S_Xbd?v^S68>yR5q6u zwb#|u*VQ&OG<0-yv^9ME6dl|bA5Kb&7)ps6PLKJNl{k`>IG&e6D$E)!$e1e0#!i>z zj#U>_*J2kpLGqE*4b1*-3v^2jhvAS}yy1czOzrFtD>&DvY=9jIlt)tzYMVNkd=Oy$E`mOyAkyUb zV$ULtVRrCf;l5ij`A=3ld1)X>8~_kkcI93H)c!aw{1dAMt_5Ixti~^sChD-Bjt#O{ zi|jxB&<-l_O@Pr7=^h4k@|`(*7`Y2 zNql`6q=`#Wc@cG!+@gbeQtCP``uL`*LLR`%f+Ooah3cIDV66o-u{lrABJX`)nt0p) zBJVy!lsIgCbW8w8PVs?3Ih1v3R079hC7yuUUz#8izZc)3by5AZ#5=Hfk$dOgo-+Ae zb{QX_sJR^&-B+9`l)Su!&UQ7n&zR=;;9G9{;K(4hZH5?kZjBGLl{+eBh9TwRXimXa zV$*bSeo|UCQ-Gs7whi!?(vfPw>^_p02j~`Mw3*gDfusW7VukOw?9QR?>dT-s(8uPR zr}0{;?X7-;k?kOl<3EtzwVy1g77l7pG!1fLyQ|rcUMeCUwmAuXPPz zWZT;Lgw6bsIk&k4T`MOpBQeLx;XfmUuO8~lmorFrM%YVQE&!72g_lTfwW zs6B+uPp!LN-p)!z%!lDa!Z-t;xbBsG7M=SgZT~gTF=?kzVT4oP%<;gVHyE zi!-^me1-ddOPOLo(woqI{P+)a9GydcJH6u>nyI|EavXje@^`D6(t>h%t()D;=fVXoHx_f3{my;ylc1qyVA6}cbSyC zbovz$x_oD1g~In;Tuit3qQE&6KXb1g($a~>N&oty^cdJ%8{C^%xh#zB60j9+|MK(t zyz*J-sWVHUb=~zXo5hbp-}_&Afima^SJeG~!m0bQ$DinNWFCRMJw(YrvGg=MVt=(nuJO2uY-c^1sFZ$$~b45SDG3kJb_ zU>sNlEW0KGIrt8o7Xmj5<(PkgkPb%uco)6vNs$MBG7k-j18eWIiTgXT`6G1kE~fJS zW?DD>y+deH!~97h0A&CI^iG})FoYl+oWqx10xpB_rx07Vg;4%71}Ma}hxNu2h{ODQ zL`d4(>zZb#?>rFSGV6R+ZMPPidu4C~toHk-C_BM@y zxy815pS{xGcE%YoMHNCYcQ`Y$B@SAp2x-Tc*(BoOA(MErb=-Y?@Dc>j*T#lEVMTNk z10_MOpved^h_7{7jN?Mo3+WId>&^Buf|j=Ss)9}4BK$}mTPlyh`p0m}McvhkflS4` zTwtJpICMg8Oh9bI%n`e6KGJEiqEt6G(xO3eF)L}2^wQBzE$=%@HiObVCa`*M>{A;&feO5%QsQK~bpP zAOWLrJj?ss_(T{>G-FUg(BtU7AKdAXsOsI9f=lp$N&JuyLT?~pIT>uo?!(^-7i_^! zQn~6WvRheTF4++GoBh<;S%2?atqF0Tq(uLRPto`V2U%c1h5(jk9e{cR9Ie*Rbr2(v zQ~(apmBSxUV|o|iuL5i>0~q8W4&<)!j;ZksMsd*hvCoatXqLG1%l%&x-nbIp@AP|9 zO+o2_Okh2OK0f1?RwlS9S_k4wj&mzYj}kIU;m`z%r&7U9spP83!Pg=waKE&5+r+y< zfS@1H+iH!KDQ74?1?c0z7I`@*tvO#?b50b~RE=EDhH}oRb5GOLkG`jUZp@$#$|CG0 zKzQCPGXrKr@SRrM1VPB^OIRVCvRTB2Is(pEVM8?pFQ&FF@@JrKwIv-yx$h;zH*%c? zGedvHAM;>;QhF7yd(;v=kNODWpcJrRvesDy3>%)kWM8o4m_&|9pnRN9?gP6C1%!v; zjIC}_@8N~Sg0*lgolpLjM&W%(w%|s%Jt5KkXDS^c?XxRRw1WFCG?o@s6mt6Xvj&i> z$RG#BD7Y~>LNUq_D)$bpP@?c@Y6ps8cp=8t>@MQPzUOsN3tTJ0#b+6Ade_EN6wZMi z#&Bg6+l$_qr^fK;#wDc|J>$t+JK(akOlnPs4@s4c&v^#~*b;P^lmoJlUzB?6!E>Ru z!@uCwNPv8@4C<4P%!v2^b;yTixKmNO5sC$-D*u{gyR*igmIhq3mM2DVCug{z-QfNx z2jdEc{(wSV>C&S|r3+gXqsgViQrP^`C3rQaf(eupml6U8m3(7CM2gy^>cZXGO zj^@f)LH7PgyJQ_ea2K$|Q5l&r72~L$=7DXIkhMejRGSU|GTgk}KO+Ot1*W7Y0ubOS zH4(6YQ-&DUNSr}~Pr-(oMfJiAL*8}Qy4N|NO0b_4bjFgAh=lm%VZl!^RNa=Y{_B7M zUlXjS{*VU<){rA= zH~SjNRTF>yuDTgSRgwn0tzaz71BQ2tInU}21)8C%6+hV+s@H+G3Tuiu@NF6fKOB(C zo=%U7$-2Q%JPBYc@xV+U<3JwRaM{ZEC@t|#goSrQ@xCX-4Ke9gDb>?(xYMBap~82$ zqP-lRIfMZd83vGfV|}@BqXvm9xLvz-VLJRWB9#x6Un~VB{%J4FYeYE#L?vjqSVxaL zqAHPbovpBAU$WdWSd<6l)29COcmH&4NIEd=oo4hk6 z##pGs&IsHxuKP}#Z4n2Pp_0w9n2M=b&3r(L=vicCcm&f_EK07D#;1Kbxs(*)cTAPH zi+$bd>;xz&A)W;|!Kz@v#9(H3d{T1~W7P}hzwoq$f;~kBI#gzTC5-HDEn_^m?z0sY zs;McmiS#EI;!}ojf#azvZpT-nSjx0j`yW&?m3*!)bN8kLx%!DQh<4a|9>LvVw!?Re zlT!1k5ua{+D7wxN!Z4`zAy|QjqaAVBequwrH4eqFu4IF#gh#@BRgdAtab30V`&bY# zx7SX?irwry0by1{5`U{6bGTZsq+LV|x#N4pZ9n-}wW{#6u{Bkre)LQ+_D&F7#61`u z>3^d483ykTv*-r!uM9lWVNm+}CRo2R`|ooFj}HW8cs1@VNx3tkjauAP#1RAV>Q1ZCq=hOcF@_QSHwF*jE->F4vBpEYJ~8Z8!PBK$}4EX1c7vPb!h=93miX;d3gn!{9}LAak+ z9blW}3AeIHhqDZ6)7a8sCg6S~@Npxwv=+KCb?X%-fD17|{OmYTbF-`iDuHMw$}Q`2&tYWr}d-j89pp-;E!K@nTa@j?Nq+sYhMX(U@zZN z{zNN1^u`KNGKSfsoH%^MV3iKsUA0561XD7e$ zlVu~~cl@6Y8>j@F>bh{)aBfubGu<1{hf}Ldm#g=9mX@)Oo1-(cbondr-A>-1rO(L| zOlR}TRRbB$%}=-({$y9drq)IS$7rOt0W+w^Xdh3fEj8}T=R4Ens#}TG>zg~D!5W{>rRpQ@}5j<)k|EW`f|mDiTcyR2c{ zP4Wu+?}ih4Rp7}6Z(6GNL{njDDoJkKHT-KC;`z&WzfRoXoUBq<;}EQw>vRLb51Dv& zzJXvHlXz>jSFNj28>>_?9>522+)+-EEG*_*9id+jlM6;(wNwEHGe z#~k^~%U#FI8Yg5M$HlZK>x=lnckqpSHfq_mKn?ynCY{!2eM$3cllJu-vDzQr4Now@ zzCVfCn5V53-TT6pG3;%z&TYGs0%i8~nr-Vk=-_sy-`L;yeUjbcCMWs2a(62T_L3oY z|M>m#lXvG43vGxG=eJ+m-dICe1z@gqcGzrejGulDsUomGgMWV)Qd+aT<8-Y1)Pc2i zbr|=pLF-Zk)Gl|6Ni-&;N%{29eu;MD9DBQBiTUhc^W_oqhUd(35*abMe(6HZ;Ea;z z=dD*425x~vT?i}1=Ug>50pee(#Nf71&*7lGZmpl=RH?lWR(#8L=xVLIMSo>bZ9z6R zY3P^(zF3`gF&5=o*Vih)n}93PJJ8}z(}t&R1Z^#Ye-7JpbicFZxgJiw@LcGpy2O&7 zeccouVaGmmtho#ohNt~IS)vEtu>Br${ObHHFzMlbmdFZumrV>^)0SJ{Hsnvz;~$r_ zu0LXbf?}B(zFBqVDg(AuL{u=CmYXwAe?u*tHUzA|W>i1|EN|G5(&9I81TJdI88+iB zP%O6=!L%%IyN9gDj5{#Qh&U1~jWt?>Sw!rP6Ub!b*4cfXS+bxnp|rXU`_tjK1Go)a zaJ%I37A~7cgGMfC#Z__dPSW6e(shn{yymZAyvF1qH~}RysIalB#-j}D0P!vIj<@ud z4mVmhkabA#O-*qH*h6vx*L|-=(K4ICWQnK!Sr*@&#mY652qeV;x9J@88}1-PLSxl7P18?fu$`LQHByw9ko85k<-kXYMl^mr^ATu|H~z zp4#iTXx(YuoDYuneSJ1XVqYnZ@?I3K-rCT~BW5gZs?xVH!{n&Rub)i_4P=%YsX zw+J%itPXXmR5wGV)To7uuytxQ58ZDG=SI91=p@$-5@)XqY}dbEL{cZnX9V&^3GGQH zd=uDI77GJ7o;67KXM1nQsr`!eNuVkw2J@%8i4d1EUEEKZ3-W~vG_ABX|T zJmocN=v!<%l_IOwV=AU&^pG{y5h`vl*1OdwYx3asw(G;B$tVL5hV#XNSefL=*UE!a zU!!yf%4oXK8V0_VkHhblAkKJ@AqyLI?o3{>`Lm+eXXvY~8c zT!x8B6kYLwmVR-Ot91~oQ;Y5Uo8by}1g))EgOcE1W@SLq-D9Jo@vtaNz5<<&h8Xj5 zilB@0j_d5qkJWe)jNhH(0)+r5$~G!%_2K-u%r$q}q`w}vK$K6y#f`xwxT(W+|5T>J zU#R3jJQA0|(PkkWryiUxm?^uNE|{P<{(biEDDV1FxG|k5YVvS(X!N~_ggGX(G)v)G z=g-~tXWjp$NFAnnGY>7F!~7rD)$Ne+=(54YGE#QD z`}1w!KK9>#ejWV1d{2#m;P(u6XQ8j-T#f!*`!OboVUyj3uIg=cQdl##lO1g2eYlN* zLWF7{`1NTJEm@#h#<&Gox<4tlixreQzE*rr`>Os}6o14XYcr z9BbxbsnO-nZaL;Z)Lwj_v=&5ZsvJO1TBj0df8d0DOtNEXY|)TRD8X_cEEs2`3jJK= z4^k5;#MTbN{-j8UCu$Vf#=_!D8icLE4StvC1$mTQi4} zfnLT9hgY|L@o2(+qT)Fd1X)M%Gydm2k+^X?c?-UWnrZP`Zi9jPK^zYmO5=6DlEO2l zn9pwbMj6c-!y7sKBs>XvR);dFT~#|=eAjBgQ|w`VTE~i`%nOT=$8tqXBc{93g@jR~ zKxy9QBthB6B|WeC^CW8uXN5jRgXea`8S8iTrCeHaWy80#74!oSFvg@jILei%6Y~$BYnJK*wb~6lXOMj)w zAIl5s*NC0zFVK%KOr<{1Qy7XfE_lK7C>b&&*OuVqrCU6SnC;&qg%O{o(h;W z8HT?^N<>F2u$?D=bRre%LI@dx>Qx? z*qM8Tk?TfTiP8glgIh8dqnf3q>R?Y74W;FL7G@p-h|;<61u?F{*fCAS#W2lnB0E&N ziOq5FsY-q8bl@Z2+%bORbtt-sbX(|M+YO#)eQ}=)ysf8~O*D+i@U@&5EjMVWv9J)C zxp4n;5&2fJhiz@}RX>NO_y3@1W*QbgNp#|4_GtB|#>%r+O&>?6s_ehH-&{|a@BR<$ zjr;X8xv*fHO|M}BMLm3Tlp;y!*8@WMuCVU;^r2&wMFd7k?MsxxmB8YNwwh}JW@pWW z$01+GVQ(*v){wS-nfr52tjm7M{py26aR6q<$IxlzM9@Hc`*mqUvqf z1P}H17$b%~$y>!^2t^Sa#!m+!I0e40WC%g&J1M(L@y?HI85?8=e6GLE&9eVx&CAXs zuum)UnI=r?9&HMUpOvo;h_vL{ItngJO=57u=jct>TD~HU7ph*mPJB=1`6w=2*ny9E zym`!kea<7FqOE745+8DnmAosg8y-)Yy1BaG6K29G?MMAb1TAN%rxibs21H*FUj&h$K=Bbhs1Vv0C*le15p;zSe0(CY+ z#dkJ0t{nMixPM(O-}7TEe#pK;YvhkccG-Z}&;`}pwB@t~J~OD!Ef;a!g)i@95*Aj! zzG`kCx=dnMT4@jGb$uNqx%EAj$Z_o1j;|&6`;Onv3T7LxGz_mq2SlPtEP;ko?3+JA z_+>h{6>sk;*ChNZ*QLGJmV609NRcs4*fn*)`{fo8eDSSJREmP^?@<` zTX`zu9wudTl{WC^Jh?ki?1*c}b%^XZzV}C19@A~+r$ZhtZ!RLs!eZnWO7E-KF!9eY zCTsJ&QkR_A9936pW_+w|B*GO)kbP|xXdTh;SWV%#*7m5dPX#tPts`8aD|qsr968Ob zQunYr^{UCoDF|wXtcy&XW|RCrlyrM{hDf_jeE$62I1qmRi3V&1=}v@RyPq^5mjoFsijq3_@cH`BF%oBKEPC*g0c(quD6@rCxKGcMra<=2{6dZGzu@ zO}Hl-K`f+h$Ns*Ca+!w9eusQDrW8CD|6@YQDT@!Jq5jMVdGrZFWktQV1(Udd@K)V| z{e>R7Ls}C*g$%aQ&3;nZ{{P)b9LIolGVjILsT0_03-GE0HbHlU!~L5^5i)9*-xb#;Nqg zb;YpVlBNKhv#Y9nQA(355`<* zQ~pw)ybAV=y8Cq>JyJH1jZv`LCPX2X{o6Fx(n)4L1T|TR6ei+QNzLGIggR4$W>mA_ zi%-Ew>A+)%RNzpI)SdXj!5~y8W^|xf8#@$Cf|NxyUSz7^EVL<^sPBkV>(53#RnUn* z_fti-*LM`fnvOI&jM(tiO}T`XC`0O2`htkV#Jb^_LG@=1y~^o56Jp zHVS#E+`*diu zPTz5qHhpOT{*e_Dr1i9_mo4V zfx$*7aC4^kpB?DwGCjatXSz(=PPYHwtR8S@noq;nas2^AJW|6H(u<9P2svIug;ns{ z35wbu+5~>-O}H4mLql(@R^~y*|3r?1%f}=0rc(_Y;nBl!Y~x>`uuO5ttkPr%FcVca z6TIHr5m4kHz zDp~G7fhGxL&!Y#P_Tht9%P%@a!};gaW!lx4qu(9NzTYqjHrLRl*1|C_=W9&A52Sui zOREPP{Qegk0bETLpypzP4Gb@1;_#MrdFJC%a|Llz(f_hGhbMys?&ce&<$%SV6KdiV zX8Xh=tmp8GFvHYNyfrhft?_ev-{+3G&z+rmGq4C%0hx?Wi#h>XJusl($@CG7%S~pQ zR8}YmTN&&dYg-u{Wn03>S;#RP4NO|a?mxLS9m2*?NwEr7X)gEfVIKLK0T? zlWk&-#|h@cljd{y!CPwrcxjECdUGR+z}2|LN9^f3FaFr%x2#2J_p*9CTD2C-H`giN zp~jXUExM`_#vhT2>sB3-WQLv)Pud7pTx(lgcgR_+vuJ7iR3K(9Y<(KbI@HSt#p}>r zlwP0yI0h3e{NVRaKPk^Jn7I0hUyYqokH8&Z(7;?4r z$5V|(BjR)plE^`&+;()mHK7#YTm2Bdh*sCQ&mr zn%zywu6lca%W(K+h@tt^A_g^9Q$P4n*nlzb&%MS*dL}1>t3G_=9a8bSjflX^e6ejl z&QPg8TdGt+AOuXriJjg+N^i?}W0#zkUV zN(t|jZ3!W^nC{6uq%~`R?uB#`X5&wq+E)QpE;;;_72ZbFy9jRQ?~qO^NYaB zG|@45|54Em;HF^o!+9o&dw2uCy{!3zPIih@tIaUB15;+06dae^g{9WMhkrTWetT5s z}Z=LYRTpH&u=iHq-?^#I42<~yNRZWdWypXajse$Ho(5< zH?hcfo?&o7{JYC1*bH2lu6S(l+Rk-IO+>AfTcGae+4e&_7AS}1VKBrXP7?BIW$uo_ zaSYoG+-5J3a17gB+jN9rkiZysL3g(MotF>6EVK^fvRUJJhjrSQ2zbUjo0o#ehfQ7& zsKZTu;0Nwej*g!=cTIi5Z6xeBJ?Jt$b#u)X_%t@YlLj3c9Cg*qpUd$Jn&d z(%x4-p{YZ=hg9pl1G!i<9mVH;Go^@xaepI}O~j?qkY9b>;5JLz9i}kYpFz11gbyQI z(@TFubⓈ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�hzIEzI6Z~tJ>Uj>Be6qjLnaY}Ex>Pwe;=0mG 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 Date: Mon, 2 Jan 2017 16:37:29 +0100 Subject: [PATCH 65/71] :memo: updated online link --- README.md | 2 +- doc/examples/README.link | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a0534615..498d1bbe 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/8soFCqS532vOyZcK) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/IoZNMHqubixQx2dN) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) diff --git a/doc/examples/README.link b/doc/examples/README.link index 128ab1dc..58daac88 100644 --- a/doc/examples/README.link +++ b/doc/examples/README.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file From 245a69d554425407a97374c133445f54d7d05952 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 3 Jan 2017 21:33:23 +0100 Subject: [PATCH 66/71] :lipstick: ran clang-tidy --- src/json.hpp | 218 +++++++++++++-------------------- src/json.hpp.re2c | 218 +++++++++++++-------------------- test/src/unit-allocator.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- 4 files changed, 174 insertions(+), 266 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 9d48e7a6..4e043ad5 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -128,7 +128,7 @@ struct has_mapped_type std::is_integral()))>::value; }; -} +} // namespace /*! @brief a class to store JSON values @@ -767,7 +767,7 @@ class basic_json }; std::unique_ptr object(alloc.allocate(1), deleter); alloc.construct(object.get(), std::forward(args)...); - assert(object.get() != nullptr); + assert(object != nullptr); return object.release(); } @@ -2583,29 +2583,25 @@ class basic_json template::value and std::is_convertible::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_object()) { return T(m_value.object->begin(), m_value.object->end()); } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } + + throw std::domain_error("type must be object, but is " + type_name()); } /// get an object (explicit) - object_t get_impl(object_t*) const + object_t get_impl(object_t* /*unused*/) const { if (is_object()) { return *(m_value.object); } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } + + throw std::domain_error("type must be object, but is " + type_name()); } /// get an array (explicit) @@ -2615,7 +2611,7 @@ class basic_json not std::is_arithmetic::value and not std::is_convertible::value and not has_mapped_type::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_array()) { @@ -2627,17 +2623,15 @@ class basic_json }); return to_vector; } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get an array (explicit) template::value and not std::is_same::value, int>::type = 0> - std::vector get_impl(std::vector*) const + std::vector get_impl(std::vector* /*unused*/) const { if (is_array()) { @@ -2650,60 +2644,52 @@ class basic_json }); return to_vector; } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get an array (explicit) template::value and not has_mapped_type::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_array()) { return T(m_value.array->begin(), m_value.array->end()); } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get an array (explicit) - array_t get_impl(array_t*) const + array_t get_impl(array_t* /*unused*/) const { if (is_array()) { return *(m_value.array); } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get a string (explicit) template::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_string()) { return *m_value.string; } - else - { - throw std::domain_error("type must be string, but is " + type_name()); - } + + throw std::domain_error("type must be string, but is " + type_name()); } /// get a number (explicit) template::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { switch (m_type) { @@ -2730,7 +2716,7 @@ class basic_json } /// get a boolean (explicit) - constexpr boolean_t get_impl(boolean_t*) const + constexpr boolean_t get_impl(boolean_t* /*unused*/) const { return is_boolean() ? m_value.boolean @@ -2738,85 +2724,85 @@ class basic_json } /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t*) noexcept + object_t* get_impl_ptr(object_t* /*unused*/) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t*) const noexcept + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t*) noexcept + array_t* get_impl_ptr(array_t* /*unused*/) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t*) const noexcept + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t*) noexcept + string_t* get_impl_ptr(string_t* /*unused*/) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t*) const noexcept + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t*) noexcept + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t*) noexcept + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t*) noexcept + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } @@ -2845,11 +2831,9 @@ class basic_json { return *ptr; } - else - { - throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name()); - } + + throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + + obj.type_name()); } public: @@ -3349,10 +3333,8 @@ class basic_json return m_value.array->operator[](idx); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3381,10 +3363,8 @@ class basic_json { return m_value.array->operator[](idx); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3429,10 +3409,8 @@ class basic_json { return m_value.object->operator[](key); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3473,10 +3451,8 @@ class basic_json assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3590,10 +3566,8 @@ class basic_json { return m_value.object->operator[](key); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3635,10 +3609,8 @@ class basic_json assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3702,10 +3674,8 @@ class basic_json { return *it; } - else - { - return default_value; - } + + return default_value; } else { @@ -4131,10 +4101,8 @@ class basic_json { return m_value.object->erase(key); } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } + + throw std::domain_error("cannot use erase() with " + type_name()); } /*! @@ -5186,10 +5154,8 @@ class basic_json result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } + + throw std::domain_error("cannot use insert() with " + type_name()); } /*! @@ -5241,10 +5207,8 @@ class basic_json result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } + + throw std::domain_error("cannot use insert() with " + type_name()); } /*! @@ -6122,7 +6086,7 @@ class basic_json { // assertion to check that the iterator range is indeed contiguous, // see http://stackoverflow.com/a/35008842/266378 for more discussion - assert(std::accumulate(first, last, std::make_pair(true, 0), + assert(std::accumulate(first, last, std::pair(true, 0), [&first](std::pair res, decltype(*first) val) { res.first &= (val == *(std::next(std::addressof(*first), res.second++))); @@ -6327,7 +6291,7 @@ class basic_json } T result; - uint8_t* ptr = reinterpret_cast(&result); + auto* ptr = reinterpret_cast(&result); for (size_t i = 0; i < sizeof(T); ++i) { *ptr++ = vec[current_index + sizeof(T) - i]; @@ -6473,7 +6437,7 @@ class basic_json { // float 64 v.push_back(0xcb); - const uint8_t* helper = reinterpret_cast(&(j.m_value.number_float)); + const auto* helper = reinterpret_cast(&(j.m_value.number_float)); for (size_t i = 0; i < 8; ++i) { v.push_back(helper[7 - i]); @@ -6716,7 +6680,7 @@ class basic_json { // Double-Precision Float v.push_back(0xfb); - const uint8_t* helper = reinterpret_cast(&(j.m_value.number_float)); + const auto* helper = reinterpret_cast(&(j.m_value.number_float)); for (size_t i = 0; i < 8; ++i) { v.push_back(helper[7 - i]); @@ -6916,7 +6880,7 @@ class basic_json { return v[current_idx]; } - else if (v[current_idx] <= 0x8f) // fixmap + if (v[current_idx] <= 0x8f) // fixmap { basic_json result = value_t::object; const size_t len = v[current_idx] & 0x0f; @@ -7577,7 +7541,7 @@ class basic_json { val = mant == 0 ? INFINITY : NAN; } - return half & 0x8000 ? -val : val; + return (half & 0x8000) != 0 ? -val : val; } case 0xfa: // Single-Precision Float (four-byte IEEE 754) @@ -7800,10 +7764,8 @@ class basic_json // from c (1 byte) to \uxxxx (6 bytes) return res + 5; } - else - { - return res; - } + + return res; } } }); @@ -8509,10 +8471,8 @@ class basic_json { return *m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + throw std::out_of_range("cannot get value"); } } } @@ -8545,10 +8505,8 @@ class basic_json { return m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + throw std::out_of_range("cannot get value"); } } } @@ -8861,10 +8819,8 @@ class basic_json { return *m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + throw std::out_of_range("cannot get value"); } } } @@ -8881,10 +8837,8 @@ class basic_json { return m_it.object_iterator->first; } - else - { - throw std::domain_error("cannot use key() for non-object iterators"); - } + + throw std::domain_error("cannot use key() for non-object iterators"); } /*! @@ -10209,7 +10163,7 @@ basic_json_parser_66: assert(m_marker == nullptr or m_marker <= m_limit); // number of processed characters (p) - const size_t num_processed_chars = static_cast(m_start - m_content); + const auto num_processed_chars = static_cast(m_start - m_content); // offset for m_marker wrt. to m_start const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; // number of unprocessed characters (u) @@ -10601,7 +10555,7 @@ basic_json_parser_66: else { // parse with strtod - result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + result.m_value.number_float = str_to_float_t(static_cast(nullptr), nullptr); // replace infinity and NAN by null if (not std::isfinite(result.m_value.number_float)) @@ -11336,7 +11290,7 @@ basic_json_parser_66: // - start: position after the previous slash for ( // search for the first slash after the first character - size_t slash = reference_string.find_first_of("/", 1), + size_t slash = reference_string.find_first_of('/', 1), // set the beginning of the first reference token start = 1; // we can stop if start == string::npos+1 = 0 @@ -11345,16 +11299,16 @@ basic_json_parser_66: // (will eventually be 0 if slash == std::string::npos) start = slash + 1, // find next slash - slash = reference_string.find_first_of("/", start)) + slash = reference_string.find_first_of('/', start)) { // use the text between the beginning of the reference token // (start) and the last slash (slash). auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of("~"); + for (size_t pos = reference_token.find_first_of('~'); pos != std::string::npos; - pos = reference_token.find_first_of("~", pos + 1)) + pos = reference_token.find_first_of('~', pos + 1)) { assert(reference_token[pos] == '~'); @@ -12188,7 +12142,7 @@ uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; -} +} // namespace nlohmann /////////////////////// @@ -12229,7 +12183,7 @@ struct hash return h(j.dump()); } }; -} +} // namespace std /*! @brief user-defined string literal for JSON values diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index e1a43b54..a64762b9 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -128,7 +128,7 @@ struct has_mapped_type std::is_integral()))>::value; }; -} +} // namespace /*! @brief a class to store JSON values @@ -767,7 +767,7 @@ class basic_json }; std::unique_ptr object(alloc.allocate(1), deleter); alloc.construct(object.get(), std::forward(args)...); - assert(object.get() != nullptr); + assert(object != nullptr); return object.release(); } @@ -2583,29 +2583,25 @@ class basic_json template::value and std::is_convertible::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_object()) { return T(m_value.object->begin(), m_value.object->end()); } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } + + throw std::domain_error("type must be object, but is " + type_name()); } /// get an object (explicit) - object_t get_impl(object_t*) const + object_t get_impl(object_t* /*unused*/) const { if (is_object()) { return *(m_value.object); } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } + + throw std::domain_error("type must be object, but is " + type_name()); } /// get an array (explicit) @@ -2615,7 +2611,7 @@ class basic_json not std::is_arithmetic::value and not std::is_convertible::value and not has_mapped_type::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_array()) { @@ -2627,17 +2623,15 @@ class basic_json }); return to_vector; } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get an array (explicit) template::value and not std::is_same::value, int>::type = 0> - std::vector get_impl(std::vector*) const + std::vector get_impl(std::vector* /*unused*/) const { if (is_array()) { @@ -2650,60 +2644,52 @@ class basic_json }); return to_vector; } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get an array (explicit) template::value and not has_mapped_type::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_array()) { return T(m_value.array->begin(), m_value.array->end()); } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get an array (explicit) - array_t get_impl(array_t*) const + array_t get_impl(array_t* /*unused*/) const { if (is_array()) { return *(m_value.array); } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } + + throw std::domain_error("type must be array, but is " + type_name()); } /// get a string (explicit) template::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { if (is_string()) { return *m_value.string; } - else - { - throw std::domain_error("type must be string, but is " + type_name()); - } + + throw std::domain_error("type must be string, but is " + type_name()); } /// get a number (explicit) template::value, int>::type = 0> - T get_impl(T*) const + T get_impl(T* /*unused*/) const { switch (m_type) { @@ -2730,7 +2716,7 @@ class basic_json } /// get a boolean (explicit) - constexpr boolean_t get_impl(boolean_t*) const + constexpr boolean_t get_impl(boolean_t* /*unused*/) const { return is_boolean() ? m_value.boolean @@ -2738,85 +2724,85 @@ class basic_json } /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t*) noexcept + object_t* get_impl_ptr(object_t* /*unused*/) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t*) const noexcept + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t*) noexcept + array_t* get_impl_ptr(array_t* /*unused*/) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t*) const noexcept + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t*) noexcept + string_t* get_impl_ptr(string_t* /*unused*/) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t*) const noexcept + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t*) noexcept + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t*) noexcept + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { return is_number_unsigned() ? &m_value.number_unsigned : nullptr; } /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t*) noexcept + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } @@ -2845,11 +2831,9 @@ class basic_json { return *ptr; } - else - { - throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name()); - } + + throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + + obj.type_name()); } public: @@ -3349,10 +3333,8 @@ class basic_json return m_value.array->operator[](idx); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3381,10 +3363,8 @@ class basic_json { return m_value.array->operator[](idx); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3429,10 +3409,8 @@ class basic_json { return m_value.object->operator[](key); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3473,10 +3451,8 @@ class basic_json assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3590,10 +3566,8 @@ class basic_json { return m_value.object->operator[](key); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3635,10 +3609,8 @@ class basic_json assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + throw std::domain_error("cannot use operator[] with " + type_name()); } /*! @@ -3702,10 +3674,8 @@ class basic_json { return *it; } - else - { - return default_value; - } + + return default_value; } else { @@ -4131,10 +4101,8 @@ class basic_json { return m_value.object->erase(key); } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } + + throw std::domain_error("cannot use erase() with " + type_name()); } /*! @@ -5186,10 +5154,8 @@ class basic_json result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } + + throw std::domain_error("cannot use insert() with " + type_name()); } /*! @@ -5241,10 +5207,8 @@ class basic_json result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } + + throw std::domain_error("cannot use insert() with " + type_name()); } /*! @@ -6122,7 +6086,7 @@ class basic_json { // assertion to check that the iterator range is indeed contiguous, // see http://stackoverflow.com/a/35008842/266378 for more discussion - assert(std::accumulate(first, last, std::make_pair(true, 0), + assert(std::accumulate(first, last, std::pair(true, 0), [&first](std::pair res, decltype(*first) val) { res.first &= (val == *(std::next(std::addressof(*first), res.second++))); @@ -6327,7 +6291,7 @@ class basic_json } T result; - uint8_t* ptr = reinterpret_cast(&result); + auto* ptr = reinterpret_cast(&result); for (size_t i = 0; i < sizeof(T); ++i) { *ptr++ = vec[current_index + sizeof(T) - i]; @@ -6473,7 +6437,7 @@ class basic_json { // float 64 v.push_back(0xcb); - const uint8_t* helper = reinterpret_cast(&(j.m_value.number_float)); + const auto* helper = reinterpret_cast(&(j.m_value.number_float)); for (size_t i = 0; i < 8; ++i) { v.push_back(helper[7 - i]); @@ -6716,7 +6680,7 @@ class basic_json { // Double-Precision Float v.push_back(0xfb); - const uint8_t* helper = reinterpret_cast(&(j.m_value.number_float)); + const auto* helper = reinterpret_cast(&(j.m_value.number_float)); for (size_t i = 0; i < 8; ++i) { v.push_back(helper[7 - i]); @@ -6916,7 +6880,7 @@ class basic_json { return v[current_idx]; } - else if (v[current_idx] <= 0x8f) // fixmap + if (v[current_idx] <= 0x8f) // fixmap { basic_json result = value_t::object; const size_t len = v[current_idx] & 0x0f; @@ -7577,7 +7541,7 @@ class basic_json { val = mant == 0 ? INFINITY : NAN; } - return half & 0x8000 ? -val : val; + return (half & 0x8000) != 0 ? -val : val; } case 0xfa: // Single-Precision Float (four-byte IEEE 754) @@ -7800,10 +7764,8 @@ class basic_json // from c (1 byte) to \uxxxx (6 bytes) return res + 5; } - else - { - return res; - } + + return res; } } }); @@ -8509,10 +8471,8 @@ class basic_json { return *m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + throw std::out_of_range("cannot get value"); } } } @@ -8545,10 +8505,8 @@ class basic_json { return m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + throw std::out_of_range("cannot get value"); } } } @@ -8861,10 +8819,8 @@ class basic_json { return *m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + throw std::out_of_range("cannot get value"); } } } @@ -8881,10 +8837,8 @@ class basic_json { return m_it.object_iterator->first; } - else - { - throw std::domain_error("cannot use key() for non-object iterators"); - } + + throw std::domain_error("cannot use key() for non-object iterators"); } /*! @@ -9359,7 +9313,7 @@ class basic_json assert(m_marker == nullptr or m_marker <= m_limit); // number of processed characters (p) - const size_t num_processed_chars = static_cast(m_start - m_content); + const auto num_processed_chars = static_cast(m_start - m_content); // offset for m_marker wrt. to m_start const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; // number of unprocessed characters (u) @@ -9751,7 +9705,7 @@ class basic_json else { // parse with strtod - result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + result.m_value.number_float = str_to_float_t(static_cast(nullptr), nullptr); // replace infinity and NAN by null if (not std::isfinite(result.m_value.number_float)) @@ -10486,7 +10440,7 @@ class basic_json // - start: position after the previous slash for ( // search for the first slash after the first character - size_t slash = reference_string.find_first_of("/", 1), + size_t slash = reference_string.find_first_of('/', 1), // set the beginning of the first reference token start = 1; // we can stop if start == string::npos+1 = 0 @@ -10495,16 +10449,16 @@ class basic_json // (will eventually be 0 if slash == std::string::npos) start = slash + 1, // find next slash - slash = reference_string.find_first_of("/", start)) + slash = reference_string.find_first_of('/', start)) { // use the text between the beginning of the reference token // (start) and the last slash (slash). auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of("~"); + for (size_t pos = reference_token.find_first_of('~'); pos != std::string::npos; - pos = reference_token.find_first_of("~", pos + 1)) + pos = reference_token.find_first_of('~', pos + 1)) { assert(reference_token[pos] == '~'); @@ -11338,7 +11292,7 @@ uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; -} +} // namespace nlohmann /////////////////////// @@ -11379,7 +11333,7 @@ struct hash return h(j.dump()); } }; -} +} // namespace std /*! @brief user-defined string literal for JSON values diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index f11d8538..76f9a16b 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -80,7 +80,7 @@ struct my_allocator : std::allocator } else { - ::new(reinterpret_cast(p)) T(std::forward(args)...); + ::new (reinterpret_cast(p)) T(std::forward(args)...); } } diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 6bfb4402..d03a5f40 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -912,7 +912,7 @@ TEST_CASE("constructors") SECTION("array") { - json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }; CHECK(j.type() == json::value_t::array); } } From f8d640b185c4b34bfb651250eb3b7113caf0317b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 3 Jan 2017 22:35:31 +0100 Subject: [PATCH 67/71] :construction_worker: better support for OSS-Fuzz --- test/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/Makefile b/test/Makefile index 68520bd3..678276ba 100644 --- a/test/Makefile +++ b/test/Makefile @@ -51,7 +51,7 @@ TESTCASES = $(patsubst src/unit-%.cpp,test-%,$(wildcard src/unit-*.cpp)) all: $(TESTCASES) clean: - rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) $(TESTCASES) + rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) $(TESTCASES) parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer ############################################################################## # single test file @@ -84,13 +84,14 @@ check: $(TESTCASES) # fuzzer ############################################################################## +FUZZER_ENGINE = src/fuzzer-driver_afl.cpp fuzzers: parse_afl_fuzzer parse_cbor_fuzzer parse_msgpack_fuzzer parse_afl_fuzzer: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_json.cpp -o $@ + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_json.cpp -o $@ parse_cbor_fuzzer: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_cbor.cpp -o $@ + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_cbor.cpp -o $@ parse_msgpack_fuzzer: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) src/fuzzer-driver_afl.cpp src/fuzzer-parse_msgpack.cpp -o $@ + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(FUZZER_ENGINE) src/fuzzer-parse_msgpack.cpp -o $@ From 767637877b6004ed16a0474ed3e3aa022fb493da Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 3 Jan 2017 22:37:07 +0100 Subject: [PATCH 68/71] :lipstick: cleanup --- src/json.hpp | 52 ++++++++++++++++++++++++----------------------- src/json.hpp.re2c | 52 ++++++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 4e043ad5..f3b77a41 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -153,7 +153,8 @@ default) @requirement The class satisfies the following concept requirements: - Basic - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null value. + JSON values can be default constructed. The result will be a JSON null + value. - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): @@ -168,8 +169,8 @@ default) - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): JSON values have [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the class - has no virtual functions or (virtual) base classes. + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. - Library-wide - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): JSON values can be compared with `==`, see @ref @@ -1894,13 +1895,15 @@ class basic_json case value_t::object: { - m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); break; } case value_t::array: { - m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); break; } @@ -3084,7 +3087,7 @@ class basic_json template < typename ValueType, typename std::enable_if < not std::is_pointer::value and not std::is_same::value -#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 +#ifndef _MSC_VER // fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif , int >::type = 0 > @@ -3750,10 +3753,8 @@ class basic_json return default_value; } } - else - { - throw std::domain_error("cannot use value() with " + type_name()); - } + + throw std::domain_error("cannot use value() with " + type_name()); } /*! @@ -5065,8 +5066,8 @@ class basic_json /*! @brief add an object to an object if key does not exist - Inserts a new element into a JSON object constructed in-place with the given - @a args if there is no element with the key in the container. If the + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the function is called on a JSON null value, an empty object is created before appending the value created from @a args. @@ -5131,8 +5132,8 @@ class basic_json @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` - @complexity Constant plus linear in the distance between pos and end of the - container. + @complexity Constant plus linear in the distance between pos and end of + the container. @liveexample{The example shows how `insert()` is used.,insert} @@ -5358,8 +5359,8 @@ class basic_json @param[in,out] other array to exchange the contents with - @throw std::domain_error when JSON value is not an array; example: `"cannot - use swap() with string"` + @throw std::domain_error when JSON value is not an array; example: + `"cannot use swap() with string"` @complexity Constant. @@ -6332,8 +6333,9 @@ class basic_json if (j.m_value.number_integer >= 0) { // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we used - // the code from the value_t::number_unsigned case here. + // signed integers and unsigned integers. Therefore, we + // used the code from the value_t::number_unsigned case + // here. if (j.m_value.number_unsigned < 128) { // positive fixnum @@ -6608,8 +6610,8 @@ class basic_json } else { - // The conversions below encode the sign in the first byte, - // and the value is converted to a positive number. + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. const auto positive_number = -1 - j.m_value.number_integer; if (j.m_value.number_integer >= -24) { @@ -6814,12 +6816,12 @@ class basic_json To secure the access to the byte vector during CBOR/MessagePack deserialization, bytes are copied from the vector into buffers. This - function checks if the number of bytes to copy (@a len) does not exceed the - size @s size of the vector. Additionally, an @a offset is given from where - to start reading the bytes. + function checks if the number of bytes to copy (@a len) does not exceed + the size @s size of the vector. Additionally, an @a offset is given from + where to start reading the bytes. - This function checks whether reading the bytes is safe; that is, offset is a - valid index in the vector, offset+len + This function checks whether reading the bytes is safe; that is, offset is + a valid index in the vector, offset+len @param[in] size size of the byte vector @param[in] len number of bytes to read diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index a64762b9..42459f4e 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -153,7 +153,8 @@ default) @requirement The class satisfies the following concept requirements: - Basic - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null value. + JSON values can be default constructed. The result will be a JSON null + value. - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): @@ -168,8 +169,8 @@ default) - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): JSON values have [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the class - has no virtual functions or (virtual) base classes. + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. - Library-wide - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): JSON values can be compared with `==`, see @ref @@ -1894,13 +1895,15 @@ class basic_json case value_t::object: { - m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); break; } case value_t::array: { - m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); break; } @@ -3084,7 +3087,7 @@ class basic_json template < typename ValueType, typename std::enable_if < not std::is_pointer::value and not std::is_same::value -#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 +#ifndef _MSC_VER // fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif , int >::type = 0 > @@ -3750,10 +3753,8 @@ class basic_json return default_value; } } - else - { - throw std::domain_error("cannot use value() with " + type_name()); - } + + throw std::domain_error("cannot use value() with " + type_name()); } /*! @@ -5065,8 +5066,8 @@ class basic_json /*! @brief add an object to an object if key does not exist - Inserts a new element into a JSON object constructed in-place with the given - @a args if there is no element with the key in the container. If the + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the function is called on a JSON null value, an empty object is created before appending the value created from @a args. @@ -5131,8 +5132,8 @@ class basic_json @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` - @complexity Constant plus linear in the distance between pos and end of the - container. + @complexity Constant plus linear in the distance between pos and end of + the container. @liveexample{The example shows how `insert()` is used.,insert} @@ -5358,8 +5359,8 @@ class basic_json @param[in,out] other array to exchange the contents with - @throw std::domain_error when JSON value is not an array; example: `"cannot - use swap() with string"` + @throw std::domain_error when JSON value is not an array; example: + `"cannot use swap() with string"` @complexity Constant. @@ -6332,8 +6333,9 @@ class basic_json if (j.m_value.number_integer >= 0) { // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we used - // the code from the value_t::number_unsigned case here. + // signed integers and unsigned integers. Therefore, we + // used the code from the value_t::number_unsigned case + // here. if (j.m_value.number_unsigned < 128) { // positive fixnum @@ -6608,8 +6610,8 @@ class basic_json } else { - // The conversions below encode the sign in the first byte, - // and the value is converted to a positive number. + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. const auto positive_number = -1 - j.m_value.number_integer; if (j.m_value.number_integer >= -24) { @@ -6814,12 +6816,12 @@ class basic_json To secure the access to the byte vector during CBOR/MessagePack deserialization, bytes are copied from the vector into buffers. This - function checks if the number of bytes to copy (@a len) does not exceed the - size @s size of the vector. Additionally, an @a offset is given from where - to start reading the bytes. + function checks if the number of bytes to copy (@a len) does not exceed + the size @s size of the vector. Additionally, an @a offset is given from + where to start reading the bytes. - This function checks whether reading the bytes is safe; that is, offset is a - valid index in the vector, offset+len + This function checks whether reading the bytes is safe; that is, offset is + a valid index in the vector, offset+len @param[in] size size of the byte vector @param[in] len number of bytes to read From cdd3b5a68c9cae62815808e36486aab60fe87ffe Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 3 Jan 2017 23:52:01 +0100 Subject: [PATCH 69/71] :ambulance: fix for #416 --- src/json.hpp | 12 ++++-------- src/json.hpp.re2c | 15 +++++---------- test/src/unit-regression.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index f3b77a41..4aa293d2 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6938,11 +6938,10 @@ class basic_json case 0xca: // float 32 { // copy bytes in reverse order into the double variable - check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(float); // skip content bytes return res; @@ -6951,11 +6950,10 @@ class basic_json case 0xcb: // float 64 { // copy bytes in reverse order into the double variable - check_length(v.size(), sizeof(double), 1); double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(double); // skip content bytes return res; @@ -7549,11 +7547,10 @@ class basic_json case 0xfa: // Single-Precision Float (four-byte IEEE 754) { // copy bytes in reverse order into the float variable - check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(float); // skip content bytes return res; @@ -7561,12 +7558,11 @@ class basic_json case 0xfb: // Double-Precision Float (eight-byte IEEE 754) { - check_length(v.size(), sizeof(double), 1); // copy bytes in reverse order into the double variable double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(double); // skip content bytes return res; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 42459f4e..73c4131a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6938,11 +6938,10 @@ class basic_json case 0xca: // float 32 { // copy bytes in reverse order into the double variable - check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(float); // skip content bytes return res; @@ -6951,11 +6950,10 @@ class basic_json case 0xcb: // float 64 { // copy bytes in reverse order into the double variable - check_length(v.size(), sizeof(double), 1); double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(double); // skip content bytes return res; @@ -7517,7 +7515,6 @@ class basic_json case 0xf9: // Half-Precision Float (two-byte IEEE 754) { - check_length(v.size(), 2, 1); idx += 2; // skip two content bytes // code from RFC 7049, Appendix D, Figure 3: @@ -7527,7 +7524,7 @@ class basic_json // include at least decoding support for them even without such // support. An example of a small decoder for half-precision // floating-point numbers in the C language is shown in Fig. 3. - const int half = (v[current_idx + 1] << 8) + v[current_idx + 2]; + const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2); const int exp = (half >> 10) & 0x1f; const int mant = half & 0x3ff; double val; @@ -7549,11 +7546,10 @@ class basic_json case 0xfa: // Single-Precision Float (four-byte IEEE 754) { // copy bytes in reverse order into the float variable - check_length(v.size(), sizeof(float), 1); float res; for (size_t byte = 0; byte < sizeof(float); ++byte) { - reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(float); // skip content bytes return res; @@ -7561,12 +7557,11 @@ class basic_json case 0xfb: // Double-Precision Float (eight-byte IEEE 754) { - check_length(v.size(), sizeof(double), 1); // copy bytes in reverse order into the double variable double res; for (size_t byte = 0; byte < sizeof(double); ++byte) { - reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); } idx += sizeof(double); // skip content bytes return res; diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 401867c2..7cb9169f 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -663,4 +663,31 @@ TEST_CASE("regression tests") std::vector vec3 {0xbf, 0x61, 0x61, 0x01}; CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range); } + + SECTION("issue #416 - Use-of-uninitialized-value (OSS-Fuzz issue 377)") + { + // original test case + std::vector vec1 + { + 0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71, + 0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a, + 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfa + }; + CHECK_THROWS_AS(json::from_cbor(vec1), std::out_of_range); + + // related test case: double-precision + std::vector vec2 + { + 0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71, + 0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a, + 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb + }; + CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range); + } } From 476507031890fd6effb7b447a156168803b0bd37 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 4 Jan 2017 18:07:46 +0100 Subject: [PATCH 70/71] :memo: added documentation wrt. UTF-8 strings #406 --- README.md | 1 + src/json.hpp | 9 +++++++-- src/json.hpp.re2c | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 498d1bbe..071d847a 100644 --- a/README.md +++ b/README.md @@ -600,6 +600,7 @@ Thanks a lot for helping out! - Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse errors. - [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. + - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs. ## Execute unit tests diff --git a/src/json.hpp b/src/json.hpp index 4aa293d2..6b1dc663 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -450,6 +450,12 @@ class basic_json std::string @endcode + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + #### String comparison [RFC 7159](http://rfc7159.net/rfc7159) states: @@ -7515,7 +7521,6 @@ class basic_json case 0xf9: // Half-Precision Float (two-byte IEEE 754) { - check_length(v.size(), 2, 1); idx += 2; // skip two content bytes // code from RFC 7049, Appendix D, Figure 3: @@ -7525,7 +7530,7 @@ class basic_json // include at least decoding support for them even without such // support. An example of a small decoder for half-precision // floating-point numbers in the C language is shown in Fig. 3. - const int half = (v[current_idx + 1] << 8) + v[current_idx + 2]; + const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2); const int exp = (half >> 10) & 0x1f; const int mant = half & 0x3ff; double val; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 73c4131a..0eed0e6d 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -450,6 +450,12 @@ class basic_json std::string @endcode + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + #### String comparison [RFC 7159](http://rfc7159.net/rfc7159) states: From 9f6c86f23317ccc67429be779a6f17855c6e5a0b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 4 Jan 2017 18:54:44 +0100 Subject: [PATCH 71/71] :bug: fix for #417 --- src/json.hpp | 6 ++---- src/json.hpp.re2c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 6b1dc663..e8861926 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -58,13 +58,11 @@ SOFTWARE. // exclude unsupported compilers #if defined(__clang__) - #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) - #if CLANG_VERSION < 30400 + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" #endif #elif defined(__GNUC__) - #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) - #if GCC_VERSION < 40900 + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 0eed0e6d..3a12a78d 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -58,13 +58,11 @@ SOFTWARE. // exclude unsupported compilers #if defined(__clang__) - #define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) - #if CLANG_VERSION < 30400 + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" #endif #elif defined(__GNUC__) - #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) - #if GCC_VERSION < 40900 + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif