Coverage Report

Created: 2025-08-24 07:07

/src/mercurial/contrib/fuzz/mpatch.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * mpatch.cc - fuzzer harness for mpatch.c
3
 *
4
 * Copyright 2018, Google Inc.
5
 *
6
 * This software may be used and distributed according to the terms of
7
 * the GNU General Public License, incorporated herein by reference.
8
 */
9
#include <iostream>
10
#include <memory>
11
#include <stdint.h>
12
#include <stdlib.h>
13
#include <vector>
14
15
#include "fuzzutil.h"
16
17
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv)
18
16
{
19
16
  return 0;
20
16
}
21
22
// To avoid having too many OOMs from the fuzzer infrastructure, we'll
23
// skip patch application if the resulting fulltext would be bigger
24
// than 10MiB.
25
219
#define MAX_OUTPUT_SIZE 10485760
26
27
extern "C" {
28
#include "bitmanipulation.h"
29
#include "mpatch.h"
30
31
struct mpatchbin {
32
  std::unique_ptr<char[]> data;
33
  size_t len;
34
};
35
36
static mpatch_flist *getitem(void *vbins, ssize_t pos)
37
16.4k
{
38
16.4k
  std::vector<mpatchbin> *bins = (std::vector<mpatchbin> *)vbins;
39
16.4k
  const mpatchbin &bin = bins->at(pos + 1);
40
16.4k
  struct mpatch_flist *res;
41
16.4k
  LOG(2) << "mpatch_decode " << bin.len << std::endl;
42
16.4k
  if (mpatch_decode(bin.data.get(), bin.len, &res) < 0)
43
1.72k
    return NULL;
44
14.7k
  return res;
45
16.4k
}
46
47
// input format:
48
// u8 number of inputs
49
// one u16 for each input, its length
50
// the inputs
51
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
52
1.97k
{
53
1.97k
  if (!Size) {
54
0
    return 0;
55
0
  }
56
  // First byte of data is how many texts we expect, first text
57
  // being the base the rest being the deltas.
58
1.97k
  ssize_t numtexts = Data[0];
59
1.97k
  if (numtexts < 2) {
60
    // No point if we don't have at least a base text and a delta...
61
2
    return 0;
62
2
  }
63
  // Each text will be described by a byte for how long it
64
  // should be, so give up if we don't have enough.
65
1.97k
  if ((Size - 1) < (numtexts * 2)) {
66
12
    return 0;
67
12
  }
68
1.96k
  size_t consumed = 1 + (numtexts * 2);
69
1.96k
  LOG(2) << "input contains " << Size << std::endl;
70
1.96k
  LOG(2) << numtexts << " texts, consuming " << consumed << std::endl;
71
1.96k
  std::vector<mpatchbin> bins;
72
1.96k
  bins.reserve(numtexts);
73
20.7k
  for (int i = 0; i < numtexts; ++i) {
74
18.8k
    mpatchbin bin;
75
18.8k
    size_t nthsize = getbeuint16((char *)Data + 1 + (2 * i));
76
18.8k
    LOG(2) << "text " << i << " is " << nthsize << std::endl;
77
18.8k
    char *start = (char *)Data + consumed;
78
18.8k
    consumed += nthsize;
79
18.8k
    if (consumed > Size) {
80
53
      LOG(2) << "ran out of data, consumed " << consumed
81
0
             << " of " << Size << std::endl;
82
53
      return 0;
83
53
    }
84
18.7k
    bin.len = nthsize;
85
18.7k
    bin.data.reset(new char[nthsize]);
86
18.7k
    memcpy(bin.data.get(), start, nthsize);
87
18.7k
    bins.push_back(std::move(bin));
88
18.7k
  }
89
1.91k
  LOG(2) << "mpatch_flist" << std::endl;
90
1.91k
  struct mpatch_flist *patch =
91
1.91k
      mpatch_fold(&bins, getitem, 0, numtexts - 1);
92
1.91k
  if (!patch) {
93
778
    return 0;
94
778
  }
95
1.13k
  LOG(2) << "mpatch_calcsize" << std::endl;
96
1.13k
  ssize_t outlen = mpatch_calcsize(bins[0].len, patch);
97
1.13k
  LOG(2) << "outlen " << outlen << std::endl;
98
1.13k
  if (outlen < 0 || outlen > MAX_OUTPUT_SIZE) {
99
913
    goto cleanup;
100
913
  }
101
219
  {
102
219
    char *dest = (char *)malloc(outlen);
103
219
    LOG(2) << "expecting " << outlen << " total bytes at "
104
0
           << (void *)dest << std::endl;
105
219
    mpatch_apply(dest, bins[0].data.get(), bins[0].len, patch);
106
219
    free(dest);
107
219
    LOG(1) << "applied a complete patch" << std::endl;
108
219
  }
109
1.13k
cleanup:
110
1.13k
  mpatch_lfree(patch);
111
1.13k
  return 0;
112
219
}
113
114
} // extern "C"