1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // This test fails because Clang no longer enables -fdelayed-template-parsing |
10 | // by default on Windows with C++20 (#69431). |
11 | // XFAIL: msvc && (clang-18 || clang-19) |
12 | |
13 | // UNSUPPORTED: c++03, c++11 |
14 | |
15 | #include <cassert> |
16 | #include <cmath> |
17 | #include <cstddef> |
18 | #include <cstdint> |
19 | #include <cstring> |
20 | #include <random> |
21 | #include <type_traits> |
22 | #include <vector> |
23 | |
24 | #include "fuzz.h" |
25 | |
26 | template <class IntT> |
27 | std::vector<IntT> GetValues(const std::uint8_t *data, std::size_t size) { |
28 | std::vector<IntT> result; |
29 | while (size >= sizeof(IntT)) { |
30 | IntT tmp; |
31 | std::memcpy(dest: &tmp, src: data, n: sizeof(IntT)); |
32 | size -= sizeof(IntT); |
33 | data += sizeof(IntT); |
34 | result.push_back(tmp); |
35 | } |
36 | return result; |
37 | } |
38 | |
39 | template <class Dist> |
40 | struct ParamTypeHelper { |
41 | using ParamT = typename Dist::param_type; |
42 | using ResultT = typename Dist::result_type; |
43 | static_assert(std::is_same<ResultT, typename ParamT::distribution_type::result_type>::value, "" ); |
44 | |
45 | static ParamT Create(const std::uint8_t* data, std::size_t size, bool &OK) { |
46 | constexpr bool select_vector_result = std::is_constructible<ParamT, ResultT*, ResultT*, ResultT*>::value; |
47 | constexpr bool select_vector_double = std::is_constructible<ParamT, double*, double*>::value; |
48 | constexpr int selector = select_vector_result ? 0 : (select_vector_double ? 1 : 2); |
49 | return DispatchAndCreate(std::integral_constant<int, selector>{}, data, size, OK); |
50 | } |
51 | |
52 | // Vector result |
53 | static ParamT DispatchAndCreate(std::integral_constant<int, 0>, const std::uint8_t *data, std::size_t size, bool &OK) { |
54 | auto Input = GetValues<ResultT>(data, size); |
55 | OK = false; |
56 | if (Input.size() < 10) |
57 | return ParamT{}; |
58 | OK = true; |
59 | auto Beg = Input.begin(); |
60 | auto End = Input.end(); |
61 | auto Mid = Beg + ((End - Beg) / 2); |
62 | |
63 | assert(Mid - Beg <= (End - Mid)); |
64 | ParamT p(Beg, Mid, Mid); |
65 | return p; |
66 | } |
67 | |
68 | // Vector double |
69 | static ParamT DispatchAndCreate(std::integral_constant<int, 1>, const std::uint8_t *data, std::size_t size, bool &OK) { |
70 | auto Input = GetValues<double>(data, size); |
71 | |
72 | OK = true; |
73 | auto Beg = Input.begin(); |
74 | auto End = Input.end(); |
75 | |
76 | ParamT p(Beg, End); |
77 | return p; |
78 | } |
79 | |
80 | // Default |
81 | static ParamT DispatchAndCreate(std::integral_constant<int, 2>, const std::uint8_t *data, std::size_t size, bool &OK) { |
82 | OK = false; |
83 | if (size < sizeof(ParamT)) |
84 | return ParamT{}; |
85 | OK = true; |
86 | ParamT input; |
87 | std::memcpy(dest: &input, src: data, n: sizeof(ParamT)); |
88 | return input; |
89 | } |
90 | }; |
91 | |
92 | template <class IntT> |
93 | struct ParamTypeHelper<std::poisson_distribution<IntT>> { |
94 | using Dist = std::poisson_distribution<IntT>; |
95 | using ParamT = typename Dist::param_type; |
96 | using ResultT = typename Dist::result_type; |
97 | |
98 | static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { |
99 | OK = false; |
100 | auto vals = GetValues<double>(data, size); |
101 | if (vals.empty() || std::isnan(x: vals[0]) || std::isnan(x: std::abs(x: vals[0])) || vals[0] < 0) |
102 | return ParamT{}; |
103 | OK = true; |
104 | return ParamT{vals[0]}; |
105 | } |
106 | }; |
107 | |
108 | template <class IntT> |
109 | struct ParamTypeHelper<std::geometric_distribution<IntT>> { |
110 | using Dist = std::geometric_distribution<IntT>; |
111 | using ParamT = typename Dist::param_type; |
112 | using ResultT = typename Dist::result_type; |
113 | |
114 | static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { |
115 | OK = false; |
116 | auto vals = GetValues<double>(data, size); |
117 | if (vals.empty() || std::isnan(x: vals[0]) || vals[0] < 0 ) |
118 | return ParamT{}; |
119 | OK = true; |
120 | return ParamT{vals[0]}; |
121 | } |
122 | }; |
123 | |
124 | template <class IntT> |
125 | struct ParamTypeHelper<std::lognormal_distribution<IntT>> { |
126 | using Dist = std::lognormal_distribution<IntT>; |
127 | using ParamT = typename Dist::param_type; |
128 | using ResultT = typename Dist::result_type; |
129 | |
130 | static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { |
131 | OK = false; |
132 | auto vals = GetValues<ResultT>(data, size); |
133 | if (vals.size() < 2 ) |
134 | return ParamT{}; |
135 | OK = true; |
136 | return ParamT{vals[0], vals[1]}; |
137 | } |
138 | }; |
139 | |
140 | template <> |
141 | struct ParamTypeHelper<std::bernoulli_distribution> { |
142 | using Dist = std::bernoulli_distribution; |
143 | using ParamT = Dist::param_type; |
144 | using ResultT = Dist::result_type; |
145 | |
146 | static ParamT Create(const std::uint8_t *data, std::size_t size, bool& OK) { |
147 | OK = false; |
148 | auto vals = GetValues<double>(data, size); |
149 | if (vals.empty()) |
150 | return ParamT{}; |
151 | OK = true; |
152 | return ParamT{vals[0]}; |
153 | } |
154 | }; |
155 | |
156 | template <class Distribution> |
157 | int helper(const std::uint8_t *data, std::size_t size) { |
158 | std::mt19937 engine; |
159 | using ParamT = typename Distribution::param_type; |
160 | bool OK; |
161 | ParamT p = ParamTypeHelper<Distribution>::Create(data, size, OK); |
162 | if (!OK) |
163 | return 0; |
164 | Distribution d(p); |
165 | volatile auto res = d(engine); |
166 | if (std::isnan(res)) { |
167 | // FIXME(llvm.org/PR44289): |
168 | // Investigate why these distributions are returning NaN and decide |
169 | // if that's what we want them to be doing. |
170 | // |
171 | // Make this assert false (or return non-zero). |
172 | return 0; |
173 | } |
174 | return 0; |
175 | } |
176 | |
177 | extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { |
178 | return helper<std::uniform_int_distribution<std::int16_t>>(data, size) || |
179 | helper<std::uniform_real_distribution<float>>(data, size) || |
180 | helper<std::bernoulli_distribution>(data, size) || |
181 | helper<std::poisson_distribution<std::int16_t>>(data, size) || |
182 | helper<std::geometric_distribution<std::int16_t>>(data, size) || |
183 | helper<std::binomial_distribution<std::int16_t>>(data, size) || |
184 | helper<std::negative_binomial_distribution<std::int16_t>>(data, size) || |
185 | helper<std::exponential_distribution<float>>(data, size) || |
186 | helper<std::gamma_distribution<float>>(data, size) || |
187 | helper<std::weibull_distribution<float>>(data, size) || |
188 | helper<std::extreme_value_distribution<float>>(data, size) || |
189 | helper<std::normal_distribution<float>>(data, size) || |
190 | helper<std::lognormal_distribution<float>>(data, size) || |
191 | helper<std::chi_squared_distribution<float>>(data, size) || |
192 | helper<std::cauchy_distribution<float>>(data, size) || |
193 | helper<std::fisher_f_distribution<float>>(data, size) || |
194 | helper<std::student_t_distribution<float>>(data, size) || |
195 | helper<std::discrete_distribution<std::int16_t>>(data, size) || |
196 | helper<std::piecewise_constant_distribution<float>>(data, size) || |
197 | helper<std::piecewise_linear_distribution<float>>(data, size); |
198 | } |
199 | |