1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
//===-- printf_float_conv_fuzz.cpp ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Fuzzing test for llvm-libc printf %f/e/g/a implementations.
///
//===----------------------------------------------------------------------===//
#include "src/stdio/snprintf.h"
#include "src/__support/FPUtil/FPBits.h"
#include <stddef.h>
#include <stdint.h>
#include "utils/MPFRWrapper/mpfr_inc.h"
constexpr int MAX_SIZE = 10000;
inline bool simple_streq(char *first, char *second, int length) {
for (int i = 0; i < length; ++i) {
if (first[i] != second[i]) {
return false;
}
}
return true;
}
inline int simple_strlen(const char *str) {
int i = 0;
for (; *str; ++str, ++i) {
;
}
return i;
}
enum class TestResult {
Success,
BufferSizeFailed,
LengthsDiffer,
StringsNotEqual,
};
template <typename F>
inline TestResult test_vals(const char *fmt, F num, int prec, int width) {
// Call snprintf on a nullptr to get the buffer size.
int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num);
if (buffer_size < 0) {
return TestResult::BufferSizeFailed;
}
char *test_buff = new char[buffer_size + 1];
char *reference_buff = new char[buffer_size + 1];
int test_result = 0;
int reference_result = 0;
test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width,
prec, num);
reference_result =
mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);
// All of these calls should return that they wrote the same amount.
if (test_result != reference_result || test_result != buffer_size) {
return TestResult::LengthsDiffer;
}
if (!simple_streq(test_buff, reference_buff, buffer_size)) {
return TestResult::StringsNotEqual;
}
delete[] test_buff;
delete[] reference_buff;
return TestResult::Success;
}
constexpr char const *fmt_arr[] = {
"%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};
// data = raw_data;
// size = sizeof(raw_data);
double num = 0.0;
int prec = 0;
int width = 0;
LIBC_NAMESPACE::fputil::FPBits<double>::StorageType raw_num = 0;
// Copy as many bytes of data as will fit into num, prec, and with. Any extras
// are ignored.
for (size_t cur = 0; cur < size; ++cur) {
if (cur < sizeof(raw_num)) {
raw_num = (raw_num << 8) + data[cur];
} else if (cur < sizeof(raw_num) + sizeof(prec)) {
prec = (prec << 8) + data[cur];
} else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
width = (width << 8) + data[cur];
}
}
num = LIBC_NAMESPACE::fputil::FPBits<double>(raw_num).get_val();
// While we could create a "ld_raw_num" from additional bytes, it's much
// easier to stick with simply casting num to long double. This avoids the
// issues around 80 bit long doubles, especially unnormal and pseudo-denormal
// numbers, which MPFR doesn't handle well.
long double ld_num = static_cast<long double>(num);
if (width > MAX_SIZE) {
width = MAX_SIZE;
} else if (width < -MAX_SIZE) {
width = -MAX_SIZE;
}
if (prec > MAX_SIZE) {
prec = MAX_SIZE;
} else if (prec < -MAX_SIZE) {
prec = -MAX_SIZE;
}
for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
++cur_fmt) {
int fmt_len = simple_strlen(fmt_arr[cur_fmt]);
TestResult result;
if (fmt_arr[cur_fmt][fmt_len - 2] == 'L') {
result = test_vals<long double>(fmt_arr[cur_fmt], ld_num, prec, width);
} else {
result = test_vals<double>(fmt_arr[cur_fmt], num, prec, width);
}
if (result != TestResult::Success) {
__builtin_trap();
}
}
return 0;
}
|