Skip to content

Commit e7866ea

Browse files
[libc] Add fuzzing for printf floats
To guarantee accuracy for all potential float values, this patch adds a fuzzer to compare the results for float conversions from our printf against MPFR's. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D156495
1 parent cd06b9d commit e7866ea

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

libc/fuzzing/stdio/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ add_libc_fuzzer(
77
COMPILE_OPTIONS
88
-DLIBC_COPT_MOCK_ARG_LIST
99
)
10+
11+
add_libc_fuzzer(
12+
printf_float_conv_fuzz
13+
NEED_MPFR
14+
SRCS
15+
printf_float_conv_fuzz.cpp
16+
DEPENDS
17+
libc.src.stdio.snprintf
18+
libc.src.__support.FPUtil.fp_bits
19+
)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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+
}

0 commit comments

Comments
 (0)