| 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 | |
| 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 | |
| 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> |
| 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(first: test_buff, second: reference_buff, length: buffer_size)) |
| 92 | return TestResult::StringsNotEqual; |
| 93 | |
| 94 | delete[] test_buff; |
| 95 | delete[] reference_buff; |
| 96 | return TestResult::Success; |
| 97 | } |
| 98 | |
| 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(num: width, max: MAX_SIZE); |
| 121 | prec = clamp(num: prec, max: 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 | |