/src/simdjson/fuzz/fuzz_implementations.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * For fuzzing all of the implementations (haswell/fallback/westmere), |
3 | | * finding any difference between the output of each which would |
4 | | * indicate inconsistency. Also, it gets the non-default backend |
5 | | * some fuzzing love. |
6 | | * |
7 | | * Copyright Paul Dreik 20200909 for the simdjson project. |
8 | | */ |
9 | | |
10 | | #include "simdjson.h" |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <cstdlib> |
14 | | #include <iostream> |
15 | | #include <string> |
16 | | #include <array> |
17 | | #include "supported_implementations.h" |
18 | | |
19 | | |
20 | | // store each implementation along with it's intermediate results, |
21 | | // which would make things easier to debug in case this fuzzer ever |
22 | | // catches anything |
23 | | struct Impl { |
24 | 46.0k | explicit Impl(const simdjson::implementation* im=nullptr) : impl(im),parser(),element(),error(),output(){} |
25 | | //silence -Weffc++ |
26 | | Impl(const Impl&)=delete; |
27 | | Impl& operator=(const Impl&)=delete; |
28 | | |
29 | | const simdjson::implementation* impl; |
30 | | simdjson::dom::parser parser; |
31 | | simdjson::dom::element element; |
32 | | simdjson::error_code error; |
33 | | std::string output; |
34 | | }; |
35 | | |
36 | | template<class Iterator> |
37 | 0 | void showErrorAndAbort(Iterator first, Iterator last) { |
38 | 0 | auto it=first; |
39 | 0 | while(it!=last) { |
40 | 0 | std::cerr<<"Implementation: "<<it->impl->name()<<"\tError:"<<it->error<<'\n'; |
41 | 0 | it++; |
42 | 0 | } |
43 | 0 | std::cerr.flush(); |
44 | 0 | std::abort(); |
45 | 0 | } |
46 | | |
47 | | template<class Iterator> |
48 | 0 | void showOutputAndAbort(Iterator first, Iterator last) { |
49 | |
|
50 | 0 | for(auto it=first;it!=last;++it) { |
51 | 0 | std::cerr<<"Implementation: "<<it->impl->name()<<"\tOutput: "<<it->output<<'\n'; |
52 | 0 | } |
53 | | |
54 | | // show the pairwise results |
55 | 0 | for(auto it1=first; it1!=last; ++it1) { |
56 | 0 | for(auto it2=it1; it2!=last; ++it2) { |
57 | 0 | if(it1!=it2) { |
58 | 0 | const bool matches=(it1->output==it2->output); |
59 | 0 | std::cerr<<"Implementation "<<it1->impl->name()<<" and "<<it2->impl->name()<<(matches?" match.":" do NOT match.")<<'\n'; |
60 | 0 | } |
61 | 0 | } |
62 | 0 | } |
63 | 0 | std::cerr.flush(); |
64 | 0 | std::abort(); |
65 | 0 | } |
66 | | |
67 | 11.5k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { |
68 | | |
69 | | // since this check is expensive, only do it once |
70 | 11.5k | static const auto supported_implementations=get_runtime_supported_implementations(); |
71 | | |
72 | | |
73 | | // make this dynamic, so it works regardless of how it was compiled |
74 | | // or what hardware it runs on |
75 | 11.5k | constexpr std::size_t Nimplementations_max=4; |
76 | 11.5k | const std::size_t Nimplementations = supported_implementations.size(); |
77 | | |
78 | 11.5k | if(Nimplementations>Nimplementations_max) { |
79 | | //there is another backend added, please bump Nimplementations_max! |
80 | 0 | std::abort(); |
81 | 0 | } |
82 | | |
83 | | // get pointers to the backend implementation |
84 | 11.5k | std::array<Impl,Nimplementations_max> implementations; |
85 | 11.5k | { |
86 | 11.5k | std::size_t i=0; |
87 | 34.5k | for(auto& e: supported_implementations) { |
88 | 34.5k | implementations[i++].impl=e; |
89 | 34.5k | } |
90 | 11.5k | } |
91 | | |
92 | | // let each implementation parse and store the result |
93 | 11.5k | std::size_t nerrors=0; |
94 | 46.0k | for(std::size_t i=0; i<Nimplementations; ++i) { |
95 | 34.5k | auto& e=implementations[i]; |
96 | 34.5k | simdjson::get_active_implementation()=e.impl; |
97 | 34.5k | e.error=e.parser.parse(Data,Size).get(e.element); |
98 | 34.5k | if(e.error) { |
99 | 15.6k | ++nerrors; |
100 | 18.9k | } else { |
101 | 18.9k | std::ostringstream oss; |
102 | 18.9k | oss<<e.element; |
103 | 18.9k | e.output=oss.str(); |
104 | 18.9k | } |
105 | 34.5k | } |
106 | | |
107 | | //we should either have no errors, or all should error |
108 | 11.5k | if(nerrors!=0) { |
109 | 5.20k | if(nerrors!=Nimplementations) { |
110 | 0 | showErrorAndAbort(implementations.begin(), |
111 | 0 | implementations.begin()+Nimplementations); |
112 | 0 | } |
113 | 5.20k | return 0; |
114 | 5.20k | } |
115 | | |
116 | | //parsing went well for all. compare the output against the first. |
117 | 6.30k | const std::string& reference=implementations[0].output; |
118 | 18.9k | for(std::size_t i=1; i<Nimplementations; ++i) { |
119 | 12.6k | if(implementations[i].output!=reference) { |
120 | 0 | showOutputAndAbort(implementations.begin(), |
121 | 0 | implementations.begin()+Nimplementations); |
122 | 0 | } |
123 | 12.6k | } |
124 | | |
125 | | //all is well |
126 | 6.30k | return 0; |
127 | 11.5k | } |