xref: /llvm-project/libc/fuzzing/stdio/printf_fixed_conv_fuzz.cpp (revision 73aab2f69773324ef0250f093bd4d782beee921a)
18e3b6054SMichael Jones //===-- printf_fixed_conv_fuzz.cpp ----------------------------------------===//
28e3b6054SMichael Jones //
38e3b6054SMichael Jones // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48e3b6054SMichael Jones // See https://llvm.org/LICENSE.txt for license information.
58e3b6054SMichael Jones // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68e3b6054SMichael Jones //
78e3b6054SMichael Jones //===----------------------------------------------------------------------===//
88e3b6054SMichael Jones ///
98e3b6054SMichael Jones /// Fuzzing test for llvm-libc printf %f/e/g/a implementations.
108e3b6054SMichael Jones ///
118e3b6054SMichael Jones //===----------------------------------------------------------------------===//
128e3b6054SMichael Jones #include "src/stdio/snprintf.h"
138e3b6054SMichael Jones 
14*73aab2f6Slntue #include "include/llvm-libc-macros/stdfix-macros.h"
158e3b6054SMichael Jones #include "src/__support/fixed_point/fx_bits.h"
168e3b6054SMichael Jones #include "src/__support/fixed_point/fx_rep.h"
178e3b6054SMichael Jones 
188e3b6054SMichael Jones #include <stddef.h>
198e3b6054SMichael Jones #include <stdint.h>
208e3b6054SMichael Jones 
218e3b6054SMichael Jones #include "utils/MPFRWrapper/mpfr_inc.h"
228e3b6054SMichael Jones 
238e3b6054SMichael Jones constexpr int MAX_SIZE = 10000;
248e3b6054SMichael Jones 
simple_streq(char * first,char * second,int length)258e3b6054SMichael Jones inline bool simple_streq(char *first, char *second, int length) {
268e3b6054SMichael Jones   for (int i = 0; i < length; ++i)
278e3b6054SMichael Jones     if (first[i] != second[i])
288e3b6054SMichael Jones       return false;
298e3b6054SMichael Jones 
308e3b6054SMichael Jones   return true;
318e3b6054SMichael Jones }
328e3b6054SMichael Jones 
clamp(int num,int max)338e3b6054SMichael Jones inline int clamp(int num, int max) {
348e3b6054SMichael Jones   if (num > max)
358e3b6054SMichael Jones     return max;
368e3b6054SMichael Jones   if (num < -max)
378e3b6054SMichael Jones     return -max;
388e3b6054SMichael Jones   return num;
398e3b6054SMichael Jones }
408e3b6054SMichael Jones 
418e3b6054SMichael Jones enum class TestResult {
428e3b6054SMichael Jones   Success,
438e3b6054SMichael Jones   BufferSizeFailed,
448e3b6054SMichael Jones   LengthsDiffer,
458e3b6054SMichael Jones   StringsNotEqual,
468e3b6054SMichael Jones };
478e3b6054SMichael Jones 
488e3b6054SMichael Jones template <typename F>
test_vals(const char * fmt,uint64_t num,int prec,int width)498e3b6054SMichael Jones inline TestResult test_vals(const char *fmt, uint64_t num, int prec,
508e3b6054SMichael Jones                             int width) {
518e3b6054SMichael Jones   typename LIBC_NAMESPACE::fixed_point::FXRep<F>::StorageType raw_num = num;
528e3b6054SMichael Jones 
538e3b6054SMichael Jones   auto raw_num_bits = LIBC_NAMESPACE::fixed_point::FXBits<F>(raw_num);
548e3b6054SMichael Jones 
558e3b6054SMichael Jones   // This needs to be a float with enough bits of precision to hold the fixed
568e3b6054SMichael Jones   // point number.
578e3b6054SMichael Jones   static_assert(sizeof(long double) > sizeof(long accum));
588e3b6054SMichael Jones 
598e3b6054SMichael Jones   // build a long double that is equivalent to the fixed point number.
608e3b6054SMichael Jones   long double ld_num =
618e3b6054SMichael Jones       static_cast<long double>(raw_num_bits.get_integral()) +
628e3b6054SMichael Jones       (static_cast<long double>(raw_num_bits.get_fraction()) /
638e3b6054SMichael Jones        static_cast<long double>(1ll << raw_num_bits.get_exponent()));
648e3b6054SMichael Jones 
658e3b6054SMichael Jones   if (raw_num_bits.get_sign())
668e3b6054SMichael Jones     ld_num = -ld_num;
678e3b6054SMichael Jones 
688e3b6054SMichael Jones   // Call snprintf on a nullptr to get the buffer size.
698e3b6054SMichael Jones   int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num);
708e3b6054SMichael Jones 
718e3b6054SMichael Jones   if (buffer_size < 0)
728e3b6054SMichael Jones     return TestResult::BufferSizeFailed;
738e3b6054SMichael Jones 
748e3b6054SMichael Jones   char *test_buff = new char[buffer_size + 1];
758e3b6054SMichael Jones   char *reference_buff = new char[buffer_size + 1];
768e3b6054SMichael Jones 
778e3b6054SMichael Jones   int test_result = 0;
788e3b6054SMichael Jones   int reference_result = 0;
798e3b6054SMichael Jones 
808e3b6054SMichael Jones   test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width,
818e3b6054SMichael Jones                                          prec, num);
828e3b6054SMichael Jones 
838e3b6054SMichael Jones   // The fixed point format is defined to be %f equivalent.
848e3b6054SMichael Jones   reference_result = mpfr_snprintf(reference_buff, buffer_size + 1, "%*.*Lf",
858e3b6054SMichael Jones                                    width, prec, ld_num);
868e3b6054SMichael Jones 
878e3b6054SMichael Jones   // All of these calls should return that they wrote the same amount.
888e3b6054SMichael Jones   if (test_result != reference_result || test_result != buffer_size)
898e3b6054SMichael Jones     return TestResult::LengthsDiffer;
908e3b6054SMichael Jones 
918e3b6054SMichael Jones   if (!simple_streq(test_buff, reference_buff, buffer_size))
928e3b6054SMichael Jones     return TestResult::StringsNotEqual;
938e3b6054SMichael Jones 
948e3b6054SMichael Jones   delete[] test_buff;
958e3b6054SMichael Jones   delete[] reference_buff;
968e3b6054SMichael Jones   return TestResult::Success;
978e3b6054SMichael Jones }
988e3b6054SMichael Jones 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)998e3b6054SMichael Jones extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
1008e3b6054SMichael Jones   // const uint8_t raw_data[] = {0x8d,0x43,0x40,0x0,0x0,0x0,};
1018e3b6054SMichael Jones   // data = raw_data;
1028e3b6054SMichael Jones   // size = sizeof(raw_data);
1038e3b6054SMichael Jones   int prec = 0;
1048e3b6054SMichael Jones   int width = 0;
1058e3b6054SMichael Jones 
1068e3b6054SMichael Jones   LIBC_NAMESPACE::fixed_point::FXRep<long accum>::StorageType raw_num = 0;
1078e3b6054SMichael Jones 
1088e3b6054SMichael Jones   // Copy as many bytes of data as will fit into num, prec, and with. Any extras
1098e3b6054SMichael Jones   // are ignored.
1108e3b6054SMichael Jones   for (size_t cur = 0; cur < size; ++cur) {
1118e3b6054SMichael Jones     if (cur < sizeof(raw_num)) {
1128e3b6054SMichael Jones       raw_num = (raw_num << 8) + data[cur];
1138e3b6054SMichael Jones     } else if (cur < sizeof(raw_num) + sizeof(prec)) {
1148e3b6054SMichael Jones       prec = (prec << 8) + data[cur];
1158e3b6054SMichael Jones     } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
1168e3b6054SMichael Jones       width = (width << 8) + data[cur];
1178e3b6054SMichael Jones     }
1188e3b6054SMichael Jones   }
1198e3b6054SMichael Jones 
1208e3b6054SMichael Jones   width = clamp(width, MAX_SIZE);
1218e3b6054SMichael Jones   prec = clamp(prec, MAX_SIZE);
1228e3b6054SMichael Jones 
1238e3b6054SMichael Jones   TestResult result;
1248e3b6054SMichael Jones   result = test_vals<long accum>("%*.*lk", raw_num, prec, width);
1258e3b6054SMichael Jones   if (result != TestResult::Success)
1268e3b6054SMichael Jones     __builtin_trap();
1278e3b6054SMichael Jones 
1288e3b6054SMichael Jones   result = test_vals<unsigned long accum>("%*.*lK", raw_num, prec, width);
1298e3b6054SMichael Jones   if (result != TestResult::Success)
1308e3b6054SMichael Jones     __builtin_trap();
1318e3b6054SMichael Jones 
1328e3b6054SMichael Jones   return 0;
1338e3b6054SMichael Jones }
134