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 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> 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 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>::UIntType 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