xref: /llvm-project/libcxx/test/libcxx/fuzzing/random.pass.cpp (revision 1c3a99c7397ea630949ab65a87fa6dd97609e4b3)
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 || clang-20)
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(&tmp, data, 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(&input, data, 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(vals[0]) || std::isnan(std::abs(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(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