[libc] Add simple long double to printf float fuzz (#68449)
Recent testing has uncovered some hard-to-find bugs in printf's long double support. This patch adds an extra long double path to the fuzzer with minimal extra effort. While a more thorough long double fuzzer would be useful, it would need to handle the non-standard cases of 80 bit long doubles such as unnormal and pseudo-denormal numbers. For that reason, a standalone long double fuzzer is left for future development.
This commit is contained in:
@@ -29,6 +29,14 @@ 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,
|
||||
@@ -36,7 +44,8 @@ enum class TestResult {
|
||||
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);
|
||||
|
||||
@@ -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) {
|
||||
@@ -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) {
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user