mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-01 05:46:07 +00:00

This one might be a bit controversial since the terminology has been introduced from the start but I think `FRACTION_LEN` is a better name here. AFAICT it really is "the number of bits after the decimal dot when the number is in normal form." `MANTISSA_WIDTH` is less precise as it's unclear whether we take the leading bit into account. This patch also renames most of the properties to use the `_LEN` suffix and fixes useless casts or variables.
142 lines
4.0 KiB
C++
142 lines
4.0 KiB
C++
//===-- 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;
|
|
}
|