1//===-- printf_fixed_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 "include/llvm-libc-macros/stdfix-macros.h"
15#include "src/__support/fixed_point/fx_bits.h"
16#include "src/__support/fixed_point/fx_rep.h"
17
18#include <stddef.h>
19#include <stdint.h>
20
21#include "utils/MPFRWrapper/mpfr_inc.h"
22
23constexpr int MAX_SIZE = 10000;
24
25inline bool simple_streq(char *first, char *second, int length) {
26 for (int i = 0; i < length; ++i)
27 if (first[i] != second[i])
28 return false;
29
30 return true;
31}
32
33inline int clamp(int num, int max) {
34 if (num > max)
35 return max;
36 if (num < -max)
37 return -max;
38 return num;
39}
40
41enum class TestResult {
42 Success,
43 BufferSizeFailed,
44 LengthsDiffer,
45 StringsNotEqual,
46};
47
48template <typename F>
49inline TestResult test_vals(const char *fmt, uint64_t num, int prec,
50 int width) {
51 typename LIBC_NAMESPACE::fixed_point::FXRep<F>::StorageType raw_num = num;
52
53 auto raw_num_bits = LIBC_NAMESPACE::fixed_point::FXBits<F>(raw_num);
54
55 // This needs to be a float with enough bits of precision to hold the fixed
56 // point number.
57 static_assert(sizeof(long double) > sizeof(long accum));
58
59 // build a long double that is equivalent to the fixed point number.
60 long double ld_num =
61 static_cast<long double>(raw_num_bits.get_integral()) +
62 (static_cast<long double>(raw_num_bits.get_fraction()) /
63 static_cast<long double>(1ll << raw_num_bits.get_exponent()));
64
65 if (raw_num_bits.get_sign())
66 ld_num = -ld_num;
67
68 // Call snprintf on a nullptr to get the buffer size.
69 int buffer_size = LIBC_NAMESPACE::snprintf(buffer: nullptr, buffsz: 0, format: fmt, width, prec, num);
70
71 if (buffer_size < 0)
72 return TestResult::BufferSizeFailed;
73
74 char *test_buff = new char[buffer_size + 1];
75 char *reference_buff = new char[buffer_size + 1];
76
77 int test_result = 0;
78 int reference_result = 0;
79
80 test_result = LIBC_NAMESPACE::snprintf(buffer: test_buff, buffsz: buffer_size + 1, format: fmt, width,
81 prec, num);
82
83 // The fixed point format is defined to be %f equivalent.
84 reference_result = mpfr_snprintf(reference_buff, buffer_size + 1, "%*.*Lf",
85 width, prec, ld_num);
86
87 // All of these calls should return that they wrote the same amount.
88 if (test_result != reference_result || test_result != buffer_size)
89 return TestResult::LengthsDiffer;
90
91 if (!simple_streq(first: test_buff, second: reference_buff, length: buffer_size))
92 return TestResult::StringsNotEqual;
93
94 delete[] test_buff;
95 delete[] reference_buff;
96 return TestResult::Success;
97}
98
99extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
100 // const uint8_t raw_data[] = {0x8d,0x43,0x40,0x0,0x0,0x0,};
101 // data = raw_data;
102 // size = sizeof(raw_data);
103 int prec = 0;
104 int width = 0;
105
106 LIBC_NAMESPACE::fixed_point::FXRep<long accum>::StorageType raw_num = 0;
107
108 // Copy as many bytes of data as will fit into num, prec, and with. Any extras
109 // are ignored.
110 for (size_t cur = 0; cur < size; ++cur) {
111 if (cur < sizeof(raw_num)) {
112 raw_num = (raw_num << 8) + data[cur];
113 } else if (cur < sizeof(raw_num) + sizeof(prec)) {
114 prec = (prec << 8) + data[cur];
115 } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
116 width = (width << 8) + data[cur];
117 }
118 }
119
120 width = clamp(num: width, max: MAX_SIZE);
121 prec = clamp(num: prec, max: MAX_SIZE);
122
123 TestResult result;
124 result = test_vals<long accum>("%*.*lk", raw_num, prec, width);
125 if (result != TestResult::Success)
126 __builtin_trap();
127
128 result = test_vals<unsigned long accum>("%*.*lK", raw_num, prec, width);
129 if (result != TestResult::Success)
130 __builtin_trap();
131
132 return 0;
133}
134

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