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 | |
23 | constexpr int MAX_SIZE = 10000; |
24 | |
25 | inline 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 | |
33 | inline 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 | |
41 | enum class TestResult { |
42 | Success, |
43 | BufferSizeFailed, |
44 | LengthsDiffer, |
45 | StringsNotEqual, |
46 | }; |
47 | |
48 | template <typename F> |
49 | inline 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 | |
99 | extern "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 | |