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