Coverage Report

Created: 2025-06-13 06:21

/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
}