Coverage Report

Created: 2025-08-24 06:26

/src/fuzz_crypto_int.cpp
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
}