1 //===-- printf_float_conv_fuzz.cpp ----------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 /// Fuzzing test for llvm-libc printf %f/e/g/a implementations.
11 //===----------------------------------------------------------------------===//
12 #include "src/stdio/snprintf.h"
14 #include "src/__support/FPUtil/FPBits.h"
19 #include "utils/MPFRWrapper/mpfr_inc.h"
21 constexpr int MAX_SIZE
= 10000;
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
]) {
32 inline int simple_strlen(const char *str
) {
34 for (; *str
; ++str
, ++i
) {
40 enum class TestResult
{
48 inline TestResult
test_vals(const char *fmt
, F num
, int prec
, int width
) {
49 // Call snprintf on a nullptr to get the buffer size.
50 int buffer_size
= LIBC_NAMESPACE::snprintf(nullptr, 0, fmt
, width
, prec
, num
);
52 if (buffer_size
< 0) {
53 return TestResult::BufferSizeFailed
;
56 char *test_buff
= new char[buffer_size
+ 1];
57 char *reference_buff
= new char[buffer_size
+ 1];
60 int reference_result
= 0;
62 test_result
= LIBC_NAMESPACE::snprintf(test_buff
, buffer_size
+ 1, fmt
, width
,
65 mpfr_snprintf(reference_buff
, buffer_size
+ 1, fmt
, width
, prec
, num
);
67 // All of these calls should return that they wrote the same amount.
68 if (test_result
!= reference_result
|| test_result
!= buffer_size
) {
69 return TestResult::LengthsDiffer
;
72 if (!simple_streq(test_buff
, reference_buff
, buffer_size
)) {
73 return TestResult::StringsNotEqual
;
77 delete[] reference_buff
;
78 return TestResult::Success
;
81 constexpr char const *fmt_arr
[] = {
82 "%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",
85 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data
, size_t size
) {
86 // const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};
88 // size = sizeof(raw_data);
93 LIBC_NAMESPACE::fputil::FPBits
<double>::StorageType raw_num
= 0;
95 // Copy as many bytes of data as will fit into num, prec, and with. Any extras
97 for (size_t cur
= 0; cur
< size
; ++cur
) {
98 if (cur
< sizeof(raw_num
)) {
99 raw_num
= (raw_num
<< 8) + data
[cur
];
100 } else if (cur
< sizeof(raw_num
) + sizeof(prec
)) {
101 prec
= (prec
<< 8) + data
[cur
];
102 } else if (cur
< sizeof(raw_num
) + sizeof(prec
) + sizeof(width
)) {
103 width
= (width
<< 8) + data
[cur
];
107 num
= LIBC_NAMESPACE::fputil::FPBits
<double>(raw_num
).get_val();
109 // While we could create a "ld_raw_num" from additional bytes, it's much
110 // easier to stick with simply casting num to long double. This avoids the
111 // issues around 80 bit long doubles, especially unnormal and pseudo-denormal
112 // numbers, which MPFR doesn't handle well.
113 long double ld_num
= static_cast<long double>(num
);
115 if (width
> MAX_SIZE
) {
117 } else if (width
< -MAX_SIZE
) {
121 if (prec
> MAX_SIZE
) {
123 } else if (prec
< -MAX_SIZE
) {
127 for (size_t cur_fmt
= 0; cur_fmt
< sizeof(fmt_arr
) / sizeof(char *);
129 int fmt_len
= simple_strlen(fmt_arr
[cur_fmt
]);
131 if (fmt_arr
[cur_fmt
][fmt_len
- 2] == 'L') {
132 result
= test_vals
<long double>(fmt_arr
[cur_fmt
], ld_num
, prec
, width
);
134 result
= test_vals
<double>(fmt_arr
[cur_fmt
], num
, prec
, width
);
136 if (result
!= TestResult::Success
) {