xref: /llvm-project/libc/fuzzing/stdio/printf_fixed_conv_fuzz.cpp (revision 73aab2f69773324ef0250f093bd4d782beee921a)
1 //===-- printf_fixed_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 "include/llvm-libc-macros/stdfix-macros.h"
15 #include "src/__support/fixed_point/fx_bits.h"
16 #include "src/__support/fixed_point/fx_rep.h"
17 
18 #include <stddef.h>
19 #include <stdint.h>
20 
21 #include "utils/MPFRWrapper/mpfr_inc.h"
22 
23 constexpr int MAX_SIZE = 10000;
24 
simple_streq(char * first,char * second,int length)25 inline bool simple_streq(char *first, char *second, int length) {
26   for (int i = 0; i < length; ++i)
27     if (first[i] != second[i])
28       return false;
29 
30   return true;
31 }
32 
clamp(int num,int max)33 inline int clamp(int num, int max) {
34   if (num > max)
35     return max;
36   if (num < -max)
37     return -max;
38   return num;
39 }
40 
41 enum class TestResult {
42   Success,
43   BufferSizeFailed,
44   LengthsDiffer,
45   StringsNotEqual,
46 };
47 
48 template <typename F>
test_vals(const char * fmt,uint64_t num,int prec,int width)49 inline TestResult test_vals(const char *fmt, uint64_t num, int prec,
50                             int width) {
51   typename LIBC_NAMESPACE::fixed_point::FXRep<F>::StorageType raw_num = num;
52 
53   auto raw_num_bits = LIBC_NAMESPACE::fixed_point::FXBits<F>(raw_num);
54 
55   // This needs to be a float with enough bits of precision to hold the fixed
56   // point number.
57   static_assert(sizeof(long double) > sizeof(long accum));
58 
59   // build a long double that is equivalent to the fixed point number.
60   long double ld_num =
61       static_cast<long double>(raw_num_bits.get_integral()) +
62       (static_cast<long double>(raw_num_bits.get_fraction()) /
63        static_cast<long double>(1ll << raw_num_bits.get_exponent()));
64 
65   if (raw_num_bits.get_sign())
66     ld_num = -ld_num;
67 
68   // Call snprintf on a nullptr to get the buffer size.
69   int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num);
70 
71   if (buffer_size < 0)
72     return TestResult::BufferSizeFailed;
73 
74   char *test_buff = new char[buffer_size + 1];
75   char *reference_buff = new char[buffer_size + 1];
76 
77   int test_result = 0;
78   int reference_result = 0;
79 
80   test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width,
81                                          prec, num);
82 
83   // The fixed point format is defined to be %f equivalent.
84   reference_result = mpfr_snprintf(reference_buff, buffer_size + 1, "%*.*Lf",
85                                    width, prec, ld_num);
86 
87   // All of these calls should return that they wrote the same amount.
88   if (test_result != reference_result || test_result != buffer_size)
89     return TestResult::LengthsDiffer;
90 
91   if (!simple_streq(test_buff, reference_buff, buffer_size))
92     return TestResult::StringsNotEqual;
93 
94   delete[] test_buff;
95   delete[] reference_buff;
96   return TestResult::Success;
97 }
98 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)99 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
100   // const uint8_t raw_data[] = {0x8d,0x43,0x40,0x0,0x0,0x0,};
101   // data = raw_data;
102   // size = sizeof(raw_data);
103   int prec = 0;
104   int width = 0;
105 
106   LIBC_NAMESPACE::fixed_point::FXRep<long accum>::StorageType raw_num = 0;
107 
108   // Copy as many bytes of data as will fit into num, prec, and with. Any extras
109   // are ignored.
110   for (size_t cur = 0; cur < size; ++cur) {
111     if (cur < sizeof(raw_num)) {
112       raw_num = (raw_num << 8) + data[cur];
113     } else if (cur < sizeof(raw_num) + sizeof(prec)) {
114       prec = (prec << 8) + data[cur];
115     } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
116       width = (width << 8) + data[cur];
117     }
118   }
119 
120   width = clamp(width, MAX_SIZE);
121   prec = clamp(prec, MAX_SIZE);
122 
123   TestResult result;
124   result = test_vals<long accum>("%*.*lk", raw_num, prec, width);
125   if (result != TestResult::Success)
126     __builtin_trap();
127 
128   result = test_vals<unsigned long accum>("%*.*lK", raw_num, prec, width);
129   if (result != TestResult::Success)
130     __builtin_trap();
131 
132   return 0;
133 }
134