xref: /llvm-project/libc/fuzzing/stdio/printf_float_conv_fuzz.cpp (revision 3546f4da1975ec65c1ccc5a2372f050c2a9b92aa)
1e7866ea2SMichael Jones //===-- printf_float_conv_fuzz.cpp ----------------------------------------===//
2e7866ea2SMichael Jones //
3e7866ea2SMichael Jones // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e7866ea2SMichael Jones // See https://llvm.org/LICENSE.txt for license information.
5e7866ea2SMichael Jones // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e7866ea2SMichael Jones //
7e7866ea2SMichael Jones //===----------------------------------------------------------------------===//
8e7866ea2SMichael Jones ///
9e7866ea2SMichael Jones /// Fuzzing test for llvm-libc printf %f/e/g/a implementations.
10e7866ea2SMichael Jones ///
11e7866ea2SMichael Jones //===----------------------------------------------------------------------===//
12e7866ea2SMichael Jones #include "src/stdio/snprintf.h"
13e7866ea2SMichael Jones 
14e7866ea2SMichael Jones #include "src/__support/FPUtil/FPBits.h"
15e7866ea2SMichael Jones 
16e7866ea2SMichael Jones #include <stddef.h>
17e7866ea2SMichael Jones #include <stdint.h>
18e7866ea2SMichael Jones 
19e7866ea2SMichael Jones #include "utils/MPFRWrapper/mpfr_inc.h"
20e7866ea2SMichael Jones 
21e7866ea2SMichael Jones constexpr int MAX_SIZE = 10000;
22e7866ea2SMichael Jones 
simple_streq(char * first,char * second,int length)23e7866ea2SMichael Jones inline bool simple_streq(char *first, char *second, int length) {
24e7866ea2SMichael Jones   for (int i = 0; i < length; ++i) {
25e7866ea2SMichael Jones     if (first[i] != second[i]) {
26e7866ea2SMichael Jones       return false;
27e7866ea2SMichael Jones     }
28e7866ea2SMichael Jones   }
29e7866ea2SMichael Jones   return true;
30e7866ea2SMichael Jones }
31e7866ea2SMichael Jones 
simple_strlen(const char * str)328a47ad4bSmichaelrj-google inline int simple_strlen(const char *str) {
338a47ad4bSmichaelrj-google   int i = 0;
348a47ad4bSmichaelrj-google   for (; *str; ++str, ++i) {
358a47ad4bSmichaelrj-google     ;
368a47ad4bSmichaelrj-google   }
378a47ad4bSmichaelrj-google   return i;
388a47ad4bSmichaelrj-google }
398a47ad4bSmichaelrj-google 
40e7866ea2SMichael Jones enum class TestResult {
41e7866ea2SMichael Jones   Success,
42e7866ea2SMichael Jones   BufferSizeFailed,
43e7866ea2SMichael Jones   LengthsDiffer,
44e7866ea2SMichael Jones   StringsNotEqual,
45e7866ea2SMichael Jones };
46e7866ea2SMichael Jones 
478a47ad4bSmichaelrj-google template <typename F>
test_vals(const char * fmt,F num,int prec,int width)488a47ad4bSmichaelrj-google inline TestResult test_vals(const char *fmt, F num, int prec, int width) {
49e7866ea2SMichael Jones   // Call snprintf on a nullptr to get the buffer size.
50b6bc9d72SGuillaume Chatelet   int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num);
51e7866ea2SMichael Jones 
52e7866ea2SMichael Jones   if (buffer_size < 0) {
53e7866ea2SMichael Jones     return TestResult::BufferSizeFailed;
54e7866ea2SMichael Jones   }
55e7866ea2SMichael Jones 
56e7866ea2SMichael Jones   char *test_buff = new char[buffer_size + 1];
57e7866ea2SMichael Jones   char *reference_buff = new char[buffer_size + 1];
58e7866ea2SMichael Jones 
59e7866ea2SMichael Jones   int test_result = 0;
60e7866ea2SMichael Jones   int reference_result = 0;
61e7866ea2SMichael Jones 
62b6bc9d72SGuillaume Chatelet   test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width,
63b6bc9d72SGuillaume Chatelet                                          prec, num);
64e7866ea2SMichael Jones   reference_result =
65e7866ea2SMichael Jones       mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);
66e7866ea2SMichael Jones 
67e7866ea2SMichael Jones   // All of these calls should return that they wrote the same amount.
68e7866ea2SMichael Jones   if (test_result != reference_result || test_result != buffer_size) {
69e7866ea2SMichael Jones     return TestResult::LengthsDiffer;
70e7866ea2SMichael Jones   }
71e7866ea2SMichael Jones 
72e7866ea2SMichael Jones   if (!simple_streq(test_buff, reference_buff, buffer_size)) {
73e7866ea2SMichael Jones     return TestResult::StringsNotEqual;
74e7866ea2SMichael Jones   }
75e7866ea2SMichael Jones 
76e7866ea2SMichael Jones   delete[] test_buff;
77e7866ea2SMichael Jones   delete[] reference_buff;
78e7866ea2SMichael Jones   return TestResult::Success;
79e7866ea2SMichael Jones }
80e7866ea2SMichael Jones 
81e7866ea2SMichael Jones constexpr char const *fmt_arr[] = {
828a47ad4bSmichaelrj-google     "%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",
83e7866ea2SMichael Jones };
84e7866ea2SMichael Jones 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)85e7866ea2SMichael Jones extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
86e7866ea2SMichael Jones   // const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};
87e7866ea2SMichael Jones   // data = raw_data;
88e7866ea2SMichael Jones   // size = sizeof(raw_data);
89e7866ea2SMichael Jones   double num = 0.0;
90e7866ea2SMichael Jones   int prec = 0;
91e7866ea2SMichael Jones   int width = 0;
92e7866ea2SMichael Jones 
93*3546f4daSGuillaume Chatelet   LIBC_NAMESPACE::fputil::FPBits<double>::StorageType raw_num = 0;
94e7866ea2SMichael Jones 
95e7866ea2SMichael Jones   // Copy as many bytes of data as will fit into num, prec, and with. Any extras
96e7866ea2SMichael Jones   // are ignored.
97e7866ea2SMichael Jones   for (size_t cur = 0; cur < size; ++cur) {
98e7866ea2SMichael Jones     if (cur < sizeof(raw_num)) {
99e7866ea2SMichael Jones       raw_num = (raw_num << 8) + data[cur];
100e7866ea2SMichael Jones     } else if (cur < sizeof(raw_num) + sizeof(prec)) {
101e7866ea2SMichael Jones       prec = (prec << 8) + data[cur];
102e7866ea2SMichael Jones     } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
103e7866ea2SMichael Jones       width = (width << 8) + data[cur];
104e7866ea2SMichael Jones     }
105e7866ea2SMichael Jones   }
106e7866ea2SMichael Jones 
107b6bc9d72SGuillaume Chatelet   num = LIBC_NAMESPACE::fputil::FPBits<double>(raw_num).get_val();
108e7866ea2SMichael Jones 
1098a47ad4bSmichaelrj-google   // While we could create a "ld_raw_num" from additional bytes, it's much
1108a47ad4bSmichaelrj-google   // easier to stick with simply casting num to long double. This avoids the
1118a47ad4bSmichaelrj-google   // issues around 80 bit long doubles, especially unnormal and pseudo-denormal
1128a47ad4bSmichaelrj-google   // numbers, which MPFR doesn't handle well.
1138a47ad4bSmichaelrj-google   long double ld_num = static_cast<long double>(num);
1148a47ad4bSmichaelrj-google 
115e7866ea2SMichael Jones   if (width > MAX_SIZE) {
116e7866ea2SMichael Jones     width = MAX_SIZE;
117e7866ea2SMichael Jones   } else if (width < -MAX_SIZE) {
118e7866ea2SMichael Jones     width = -MAX_SIZE;
119e7866ea2SMichael Jones   }
120e7866ea2SMichael Jones 
121e7866ea2SMichael Jones   if (prec > MAX_SIZE) {
122e7866ea2SMichael Jones     prec = MAX_SIZE;
123e7866ea2SMichael Jones   } else if (prec < -MAX_SIZE) {
124e7866ea2SMichael Jones     prec = -MAX_SIZE;
125e7866ea2SMichael Jones   }
126e7866ea2SMichael Jones 
127e7866ea2SMichael Jones   for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
128e7866ea2SMichael Jones        ++cur_fmt) {
1298a47ad4bSmichaelrj-google     int fmt_len = simple_strlen(fmt_arr[cur_fmt]);
1308a47ad4bSmichaelrj-google     TestResult result;
1318a47ad4bSmichaelrj-google     if (fmt_arr[cur_fmt][fmt_len - 2] == 'L') {
1328a47ad4bSmichaelrj-google       result = test_vals<long double>(fmt_arr[cur_fmt], ld_num, prec, width);
1338a47ad4bSmichaelrj-google     } else {
1348a47ad4bSmichaelrj-google       result = test_vals<double>(fmt_arr[cur_fmt], num, prec, width);
1358a47ad4bSmichaelrj-google     }
136e7866ea2SMichael Jones     if (result != TestResult::Success) {
137e7866ea2SMichael Jones       __builtin_trap();
138e7866ea2SMichael Jones     }
139e7866ea2SMichael Jones   }
140e7866ea2SMichael Jones   return 0;
141e7866ea2SMichael Jones }
142