Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc] Add simple long double to printf float fuzz #68449

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions libc/fuzzing/stdio/printf_float_conv_fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,23 @@ inline bool simple_streq(char *first, char *second, int length) {
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,
};

inline TestResult test_vals(const char *fmt, double num, int prec, int width) {
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);

Expand Down Expand Up @@ -70,10 +79,7 @@ inline TestResult test_vals(const char *fmt, double num, int prec, int width) {
}

constexpr char const *fmt_arr[] = {
"%*.*f",
"%*.*e",
"%*.*g",
"%*.*a",
"%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",
};

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
Expand All @@ -100,6 +106,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

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) {
Expand All @@ -114,7 +126,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
++cur_fmt) {
TestResult result = test_vals(fmt_arr[cur_fmt], num, prec, width);
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();
}
Expand Down
5 changes: 3 additions & 2 deletions libc/src/stdio/printf_core/float_hex_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ LIBC_INLINE int convert_float_hex_exp(Writer *writer,

// This is to handle situations where the mantissa isn't an even number of hex
// digits. This is primarily relevant for x86 80 bit long doubles, which have
// 63 bit mantissas.
if (mantissa_width % BITS_IN_HEX_DIGIT != 0) {
// 63 bit mantissas. In the case where the mantissa is 0, however, the
// exponent should stay as 0.
if (mantissa_width % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) {
exponent -= mantissa_width % BITS_IN_HEX_DIGIT;
}

Expand Down
3 changes: 3 additions & 0 deletions libc/test/src/stdio/sprintf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,9 @@ TEST_F(LlvmLibcSPrintfTest, FloatHexExpConv) {
written = LIBC_NAMESPACE::sprintf(buff, "%.5a", nan);
ASSERT_STREQ_LEN(written, buff, "nan");

written = LIBC_NAMESPACE::sprintf(buff, "%La", 0.0L);
ASSERT_STREQ_LEN(written, buff, "0x0p+0");

written = LIBC_NAMESPACE::sprintf(buff, "%.1La", 0.1L);
#if defined(SPECIAL_X86_LONG_DOUBLE)
ASSERT_STREQ_LEN(written, buff, "0xc.dp-7");
Expand Down