xref: /llvm-project/libc/fuzzing/stdio/printf_float_conv_fuzz.cpp (revision e7866ea2125efe4baed98f6f9545966acf1ebad0)
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 
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 
32 enum class TestResult {
33   Success,
34   BufferSizeFailed,
35   LengthsDiffer,
36   StringsNotEqual,
37 };
38 
39 inline TestResult test_vals(const char *fmt, double num, int prec, int width) {
40   // Call snprintf on a nullptr to get the buffer size.
41   int buffer_size = __llvm_libc::snprintf(nullptr, 0, fmt, width, prec, num);
42 
43   if (buffer_size < 0) {
44     return TestResult::BufferSizeFailed;
45   }
46 
47   char *test_buff = new char[buffer_size + 1];
48   char *reference_buff = new char[buffer_size + 1];
49 
50   int test_result = 0;
51   int reference_result = 0;
52 
53   test_result =
54       __llvm_libc::snprintf(test_buff, buffer_size + 1, fmt, width, prec, num);
55   reference_result =
56       mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);
57 
58   // All of these calls should return that they wrote the same amount.
59   if (test_result != reference_result || test_result != buffer_size) {
60     return TestResult::LengthsDiffer;
61   }
62 
63   if (!simple_streq(test_buff, reference_buff, buffer_size)) {
64     return TestResult::StringsNotEqual;
65   }
66 
67   delete[] test_buff;
68   delete[] reference_buff;
69   return TestResult::Success;
70 }
71 
72 constexpr char const *fmt_arr[] = {
73     "%*.*f",
74     "%*.*e",
75     "%*.*g",
76     "%*.*a",
77 };
78 
79 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
80   // const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};
81   // data = raw_data;
82   // size = sizeof(raw_data);
83   double num = 0.0;
84   int prec = 0;
85   int width = 0;
86 
87   __llvm_libc::fputil::FPBits<double>::UIntType raw_num = 0;
88 
89   // Copy as many bytes of data as will fit into num, prec, and with. Any extras
90   // are ignored.
91   for (size_t cur = 0; cur < size; ++cur) {
92     if (cur < sizeof(raw_num)) {
93       raw_num = (raw_num << 8) + data[cur];
94     } else if (cur < sizeof(raw_num) + sizeof(prec)) {
95       prec = (prec << 8) + data[cur];
96     } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
97       width = (width << 8) + data[cur];
98     }
99   }
100 
101   num = __llvm_libc::fputil::FPBits<double>(raw_num).get_val();
102 
103   if (width > MAX_SIZE) {
104     width = MAX_SIZE;
105   } else if (width < -MAX_SIZE) {
106     width = -MAX_SIZE;
107   }
108 
109   if (prec > MAX_SIZE) {
110     prec = MAX_SIZE;
111   } else if (prec < -MAX_SIZE) {
112     prec = -MAX_SIZE;
113   }
114 
115   for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
116        ++cur_fmt) {
117     TestResult result = test_vals(fmt_arr[cur_fmt], num, prec, width);
118     if (result != TestResult::Success) {
119       __builtin_trap();
120     }
121   }
122   return 0;
123 }
124