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
21constexpr int MAX_SIZE = 10000;
22
23inline 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
32inline int simple_strlen(const char *str) {
33 int i = 0;
34 for (; *str; ++str, ++i) {
35 ;
36 }
37 return i;
38}
39
40enum class TestResult {
41 Success,
42 BufferSizeFailed,
43 LengthsDiffer,
44 StringsNotEqual,
45};
46
47template <typename F>
48inline 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(buffer: nullptr, buffsz: 0, format: fmt, width, prec, num);
51
52 if (buffer_size < 0) {
53 return TestResult::BufferSizeFailed;
54 }
55
56 char *test_buff = new char[buffer_size + 1];
57 char *reference_buff = new char[buffer_size + 1];
58
59 int test_result = 0;
60 int reference_result = 0;
61
62 test_result = LIBC_NAMESPACE::snprintf(buffer: test_buff, buffsz: buffer_size + 1, format: fmt, width,
63 prec, num);
64 reference_result =
65 mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);
66
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;
70 }
71
72 if (!simple_streq(first: test_buff, second: reference_buff, length: buffer_size)) {
73 return TestResult::StringsNotEqual;
74 }
75
76 delete[] test_buff;
77 delete[] reference_buff;
78 return TestResult::Success;
79}
80
81constexpr char const *fmt_arr[] = {
82 "%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",
83};
84
85extern "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};
87 // data = raw_data;
88 // size = sizeof(raw_data);
89 double num = 0.0;
90 int prec = 0;
91 int width = 0;
92
93 LIBC_NAMESPACE::fputil::FPBits<double>::StorageType raw_num = 0;
94
95 // Copy as many bytes of data as will fit into num, prec, and with. Any extras
96 // are ignored.
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];
104 }
105 }
106
107 num = LIBC_NAMESPACE::fputil::FPBits<double>(raw_num).get_val();
108
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);
114
115 if (width > MAX_SIZE) {
116 width = MAX_SIZE;
117 } else if (width < -MAX_SIZE) {
118 width = -MAX_SIZE;
119 }
120
121 if (prec > MAX_SIZE) {
122 prec = MAX_SIZE;
123 } else if (prec < -MAX_SIZE) {
124 prec = -MAX_SIZE;
125 }
126
127 for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
128 ++cur_fmt) {
129 int fmt_len = simple_strlen(str: fmt_arr[cur_fmt]);
130 TestResult result;
131 if (fmt_arr[cur_fmt][fmt_len - 2] == 'L') {
132 result = test_vals<long double>(fmt: fmt_arr[cur_fmt], num: ld_num, prec, width);
133 } else {
134 result = test_vals<double>(fmt: fmt_arr[cur_fmt], num, prec, width);
135 }
136 if (result != TestResult::Success) {
137 __builtin_trap();
138 }
139 }
140 return 0;
141}
142

source code of libc/fuzzing/stdio/printf_float_conv_fuzz.cpp