xref: /llvm-project/libc/fuzzing/stdio/printf_float_conv_fuzz.cpp (revision 3546f4da1975ec65c1ccc5a2372f050c2a9b92aa)
1 //===-- printf_float_conv_fuzz.cpp ----------------------------------------===//
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 /// Fuzzing test for llvm-libc printf %f/e/g/a implementations.
10 ///
11 //===----------------------------------------------------------------------===//
12 #include "src/stdio/snprintf.h"
13 
14 #include "src/__support/FPUtil/FPBits.h"
15 
16 #include <stddef.h>
17 #include <stdint.h>
18 
19 #include "utils/MPFRWrapper/mpfr_inc.h"
20 
21 constexpr int MAX_SIZE = 10000;
22 
simple_streq(char * first,char * second,int length)23 inline bool simple_streq(char *first, char *second, int length) {
24   for (int i = 0; i < length; ++i) {
25     if (first[i] != second[i]) {
26       return false;
27     }
28   }
29   return true;
30 }
31 
simple_strlen(const char * str)32 inline int simple_strlen(const char *str) {
33   int i = 0;
34   for (; *str; ++str, ++i) {
35     ;
36   }
37   return i;
38 }
39 
40 enum class TestResult {
41   Success,
42   BufferSizeFailed,
43   LengthsDiffer,
44   StringsNotEqual,
45 };
46 
47 template <typename F>
test_vals(const char * fmt,F num,int prec,int width)48 inline TestResult test_vals(const char *fmt, F num, int prec, int width) {
49   // Call snprintf on a nullptr to get the buffer size.
50   int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num);
51 
52   if (buffer_size < 0) {
53     return TestResult::BufferSizeFailed;
54   }
55 
56   char *test_buff = new char[buffer_size + 1];
57   char *reference_buff = new char[buffer_size + 1];
58 
59   int test_result = 0;
60   int reference_result = 0;
61 
62   test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width,
63                                          prec, num);
64   reference_result =
65       mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);
66 
67   // All of these calls should return that they wrote the same amount.
68   if (test_result != reference_result || test_result != buffer_size) {
69     return TestResult::LengthsDiffer;
70   }
71 
72   if (!simple_streq(test_buff, reference_buff, buffer_size)) {
73     return TestResult::StringsNotEqual;
74   }
75 
76   delete[] test_buff;
77   delete[] reference_buff;
78   return TestResult::Success;
79 }
80 
81 constexpr char const *fmt_arr[] = {
82     "%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",
83 };
84 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)85 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
86   // const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};
87   // data = raw_data;
88   // size = sizeof(raw_data);
89   double num = 0.0;
90   int prec = 0;
91   int width = 0;
92 
93   LIBC_NAMESPACE::fputil::FPBits<double>::StorageType raw_num = 0;
94 
95   // Copy as many bytes of data as will fit into num, prec, and with. Any extras
96   // are ignored.
97   for (size_t cur = 0; cur < size; ++cur) {
98     if (cur < sizeof(raw_num)) {
99       raw_num = (raw_num << 8) + data[cur];
100     } else if (cur < sizeof(raw_num) + sizeof(prec)) {
101       prec = (prec << 8) + data[cur];
102     } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
103       width = (width << 8) + data[cur];
104     }
105   }
106 
107   num = LIBC_NAMESPACE::fputil::FPBits<double>(raw_num).get_val();
108 
109   // While we could create a "ld_raw_num" from additional bytes, it's much
110   // easier to stick with simply casting num to long double. This avoids the
111   // issues around 80 bit long doubles, especially unnormal and pseudo-denormal
112   // numbers, which MPFR doesn't handle well.
113   long double ld_num = static_cast<long double>(num);
114 
115   if (width > MAX_SIZE) {
116     width = MAX_SIZE;
117   } else if (width < -MAX_SIZE) {
118     width = -MAX_SIZE;
119   }
120 
121   if (prec > MAX_SIZE) {
122     prec = MAX_SIZE;
123   } else if (prec < -MAX_SIZE) {
124     prec = -MAX_SIZE;
125   }
126 
127   for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
128        ++cur_fmt) {
129     int fmt_len = simple_strlen(fmt_arr[cur_fmt]);
130     TestResult result;
131     if (fmt_arr[cur_fmt][fmt_len - 2] == 'L') {
132       result = test_vals<long double>(fmt_arr[cur_fmt], ld_num, prec, width);
133     } else {
134       result = test_vals<double>(fmt_arr[cur_fmt], num, prec, width);
135     }
136     if (result != TestResult::Success) {
137       __builtin_trap();
138     }
139   }
140   return 0;
141 }
142