11#include " fast_float/fast_float.h"
22#include < fuzzer/FuzzedDataProvider.h>
3+ #include < cstdint>
4+ #include < cstdio>
5+ #include < cstdlib>
6+ #include < cstring>
37#include < string>
48#include < system_error>
9+ #include < type_traits>
10+ #include < vector>
11+
12+ #if defined(__has_include)
13+ #if __has_include(<charconv>) && __cplusplus >= 201703L
14+ #include < charconv>
15+ #define FASTFLOAT_FUZZ_CHARCONV 1
16+ #endif
17+ #endif
18+
19+ static void fail (char const *check) {
20+ fprintf (stderr, " fast_float fuzz failure: %s\n " , check);
21+ abort ();
22+ }
523
624fast_float::chars_format arbitrary_format (FuzzedDataProvider &fdp) {
725 using fast_float::chars_format;
8- switch (fdp.ConsumeIntegralInRange <int >(0 , 3 )) {
26+ chars_format format;
27+ switch (fdp.ConsumeIntegralInRange <int >(0 , 7 )) {
928 case 0 :
10- return chars_format::scientific;
29+ format = chars_format::scientific;
1130 break ;
1231 case 1 :
13- return chars_format::fixed;
32+ format = chars_format::fixed;
1433 break ;
1534 case 2 :
16- return chars_format::fixed;
35+ format = chars_format::json;
36+ break ;
37+ case 3 :
38+ format = chars_format::json_or_infnan;
39+ break ;
40+ case 4 :
41+ format = chars_format::fortran ;
1742 break ;
43+ case 5 :
44+ format = chars_format::javascript;
45+ break ;
46+ case 6 :
47+ format = chars_format::javascript_sloppy;
48+ break ;
49+ default :
50+ format = chars_format::general;
51+ break ;
52+ }
53+ if (fdp.ConsumeBool ()) {
54+ format = format | chars_format::allow_leading_plus;
55+ }
56+ if (fdp.ConsumeBool ()) {
57+ format = format | chars_format::skip_white_space;
58+ }
59+ return format;
60+ }
61+
62+ template <typename T> uint64_t bits_of (T value) {
63+ uint64_t bits = 0 ;
64+ memcpy (&bits, &value, sizeof (T));
65+ return bits;
66+ }
67+
68+ // The same ASCII text must parse identically for every character type: same
69+ // error code, same number of consumed code units, bit-identical value.
70+ template <typename UC >
71+ void check_widened (std::string const &input,
72+ fast_float::from_chars_result_t <char > expected,
73+ double expected_value, fast_float::chars_format format) {
74+ std::vector<UC > wide;
75+ wide.reserve (input.size ());
76+ for (char c : input) {
77+ wide.push_back (UC (static_cast <unsigned char >(c)));
78+ }
79+ double value = 0.0 ;
80+ auto result = fast_float::from_chars (wide.data (), wide.data () + wide.size (),
81+ value, format);
82+ if (result.ec != expected.ec ||
83+ (result.ptr - wide.data ()) != (expected.ptr - input.data ())) {
84+ fail (" wide result disagrees with char result" );
85+ }
86+ if (expected.ec == std::errc () && bits_of (value) != bits_of (expected_value)) {
87+ fail (" wide value disagrees with char value" );
88+ }
89+ }
90+
91+ // Every number format accepted by the library is pure ASCII, so a successful
92+ // parse must never consume a code unit >= 0x80. A violation means some helper
93+ // inspected only the low byte of a wide code unit (the bug behind #391).
94+ // The generated sequences keep an ASCII low byte but take hostile high bits:
95+ // top-bit-set units (near the minimum of a signed wchar_t, the shape behind
96+ // #422), sign-extended negative units (the shape behind #395), and non-ASCII
97+ // code points whose low byte is an ASCII digit (the shape behind #391).
98+ template <typename UC >
99+ void check_arbitrary_units (FuzzedDataProvider &fdp,
100+ fast_float::chars_format format) {
101+ using unsigned_uc = typename std::make_unsigned<UC >::type;
102+ size_t count = fdp.ConsumeIntegralInRange <size_t >(0 , 64 );
103+ std::vector<UC > units;
104+ units.reserve (count);
105+ for (size_t i = 0 ; i < count; ++i) {
106+ unsigned_uc unit = fdp.ConsumeIntegral <uint8_t >();
107+ switch (fdp.ConsumeIntegralInRange <int >(0 , 4 )) {
108+ case 0 : // top bit set: near the minimum value when UC is signed
109+ unit = unsigned_uc (unit | (unsigned_uc (1 ) << (sizeof (UC ) * 8 - 1 )));
110+ break ;
111+ case 1 : // all high bits set: a small negative value when UC is signed
112+ unit = unsigned_uc (unit | ~unsigned_uc (0xFF ));
113+ break ;
114+ case 2 : // arbitrary high byte: a non-ASCII code point, ASCII low byte
115+ unit = unsigned_uc (
116+ unit | unsigned_uc (unsigned_uc (fdp.ConsumeIntegral <uint8_t >()) << 8 ));
117+ break ;
118+ default : // plain ASCII unit
119+ break ;
120+ }
121+ units.push_back (UC (unit));
122+ }
123+ if (units.empty ()) {
124+ return ;
125+ }
126+ UC const *begin = units.data ();
127+ UC const *end = begin + units.size ();
128+
129+ double as_double = 0.0 ;
130+ auto result = fast_float::from_chars (begin, end, as_double, format);
131+ if (result.ec == std::errc ()) {
132+ for (UC const *p = begin; p != result.ptr ; ++p) {
133+ if (static_cast <unsigned_uc>(*p) >= 0x80 ) {
134+ fail (" float parse consumed a non-ASCII code unit" );
135+ }
136+ }
137+ }
138+ uint16_t as_uint16 = 0 ;
139+ auto int_result = fast_float::from_chars (begin, end, as_uint16);
140+ if (int_result.ec == std::errc ()) {
141+ for (UC const *p = begin; p != int_result.ptr ; ++p) {
142+ if (static_cast <unsigned_uc>(*p) >= 0x80 ) {
143+ fail (" integer parse consumed a non-ASCII code unit" );
144+ }
145+ }
146+ }
147+ }
148+
149+ // fast_float's integer parsing is specified to match std::from_chars.
150+ template <typename T> void check_int (std::string const &input, int base) {
151+ T value{};
152+ auto result = fast_float::from_chars (
153+ input.data (), input.data () + input.size (), value, base);
154+ #ifdef FASTFLOAT_FUZZ_CHARCONV
155+ T expected{};
156+ auto std_result = std::from_chars (input.data (), input.data () + input.size (),
157+ expected, base);
158+ if (result.ec != std_result.ec || result.ptr != std_result.ptr ) {
159+ fail (" integer result disagrees with std::from_chars" );
18160 }
19- return chars_format::general;
161+ if (result.ec == std::errc () && value != expected) {
162+ fail (" integer value disagrees with std::from_chars" );
163+ }
164+ #else
165+ static_cast <void >(result);
166+ #endif
167+ }
168+
169+ template <typename T>
170+ void check_int_all_bases (std::string const &input, int base) {
171+ check_int<T>(input, 10 );
172+ check_int<T>(input, base);
20173}
21174
22175extern " C" int LLVMFuzzerTestOneInput (uint8_t const *data, size_t size) {
23176 FuzzedDataProvider fdp (data, size);
24177 fast_float::chars_format format = arbitrary_format (fdp);
25- double result_d = 0.0 ;
178+ int base = fdp.ConsumeIntegralInRange <int >(2 , 36 );
179+
26180 std::string input_d = fdp.ConsumeRandomLengthString (128 );
27- auto answer = fast_float::from_chars (
181+ double result_d = 0.0 ;
182+ auto answer_d = fast_float::from_chars (
28183 input_d.data (), input_d.data () + input_d.size (), result_d, format);
29- std::string input_f = fdp.ConsumeRandomLengthString (128 );
30184 float result_f = 0.0 ;
31- answer = fast_float::from_chars (
32- input_f.data (), input_f.data () + input_f.size (), result_f, format);
33- int result_i = 0 ;
185+ fast_float::from_chars (input_d.data (), input_d.data () + input_d.size (),
186+ result_f, format);
187+ check_widened<wchar_t >(input_d, answer_d, result_d, format);
188+ check_widened<char16_t >(input_d, answer_d, result_d, format);
189+ check_widened<char32_t >(input_d, answer_d, result_d, format);
190+
34191 std::string input_i = fdp.ConsumeRandomLengthString (128 );
35- answer = fast_float::from_chars (input_i.data (),
36- input_i.data () + input_i.size (), result_i);
192+ check_int_all_bases<uint8_t >(input_i, base);
193+ check_int_all_bases<int8_t >(input_i, base);
194+ check_int_all_bases<uint16_t >(input_i, base);
195+ check_int_all_bases<int16_t >(input_i, base);
196+ check_int_all_bases<uint32_t >(input_i, base);
197+ check_int_all_bases<int32_t >(input_i, base);
198+ check_int_all_bases<uint64_t >(input_i, base);
199+ check_int_all_bases<int64_t >(input_i, base);
200+
201+ check_arbitrary_units<wchar_t >(fdp, format);
202+ check_arbitrary_units<char16_t >(fdp, format);
203+ check_arbitrary_units<char32_t >(fdp, format);
37204 return 0 ;
38- }
205+ }
0 commit comments