forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 334
/
Copy pathprintf_float_conv_fuzz.cpp
141 lines (117 loc) · 4.03 KB
/
printf_float_conv_fuzz.cpp
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;
}