Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2025 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | //////////////////////////////////////////////////////////////////////////////// |
16 | | #include <stddef.h> |
17 | | #include <stdint.h> |
18 | | #include <string.h> |
19 | | #include <vector> |
20 | | #include <algorithm> |
21 | | |
22 | | #include <fuzzer/FuzzedDataProvider.h> |
23 | | extern "C" { |
24 | | #include "md5_int.h" |
25 | | #include "sha256_int.h" |
26 | | #include "sha512_256_int.h" |
27 | | } |
28 | | |
29 | | // Fuzzing target function pointer types for the internal hash APIs |
30 | | template <typename HashType> using InitFn = void (*)(HashType*); |
31 | | template <typename HashType> using UpdateFn = void (*)(HashType*, size_t, const uint8_t*); |
32 | | template <typename HashType> using FinishFn = void (*)(HashType*, uint8_t*); |
33 | | |
34 | | // Generic hashing flow that fuzz same hashing procedure for different algorithm |
35 | | template <typename HashType> |
36 | | static void fuzz_hash_int_multi(FuzzedDataProvider &fdp, |
37 | | size_t block_size, |
38 | | InitFn<HashType> init_fn, |
39 | | UpdateFn<HashType> update_fn, |
40 | | FinishFn<HashType> finish_fn, |
41 | 4.40k | size_t digest_size) { |
42 | 4.40k | if (!fdp.remaining_bytes()) { |
43 | 39 | return; |
44 | 39 | } |
45 | | |
46 | | // Pull a random slice of data for fuzzing |
47 | 4.36k | size_t take_len = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); |
48 | 4.36k | std::vector<uint8_t> input_bytes = fdp.ConsumeBytes<uint8_t>(take_len); |
49 | | |
50 | | // Create 1 to 4 independent hashing contexts with it own digest buffer |
51 | 4.36k | const unsigned num_contexts = fdp.ConsumeIntegralInRange<unsigned>(1, 4); |
52 | 4.36k | std::vector<HashType> contexts(num_contexts); |
53 | 4.36k | std::vector<std::vector<uint8_t>> digests(num_contexts, std::vector<uint8_t>(digest_size)); |
54 | 15.1k | for (unsigned i = 0; i < num_contexts; i++) { |
55 | 10.8k | init_fn(&contexts[i]); |
56 | 10.8k | } |
57 | | |
58 | | // Intentionally misalign the data pointer to stress alignment sensitive paths |
59 | 4.36k | const size_t misalign_pad = fdp.ConsumeIntegralInRange<size_t>(0, 64); |
60 | 4.36k | std::vector<uint8_t> scratch_buf(misalign_pad + input_bytes.size()); |
61 | 4.36k | if (!input_bytes.empty()) { |
62 | 3.43k | memcpy(scratch_buf.data() + misalign_pad, input_bytes.data(), input_bytes.size()); |
63 | 3.43k | } |
64 | | |
65 | | // Define cursor and remaining bytes counter to keep track of the multiple hash update iterations |
66 | 4.36k | const uint8_t *cursor = scratch_buf.data() + misalign_pad; |
67 | 4.36k | size_t remaining = input_bytes.size(); |
68 | | |
69 | | // Perform multiple hash update iterations on the raw data |
70 | 4.36k | unsigned num_iterations = fdp.ConsumeIntegralInRange<unsigned>(1, 4); |
71 | 12.5k | while (num_iterations-- && remaining > 0) { |
72 | | // Pick which context to feed this iteration |
73 | 8.17k | const unsigned ctx_index = (num_contexts == 1) ? 0 : fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); |
74 | | |
75 | | // Choose a chunking pattern for this iteration |
76 | 8.17k | enum Pattern { LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT }; |
77 | 8.17k | Pattern pattern = fdp.PickValueInArray<Pattern>({LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT}); |
78 | | |
79 | 8.17k | size_t chunk_len = 0; |
80 | 8.17k | switch (pattern) { |
81 | 1.74k | case LESS1: { |
82 | | // Consume 1 byte less from block size from the raw data for this iteration |
83 | 1.74k | if (block_size > 1) { |
84 | 1.74k | chunk_len = std::min(remaining, block_size - 1); |
85 | 1.74k | } |
86 | 1.74k | break; |
87 | 0 | } |
88 | 469 | case EQ: { |
89 | | // Consume block size bytes from the raw data for this iteration |
90 | 469 | chunk_len = std::min(remaining, block_size); |
91 | 469 | break; |
92 | 0 | } |
93 | 469 | case PLUS1: { |
94 | | // Consume 1 byte more from block size from the raw data for this iteration |
95 | 469 | chunk_len = std::min(remaining, block_size + 1); |
96 | 469 | break; |
97 | 0 | } |
98 | 1.33k | case SMALL: { |
99 | | // Consume 1 to 32 bytes from the raw data for this iteration |
100 | 1.33k | size_t small_len = (size_t)fdp.ConsumeIntegralInRange<int>(1, 32); |
101 | 1.33k | chunk_len = std::min(remaining, small_len); |
102 | 1.33k | break; |
103 | 0 | } |
104 | 1.54k | case RANDOM: { |
105 | | // Consume random bytes from the raw data for this iteration |
106 | 1.54k | chunk_len = (remaining >= 1) ? (size_t)fdp.ConsumeIntegralInRange<size_t>(1, remaining) : 0; |
107 | 1.54k | break; |
108 | 0 | } |
109 | 310 | case TAIL: { |
110 | | // Consume all remaining bytes from the raw data for this iteration |
111 | 310 | chunk_len = remaining; |
112 | 310 | break; |
113 | 0 | } |
114 | 2.30k | case HALT: { |
115 | | // Consume small chunk and consider reinitialisation or early halt of the hash iteration |
116 | 2.30k | size_t step = std::max<size_t>(1, fdp.ConsumeIntegralInRange<size_t>(1, block_size)); |
117 | 2.30k | size_t loops = fdp.ConsumeIntegralInRange<size_t>(1, 4); |
118 | 7.69k | for (size_t j = 0; j < loops && remaining > 0; j++) { |
119 | 5.39k | size_t w = std::min(remaining, step); |
120 | 5.39k | update_fn(&contexts[ctx_index], w, cursor); |
121 | 5.39k | cursor += w; |
122 | 5.39k | remaining -= w; |
123 | 5.39k | } |
124 | | |
125 | | // Randomly reinitialise the hash stream |
126 | 2.30k | if (fdp.ConsumeBool()) { |
127 | 1.51k | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); |
128 | 1.51k | init_fn(&contexts[ctx_index]); |
129 | 1.51k | } |
130 | 2.30k | continue; |
131 | 0 | } |
132 | 8.17k | } |
133 | 5.86k | if (chunk_len == 0 || chunk_len > remaining) { |
134 | 0 | continue; |
135 | 0 | } |
136 | | |
137 | | // Occasionally reinitialise a context between update iterations |
138 | 5.86k | if (fdp.ConsumeBool()) { |
139 | 2.86k | init_fn(&contexts[ctx_index]); |
140 | 2.86k | } |
141 | | |
142 | | // Fuzz the update function |
143 | 5.86k | update_fn(&contexts[ctx_index], chunk_len, cursor); |
144 | 5.86k | cursor += chunk_len; |
145 | 5.86k | remaining -= chunk_len; |
146 | | |
147 | | // Randomly halt and reinitialise the stream |
148 | 5.86k | if (fdp.ConsumeBool()) { |
149 | 2.85k | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); |
150 | 2.85k | init_fn(&contexts[ctx_index]); |
151 | 2.85k | } |
152 | 5.86k | } |
153 | | |
154 | | // Fuzz the finish function for all contexts |
155 | 15.1k | for (unsigned i = 0; i < num_contexts; i++) { |
156 | 10.8k | finish_fn(&contexts[i], digests[i].data()); |
157 | 10.8k | } |
158 | | |
159 | | // Additional fuzzing on special context chaining approach |
160 | 4.36k | if (num_contexts >= 2 && digest_size && fdp.ConsumeBool()) { |
161 | 1.42k | unsigned src_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); |
162 | 1.42k | unsigned dst_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); |
163 | 1.42k | if (src_idx != dst_idx) { |
164 | 532 | init_fn(&contexts[dst_idx]); |
165 | 532 | size_t offset = fdp.ConsumeIntegralInRange<size_t>(0, digest_size - 1); |
166 | 532 | size_t feed_len = std::min(digest_size - offset, |
167 | 532 | (size_t)fdp.ConsumeIntegralInRange<size_t>(1, digest_size)); |
168 | 532 | update_fn(&contexts[dst_idx], feed_len, digests[src_idx].data() + offset); |
169 | 532 | finish_fn(&contexts[dst_idx], digests[dst_idx].data()); |
170 | 532 | } |
171 | 1.42k | } |
172 | 4.36k | } fuzz_crypto_int.cpp:void fuzz_hash_int_multi<mhd_Md5CtxInt>(FuzzedDataProvider&, unsigned long, void (*)(mhd_Md5CtxInt*), void (*)(mhd_Md5CtxInt*, unsigned long, unsigned char const*), void (*)(mhd_Md5CtxInt*, unsigned char*), unsigned long) Line | Count | Source | 41 | 1.65k | size_t digest_size) { | 42 | 1.65k | if (!fdp.remaining_bytes()) { | 43 | 31 | return; | 44 | 31 | } | 45 | | | 46 | | // Pull a random slice of data for fuzzing | 47 | 1.62k | size_t take_len = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); | 48 | 1.62k | std::vector<uint8_t> input_bytes = fdp.ConsumeBytes<uint8_t>(take_len); | 49 | | | 50 | | // Create 1 to 4 independent hashing contexts with it own digest buffer | 51 | 1.62k | const unsigned num_contexts = fdp.ConsumeIntegralInRange<unsigned>(1, 4); | 52 | 1.62k | std::vector<HashType> contexts(num_contexts); | 53 | 1.62k | std::vector<std::vector<uint8_t>> digests(num_contexts, std::vector<uint8_t>(digest_size)); | 54 | 5.68k | for (unsigned i = 0; i < num_contexts; i++) { | 55 | 4.06k | init_fn(&contexts[i]); | 56 | 4.06k | } | 57 | | | 58 | | // Intentionally misalign the data pointer to stress alignment sensitive paths | 59 | 1.62k | const size_t misalign_pad = fdp.ConsumeIntegralInRange<size_t>(0, 64); | 60 | 1.62k | std::vector<uint8_t> scratch_buf(misalign_pad + input_bytes.size()); | 61 | 1.62k | if (!input_bytes.empty()) { | 62 | 1.28k | memcpy(scratch_buf.data() + misalign_pad, input_bytes.data(), input_bytes.size()); | 63 | 1.28k | } | 64 | | | 65 | | // Define cursor and remaining bytes counter to keep track of the multiple hash update iterations | 66 | 1.62k | const uint8_t *cursor = scratch_buf.data() + misalign_pad; | 67 | 1.62k | size_t remaining = input_bytes.size(); | 68 | | | 69 | | // Perform multiple hash update iterations on the raw data | 70 | 1.62k | unsigned num_iterations = fdp.ConsumeIntegralInRange<unsigned>(1, 4); | 71 | 4.72k | while (num_iterations-- && remaining > 0) { | 72 | | // Pick which context to feed this iteration | 73 | 3.10k | const unsigned ctx_index = (num_contexts == 1) ? 0 : fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 74 | | | 75 | | // Choose a chunking pattern for this iteration | 76 | 3.10k | enum Pattern { LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT }; | 77 | 3.10k | Pattern pattern = fdp.PickValueInArray<Pattern>({LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT}); | 78 | | | 79 | 3.10k | size_t chunk_len = 0; | 80 | 3.10k | switch (pattern) { | 81 | 697 | case LESS1: { | 82 | | // Consume 1 byte less from block size from the raw data for this iteration | 83 | 697 | if (block_size > 1) { | 84 | 697 | chunk_len = std::min(remaining, block_size - 1); | 85 | 697 | } | 86 | 697 | break; | 87 | 0 | } | 88 | 142 | case EQ: { | 89 | | // Consume block size bytes from the raw data for this iteration | 90 | 142 | chunk_len = std::min(remaining, block_size); | 91 | 142 | break; | 92 | 0 | } | 93 | 233 | case PLUS1: { | 94 | | // Consume 1 byte more from block size from the raw data for this iteration | 95 | 233 | chunk_len = std::min(remaining, block_size + 1); | 96 | 233 | break; | 97 | 0 | } | 98 | 638 | case SMALL: { | 99 | | // Consume 1 to 32 bytes from the raw data for this iteration | 100 | 638 | size_t small_len = (size_t)fdp.ConsumeIntegralInRange<int>(1, 32); | 101 | 638 | chunk_len = std::min(remaining, small_len); | 102 | 638 | break; | 103 | 0 | } | 104 | 466 | case RANDOM: { | 105 | | // Consume random bytes from the raw data for this iteration | 106 | 466 | chunk_len = (remaining >= 1) ? (size_t)fdp.ConsumeIntegralInRange<size_t>(1, remaining) : 0; | 107 | 466 | break; | 108 | 0 | } | 109 | 103 | case TAIL: { | 110 | | // Consume all remaining bytes from the raw data for this iteration | 111 | 103 | chunk_len = remaining; | 112 | 103 | break; | 113 | 0 | } | 114 | 826 | case HALT: { | 115 | | // Consume small chunk and consider reinitialisation or early halt of the hash iteration | 116 | 826 | size_t step = std::max<size_t>(1, fdp.ConsumeIntegralInRange<size_t>(1, block_size)); | 117 | 826 | size_t loops = fdp.ConsumeIntegralInRange<size_t>(1, 4); | 118 | 2.77k | for (size_t j = 0; j < loops && remaining > 0; j++) { | 119 | 1.95k | size_t w = std::min(remaining, step); | 120 | 1.95k | update_fn(&contexts[ctx_index], w, cursor); | 121 | 1.95k | cursor += w; | 122 | 1.95k | remaining -= w; | 123 | 1.95k | } | 124 | | | 125 | | // Randomly reinitialise the hash stream | 126 | 826 | if (fdp.ConsumeBool()) { | 127 | 558 | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); | 128 | 558 | init_fn(&contexts[ctx_index]); | 129 | 558 | } | 130 | 826 | continue; | 131 | 0 | } | 132 | 3.10k | } | 133 | 2.27k | if (chunk_len == 0 || chunk_len > remaining) { | 134 | 0 | continue; | 135 | 0 | } | 136 | | | 137 | | // Occasionally reinitialise a context between update iterations | 138 | 2.27k | if (fdp.ConsumeBool()) { | 139 | 1.19k | init_fn(&contexts[ctx_index]); | 140 | 1.19k | } | 141 | | | 142 | | // Fuzz the update function | 143 | 2.27k | update_fn(&contexts[ctx_index], chunk_len, cursor); | 144 | 2.27k | cursor += chunk_len; | 145 | 2.27k | remaining -= chunk_len; | 146 | | | 147 | | // Randomly halt and reinitialise the stream | 148 | 2.27k | if (fdp.ConsumeBool()) { | 149 | 1.17k | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); | 150 | 1.17k | init_fn(&contexts[ctx_index]); | 151 | 1.17k | } | 152 | 2.27k | } | 153 | | | 154 | | // Fuzz the finish function for all contexts | 155 | 5.68k | for (unsigned i = 0; i < num_contexts; i++) { | 156 | 4.06k | finish_fn(&contexts[i], digests[i].data()); | 157 | 4.06k | } | 158 | | | 159 | | // Additional fuzzing on special context chaining approach | 160 | 1.62k | if (num_contexts >= 2 && digest_size && fdp.ConsumeBool()) { | 161 | 566 | unsigned src_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 162 | 566 | unsigned dst_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 163 | 566 | if (src_idx != dst_idx) { | 164 | 168 | init_fn(&contexts[dst_idx]); | 165 | 168 | size_t offset = fdp.ConsumeIntegralInRange<size_t>(0, digest_size - 1); | 166 | 168 | size_t feed_len = std::min(digest_size - offset, | 167 | 168 | (size_t)fdp.ConsumeIntegralInRange<size_t>(1, digest_size)); | 168 | 168 | update_fn(&contexts[dst_idx], feed_len, digests[src_idx].data() + offset); | 169 | 168 | finish_fn(&contexts[dst_idx], digests[dst_idx].data()); | 170 | 168 | } | 171 | 566 | } | 172 | 1.62k | } |
fuzz_crypto_int.cpp:void fuzz_hash_int_multi<mhd_Sha256CtxInt>(FuzzedDataProvider&, unsigned long, void (*)(mhd_Sha256CtxInt*), void (*)(mhd_Sha256CtxInt*, unsigned long, unsigned char const*), void (*)(mhd_Sha256CtxInt*, unsigned char*), unsigned long) Line | Count | Source | 41 | 1.30k | size_t digest_size) { | 42 | 1.30k | if (!fdp.remaining_bytes()) { | 43 | 2 | return; | 44 | 2 | } | 45 | | | 46 | | // Pull a random slice of data for fuzzing | 47 | 1.30k | size_t take_len = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); | 48 | 1.30k | std::vector<uint8_t> input_bytes = fdp.ConsumeBytes<uint8_t>(take_len); | 49 | | | 50 | | // Create 1 to 4 independent hashing contexts with it own digest buffer | 51 | 1.30k | const unsigned num_contexts = fdp.ConsumeIntegralInRange<unsigned>(1, 4); | 52 | 1.30k | std::vector<HashType> contexts(num_contexts); | 53 | 1.30k | std::vector<std::vector<uint8_t>> digests(num_contexts, std::vector<uint8_t>(digest_size)); | 54 | 4.51k | for (unsigned i = 0; i < num_contexts; i++) { | 55 | 3.20k | init_fn(&contexts[i]); | 56 | 3.20k | } | 57 | | | 58 | | // Intentionally misalign the data pointer to stress alignment sensitive paths | 59 | 1.30k | const size_t misalign_pad = fdp.ConsumeIntegralInRange<size_t>(0, 64); | 60 | 1.30k | std::vector<uint8_t> scratch_buf(misalign_pad + input_bytes.size()); | 61 | 1.30k | if (!input_bytes.empty()) { | 62 | 1.02k | memcpy(scratch_buf.data() + misalign_pad, input_bytes.data(), input_bytes.size()); | 63 | 1.02k | } | 64 | | | 65 | | // Define cursor and remaining bytes counter to keep track of the multiple hash update iterations | 66 | 1.30k | const uint8_t *cursor = scratch_buf.data() + misalign_pad; | 67 | 1.30k | size_t remaining = input_bytes.size(); | 68 | | | 69 | | // Perform multiple hash update iterations on the raw data | 70 | 1.30k | unsigned num_iterations = fdp.ConsumeIntegralInRange<unsigned>(1, 4); | 71 | 3.72k | while (num_iterations-- && remaining > 0) { | 72 | | // Pick which context to feed this iteration | 73 | 2.41k | const unsigned ctx_index = (num_contexts == 1) ? 0 : fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 74 | | | 75 | | // Choose a chunking pattern for this iteration | 76 | 2.41k | enum Pattern { LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT }; | 77 | 2.41k | Pattern pattern = fdp.PickValueInArray<Pattern>({LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT}); | 78 | | | 79 | 2.41k | size_t chunk_len = 0; | 80 | 2.41k | switch (pattern) { | 81 | 533 | case LESS1: { | 82 | | // Consume 1 byte less from block size from the raw data for this iteration | 83 | 533 | if (block_size > 1) { | 84 | 533 | chunk_len = std::min(remaining, block_size - 1); | 85 | 533 | } | 86 | 533 | break; | 87 | 0 | } | 88 | 154 | case EQ: { | 89 | | // Consume block size bytes from the raw data for this iteration | 90 | 154 | chunk_len = std::min(remaining, block_size); | 91 | 154 | break; | 92 | 0 | } | 93 | 107 | case PLUS1: { | 94 | | // Consume 1 byte more from block size from the raw data for this iteration | 95 | 107 | chunk_len = std::min(remaining, block_size + 1); | 96 | 107 | break; | 97 | 0 | } | 98 | 367 | case SMALL: { | 99 | | // Consume 1 to 32 bytes from the raw data for this iteration | 100 | 367 | size_t small_len = (size_t)fdp.ConsumeIntegralInRange<int>(1, 32); | 101 | 367 | chunk_len = std::min(remaining, small_len); | 102 | 367 | break; | 103 | 0 | } | 104 | 438 | case RANDOM: { | 105 | | // Consume random bytes from the raw data for this iteration | 106 | 438 | chunk_len = (remaining >= 1) ? (size_t)fdp.ConsumeIntegralInRange<size_t>(1, remaining) : 0; | 107 | 438 | break; | 108 | 0 | } | 109 | 95 | case TAIL: { | 110 | | // Consume all remaining bytes from the raw data for this iteration | 111 | 95 | chunk_len = remaining; | 112 | 95 | break; | 113 | 0 | } | 114 | 725 | case HALT: { | 115 | | // Consume small chunk and consider reinitialisation or early halt of the hash iteration | 116 | 725 | size_t step = std::max<size_t>(1, fdp.ConsumeIntegralInRange<size_t>(1, block_size)); | 117 | 725 | size_t loops = fdp.ConsumeIntegralInRange<size_t>(1, 4); | 118 | 2.48k | for (size_t j = 0; j < loops && remaining > 0; j++) { | 119 | 1.75k | size_t w = std::min(remaining, step); | 120 | 1.75k | update_fn(&contexts[ctx_index], w, cursor); | 121 | 1.75k | cursor += w; | 122 | 1.75k | remaining -= w; | 123 | 1.75k | } | 124 | | | 125 | | // Randomly reinitialise the hash stream | 126 | 725 | if (fdp.ConsumeBool()) { | 127 | 480 | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); | 128 | 480 | init_fn(&contexts[ctx_index]); | 129 | 480 | } | 130 | 725 | continue; | 131 | 0 | } | 132 | 2.41k | } | 133 | 1.69k | if (chunk_len == 0 || chunk_len > remaining) { | 134 | 0 | continue; | 135 | 0 | } | 136 | | | 137 | | // Occasionally reinitialise a context between update iterations | 138 | 1.69k | if (fdp.ConsumeBool()) { | 139 | 839 | init_fn(&contexts[ctx_index]); | 140 | 839 | } | 141 | | | 142 | | // Fuzz the update function | 143 | 1.69k | update_fn(&contexts[ctx_index], chunk_len, cursor); | 144 | 1.69k | cursor += chunk_len; | 145 | 1.69k | remaining -= chunk_len; | 146 | | | 147 | | // Randomly halt and reinitialise the stream | 148 | 1.69k | if (fdp.ConsumeBool()) { | 149 | 813 | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); | 150 | 813 | init_fn(&contexts[ctx_index]); | 151 | 813 | } | 152 | 1.69k | } | 153 | | | 154 | | // Fuzz the finish function for all contexts | 155 | 4.51k | for (unsigned i = 0; i < num_contexts; i++) { | 156 | 3.20k | finish_fn(&contexts[i], digests[i].data()); | 157 | 3.20k | } | 158 | | | 159 | | // Additional fuzzing on special context chaining approach | 160 | 1.30k | if (num_contexts >= 2 && digest_size && fdp.ConsumeBool()) { | 161 | 419 | unsigned src_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 162 | 419 | unsigned dst_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 163 | 419 | if (src_idx != dst_idx) { | 164 | 184 | init_fn(&contexts[dst_idx]); | 165 | 184 | size_t offset = fdp.ConsumeIntegralInRange<size_t>(0, digest_size - 1); | 166 | 184 | size_t feed_len = std::min(digest_size - offset, | 167 | 184 | (size_t)fdp.ConsumeIntegralInRange<size_t>(1, digest_size)); | 168 | 184 | update_fn(&contexts[dst_idx], feed_len, digests[src_idx].data() + offset); | 169 | 184 | finish_fn(&contexts[dst_idx], digests[dst_idx].data()); | 170 | 184 | } | 171 | 419 | } | 172 | 1.30k | } |
fuzz_crypto_int.cpp:void fuzz_hash_int_multi<mhd_Sha512_256CtxInt>(FuzzedDataProvider&, unsigned long, void (*)(mhd_Sha512_256CtxInt*), void (*)(mhd_Sha512_256CtxInt*, unsigned long, unsigned char const*), void (*)(mhd_Sha512_256CtxInt*, unsigned char*), unsigned long) Line | Count | Source | 41 | 1.44k | size_t digest_size) { | 42 | 1.44k | if (!fdp.remaining_bytes()) { | 43 | 6 | return; | 44 | 6 | } | 45 | | | 46 | | // Pull a random slice of data for fuzzing | 47 | 1.43k | size_t take_len = fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()); | 48 | 1.43k | std::vector<uint8_t> input_bytes = fdp.ConsumeBytes<uint8_t>(take_len); | 49 | | | 50 | | // Create 1 to 4 independent hashing contexts with it own digest buffer | 51 | 1.43k | const unsigned num_contexts = fdp.ConsumeIntegralInRange<unsigned>(1, 4); | 52 | 1.43k | std::vector<HashType> contexts(num_contexts); | 53 | 1.43k | std::vector<std::vector<uint8_t>> digests(num_contexts, std::vector<uint8_t>(digest_size)); | 54 | 4.97k | for (unsigned i = 0; i < num_contexts; i++) { | 55 | 3.54k | init_fn(&contexts[i]); | 56 | 3.54k | } | 57 | | | 58 | | // Intentionally misalign the data pointer to stress alignment sensitive paths | 59 | 1.43k | const size_t misalign_pad = fdp.ConsumeIntegralInRange<size_t>(0, 64); | 60 | 1.43k | std::vector<uint8_t> scratch_buf(misalign_pad + input_bytes.size()); | 61 | 1.43k | if (!input_bytes.empty()) { | 62 | 1.12k | memcpy(scratch_buf.data() + misalign_pad, input_bytes.data(), input_bytes.size()); | 63 | 1.12k | } | 64 | | | 65 | | // Define cursor and remaining bytes counter to keep track of the multiple hash update iterations | 66 | 1.43k | const uint8_t *cursor = scratch_buf.data() + misalign_pad; | 67 | 1.43k | size_t remaining = input_bytes.size(); | 68 | | | 69 | | // Perform multiple hash update iterations on the raw data | 70 | 1.43k | unsigned num_iterations = fdp.ConsumeIntegralInRange<unsigned>(1, 4); | 71 | 4.08k | while (num_iterations-- && remaining > 0) { | 72 | | // Pick which context to feed this iteration | 73 | 2.64k | const unsigned ctx_index = (num_contexts == 1) ? 0 : fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 74 | | | 75 | | // Choose a chunking pattern for this iteration | 76 | 2.64k | enum Pattern { LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT }; | 77 | 2.64k | Pattern pattern = fdp.PickValueInArray<Pattern>({LESS1, EQ, PLUS1, SMALL, RANDOM, TAIL, HALT}); | 78 | | | 79 | 2.64k | size_t chunk_len = 0; | 80 | 2.64k | switch (pattern) { | 81 | 513 | case LESS1: { | 82 | | // Consume 1 byte less from block size from the raw data for this iteration | 83 | 513 | if (block_size > 1) { | 84 | 513 | chunk_len = std::min(remaining, block_size - 1); | 85 | 513 | } | 86 | 513 | break; | 87 | 0 | } | 88 | 173 | case EQ: { | 89 | | // Consume block size bytes from the raw data for this iteration | 90 | 173 | chunk_len = std::min(remaining, block_size); | 91 | 173 | break; | 92 | 0 | } | 93 | 129 | case PLUS1: { | 94 | | // Consume 1 byte more from block size from the raw data for this iteration | 95 | 129 | chunk_len = std::min(remaining, block_size + 1); | 96 | 129 | break; | 97 | 0 | } | 98 | 326 | case SMALL: { | 99 | | // Consume 1 to 32 bytes from the raw data for this iteration | 100 | 326 | size_t small_len = (size_t)fdp.ConsumeIntegralInRange<int>(1, 32); | 101 | 326 | chunk_len = std::min(remaining, small_len); | 102 | 326 | break; | 103 | 0 | } | 104 | 639 | case RANDOM: { | 105 | | // Consume random bytes from the raw data for this iteration | 106 | 639 | chunk_len = (remaining >= 1) ? (size_t)fdp.ConsumeIntegralInRange<size_t>(1, remaining) : 0; | 107 | 639 | break; | 108 | 0 | } | 109 | 112 | case TAIL: { | 110 | | // Consume all remaining bytes from the raw data for this iteration | 111 | 112 | chunk_len = remaining; | 112 | 112 | break; | 113 | 0 | } | 114 | 754 | case HALT: { | 115 | | // Consume small chunk and consider reinitialisation or early halt of the hash iteration | 116 | 754 | size_t step = std::max<size_t>(1, fdp.ConsumeIntegralInRange<size_t>(1, block_size)); | 117 | 754 | size_t loops = fdp.ConsumeIntegralInRange<size_t>(1, 4); | 118 | 2.43k | for (size_t j = 0; j < loops && remaining > 0; j++) { | 119 | 1.68k | size_t w = std::min(remaining, step); | 120 | 1.68k | update_fn(&contexts[ctx_index], w, cursor); | 121 | 1.68k | cursor += w; | 122 | 1.68k | remaining -= w; | 123 | 1.68k | } | 124 | | | 125 | | // Randomly reinitialise the hash stream | 126 | 754 | if (fdp.ConsumeBool()) { | 127 | 472 | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); | 128 | 472 | init_fn(&contexts[ctx_index]); | 129 | 472 | } | 130 | 754 | continue; | 131 | 0 | } | 132 | 2.64k | } | 133 | 1.89k | if (chunk_len == 0 || chunk_len > remaining) { | 134 | 0 | continue; | 135 | 0 | } | 136 | | | 137 | | // Occasionally reinitialise a context between update iterations | 138 | 1.89k | if (fdp.ConsumeBool()) { | 139 | 831 | init_fn(&contexts[ctx_index]); | 140 | 831 | } | 141 | | | 142 | | // Fuzz the update function | 143 | 1.89k | update_fn(&contexts[ctx_index], chunk_len, cursor); | 144 | 1.89k | cursor += chunk_len; | 145 | 1.89k | remaining -= chunk_len; | 146 | | | 147 | | // Randomly halt and reinitialise the stream | 148 | 1.89k | if (fdp.ConsumeBool()) { | 149 | 864 | finish_fn(&contexts[ctx_index], digests[ctx_index].data()); | 150 | 864 | init_fn(&contexts[ctx_index]); | 151 | 864 | } | 152 | 1.89k | } | 153 | | | 154 | | // Fuzz the finish function for all contexts | 155 | 4.97k | for (unsigned i = 0; i < num_contexts; i++) { | 156 | 3.54k | finish_fn(&contexts[i], digests[i].data()); | 157 | 3.54k | } | 158 | | | 159 | | // Additional fuzzing on special context chaining approach | 160 | 1.43k | if (num_contexts >= 2 && digest_size && fdp.ConsumeBool()) { | 161 | 444 | unsigned src_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 162 | 444 | unsigned dst_idx = fdp.ConsumeIntegralInRange<unsigned>(0, num_contexts - 1); | 163 | 444 | if (src_idx != dst_idx) { | 164 | 180 | init_fn(&contexts[dst_idx]); | 165 | 180 | size_t offset = fdp.ConsumeIntegralInRange<size_t>(0, digest_size - 1); | 166 | 180 | size_t feed_len = std::min(digest_size - offset, | 167 | 180 | (size_t)fdp.ConsumeIntegralInRange<size_t>(1, digest_size)); | 168 | 180 | update_fn(&contexts[dst_idx], feed_len, digests[src_idx].data() + offset); | 169 | 180 | finish_fn(&contexts[dst_idx], digests[dst_idx].data()); | 170 | 180 | } | 171 | 444 | } | 172 | 1.43k | } |
|
173 | | |
174 | 2.24k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
175 | 2.24k | FuzzedDataProvider fdp(data, size); |
176 | | |
177 | 6.64k | for (int i = 0; i < fdp.ConsumeIntegralInRange<unsigned>(1, 4); i++) { |
178 | 4.40k | switch (fdp.ConsumeIntegralInRange<int>(0, 2)) { |
179 | 1.65k | case 0: |
180 | 1.65k | fuzz_hash_int_multi<struct mhd_Md5CtxInt>( |
181 | 1.65k | fdp, mhd_MD5_BLOCK_SIZE, |
182 | 1.65k | mhd_MD5_init, mhd_MD5_update, mhd_MD5_finish, mhd_MD5_DIGEST_SIZE); |
183 | 1.65k | break; |
184 | 1.30k | case 1: |
185 | 1.30k | fuzz_hash_int_multi<struct mhd_Sha256CtxInt>( |
186 | 1.30k | fdp, mhd_SHA256_BLOCK_SIZE, |
187 | 1.30k | mhd_SHA256_init, mhd_SHA256_update, mhd_SHA256_finish, mhd_SHA256_DIGEST_SIZE); |
188 | 1.30k | break; |
189 | 1.44k | case 2: |
190 | 1.44k | default: |
191 | 1.44k | fuzz_hash_int_multi<struct mhd_Sha512_256CtxInt>( |
192 | 1.44k | fdp, mhd_SHA512_256_BLOCK_SIZE, |
193 | 1.44k | mhd_SHA512_256_init, mhd_SHA512_256_update, mhd_SHA512_256_finish, mhd_SHA512_256_DIGEST_SIZE); |
194 | 1.44k | break; |
195 | 4.40k | } |
196 | 4.40k | } |
197 | 2.24k | return 0; |
198 | 2.24k | } |