Coverage Report

Created: 2025-08-26 06:58

/src/bind9/fuzz/dns_qp.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <assert.h>
15
#include <err.h>
16
#include <stdbool.h>
17
#include <stdint.h>
18
19
#include <isc/random.h>
20
#include <isc/refcount.h>
21
#include <isc/rwlock.h>
22
#include <isc/urcu.h>
23
#include <isc/util.h>
24
25
#include <dns/qp.h>
26
#include <dns/types.h>
27
28
#include "fuzz.h"
29
#include "qp_p.h"
30
31
#include <tests/qp.h>
32
33
bool debug = false;
34
35
#if 0
36
#define TRACE(...) warnx(__VA_ARGS__)
37
#else
38
#define TRACE(...)
39
#endif
40
41
#if 0
42
#define ASSERT(p)                                               \
43
  do {                                                    \
44
    warnx("%s:%d: %s (%s)", __func__, __LINE__, #p, \
45
          (p) ? "OK" : "FAIL");                     \
46
    ok = ok && (p);                                 \
47
  } while (0)
48
#else
49
128M
#define ASSERT(p) assert(p)
50
#endif
51
52
static struct {
53
  uint32_t refcount;
54
  bool exists;
55
  uint8_t len;
56
  dns_qpkey_t key;
57
  dns_qpkey_t ascii;
58
} item[256 * 256 / 4];
59
60
static void
61
13.9M
fuzz_attach(void *ctx, void *pval, uint32_t ival) {
62
13.9M
  assert(ctx == NULL);
63
13.9M
  assert(pval == &item[ival]);
64
13.9M
  item[ival].refcount++;
65
13.9M
}
66
67
static void
68
13.9M
fuzz_detach(void *ctx, void *pval, uint32_t ival) {
69
13.9M
  assert(ctx == NULL);
70
13.9M
  assert(pval == &item[ival]);
71
13.9M
  item[ival].refcount--;
72
13.9M
}
73
74
static size_t
75
33.2M
fuzz_makekey(dns_qpkey_t key, void *ctx, void *pval, uint32_t ival) {
76
33.2M
  assert(ctx == NULL);
77
33.2M
  assert(pval == &item[ival]);
78
33.2M
  memmove(key, item[ival].key, item[ival].len);
79
33.2M
  return item[ival].len;
80
33.2M
}
81
82
static void
83
0
fuzz_triename(void *ctx, char *buf, size_t size) {
84
0
  assert(ctx == NULL);
85
0
  strlcpy(buf, "fuzz", size);
86
0
}
87
88
const dns_qpmethods_t fuzz_methods = {
89
  fuzz_attach,
90
  fuzz_detach,
91
  fuzz_makekey,
92
  fuzz_triename,
93
};
94
95
static uint8_t
96
2.11M
random_byte(void) {
97
2.11M
  return isc_random_uniform(SHIFT_OFFSET - SHIFT_NOBYTE) + SHIFT_NOBYTE;
98
2.11M
}
99
100
int
101
2
LLVMFuzzerInitialize(int *argc, char ***argv) {
102
2
  UNUSED(argc);
103
2
  UNUSED(argv);
104
105
32.7k
  for (size_t i = 0; i < ARRAY_SIZE(item); i++) {
106
32.7k
    size_t len = isc_random_uniform(100) + 16;
107
32.7k
    item[i].len = len + 1;
108
32.7k
    item[i].key[0] = 0;
109
2.14M
    for (size_t off = 1; off < len; off++) {
110
2.11M
      item[i].key[off] = random_byte();
111
2.11M
    }
112
32.7k
    memmove(item[i].ascii, item[i].key, item[i].len);
113
32.7k
    qp_test_keytoascii(item[i].ascii, item[i].len);
114
32.7k
  }
115
116
2
  return 0;
117
2
}
118
119
int
120
1.25k
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
121
1.25k
  isc_result_t result;
122
123
1.25k
  TRACE("------------------------------------------------");
124
125
1.25k
  isc_mem_t *mctx = NULL;
126
1.25k
  isc_mem_create("fuzz", &mctx);
127
1.25k
  isc_mem_setdestroycheck(mctx, true);
128
129
1.25k
  dns_qp_t *qp = NULL;
130
1.25k
  dns_qp_create(mctx, &fuzz_methods, NULL, &qp);
131
132
  /* avoid overrun */
133
1.25k
  size = size & ~1;
134
135
1.25k
  size_t count = 0;
136
137
32.0M
  for (size_t in = 0; in < size; in += 2) {
138
32.0M
    size_t what = data[in] + data[in + 1] * 256;
139
32.0M
    size_t i = (what / 4) % (count * 2 + 2);
140
32.0M
    bool exists = item[i].exists;
141
32.0M
    uint32_t refcount = item[i].refcount;
142
32.0M
    bool ok = true;
143
32.0M
    if (what & 2) {
144
1.65M
      void *pval = NULL;
145
1.65M
      uint32_t ival = ~0U;
146
1.65M
      result = dns_qp_getkey(qp, item[i].key, item[i].len,
147
1.65M
                 &pval, &ival);
148
1.65M
      TRACE("count %zu get %s %zu >%s<", count,
149
1.65M
            isc_result_toid(result), i, item[i].ascii);
150
1.65M
      if (result == ISC_R_SUCCESS) {
151
1.38M
        ASSERT(pval == &item[i]);
152
1.38M
        ASSERT(ival == i);
153
1.38M
        ASSERT(item[i].refcount == 1);
154
1.38M
        ASSERT(item[i].exists == true);
155
1.38M
      } else if (result == ISC_R_NOTFOUND) {
156
267k
        ASSERT(pval == NULL);
157
267k
        ASSERT(ival == ~0U);
158
267k
        ASSERT(item[i].refcount == 0);
159
267k
        ASSERT(item[i].exists == false);
160
267k
      } else {
161
0
        UNREACHABLE();
162
0
      }
163
30.3M
    } else if (what & 1) {
164
15.5M
      result = dns_qp_insert(qp, &item[i], i);
165
15.5M
      TRACE("count %zu ins %s %zu >%s<", count,
166
15.5M
            isc_result_toid(result), i, item[i].ascii);
167
15.5M
      if (result == ISC_R_SUCCESS) {
168
13.9M
        item[i].exists = true;
169
13.9M
        ASSERT(exists == false);
170
13.9M
        ASSERT(refcount == 0);
171
13.9M
        ASSERT(item[i].refcount == 1);
172
13.9M
        count += 1;
173
13.9M
        ASSERT(qp->leaf_count == count);
174
13.9M
      } else if (result == ISC_R_EXISTS) {
175
1.58M
        ASSERT(exists == true);
176
1.58M
        ASSERT(refcount == 1);
177
1.58M
        ASSERT(item[i].refcount == 1);
178
1.58M
        ASSERT(qp->leaf_count == count);
179
1.58M
      } else {
180
0
        UNREACHABLE();
181
0
      }
182
15.5M
    } else {
183
14.8M
      result = dns_qp_deletekey(qp, item[i].key, item[i].len,
184
14.8M
              NULL, NULL);
185
14.8M
      TRACE("count %zu del %s %zu >%s<", count,
186
14.8M
            isc_result_toid(result), i, item[i].ascii);
187
14.8M
      if (result == ISC_R_SUCCESS) {
188
12.8M
        item[i].exists = false;
189
12.8M
        ASSERT(exists == true);
190
12.8M
        ASSERT(refcount == 1);
191
12.8M
        ASSERT(item[i].refcount == 0);
192
12.8M
        count -= 1;
193
12.8M
        ASSERT(qp->leaf_count == count);
194
12.8M
      } else if (result == ISC_R_NOTFOUND) {
195
2.07M
        ASSERT(exists == false);
196
2.07M
        ASSERT(refcount == 0);
197
2.07M
        ASSERT(item[i].refcount == 0);
198
2.07M
        ASSERT(qp->leaf_count == count);
199
2.07M
      } else {
200
0
        UNREACHABLE();
201
0
      }
202
14.8M
    }
203
32.0M
    if (!ok) {
204
0
      qp_test_dumpqp(qp);
205
0
      qp_test_dumptrie(qp);
206
0
    }
207
32.0M
    assert(ok);
208
32.0M
  }
209
210
20.4M
  for (size_t i = 0; i < ARRAY_SIZE(item); i++) {
211
20.4M
    assert(item[i].exists == (item[i].refcount != 0));
212
20.4M
  }
213
214
1.25k
  dns_qp_destroy(&qp);
215
1.25k
  isc_mem_detach(&mctx);
216
1.25k
  isc_mem_checkdestroyed(stderr);
217
218
20.4M
  for (size_t i = 0; i < ARRAY_SIZE(item); i++) {
219
20.4M
    item[i].exists = false;
220
20.4M
    assert(item[i].refcount == 0);
221
20.4M
  }
222
223
1.25k
  return 0;
224
1.25k
}