Coverage Report

Created: 2026-10-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl41/fuzz/hashtable.c
Line
Count
Source
1
/*
2
 * Copyright 2024-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 * https://www.openssl.org/source/license.html
8
 * or in the file LICENSE in the source distribution.
9
 */
10
11
/*
12
 * Test hashtable operation.
13
 */
14
#include <limits.h>
15
#include <stdio.h>
16
#include <openssl/err.h>
17
#include <openssl/bio.h>
18
#include <internal/common.h>
19
#include <internal/hashtable.h>
20
#include "fuzzer.h"
21
22
/*
23
 * Make the key space very small here to make lookups
24
 * easy to predict for the purposes of validation
25
 * A two byte key gives us 65536 possible entries
26
 * so we can allocate a flat table to compare to
27
 */
28
HT_START_KEY_DEFN(fuzzer_key)
29
HT_DEF_KEY_FIELD(fuzzkey, uint16_t)
30
HT_END_KEY_DEFN(FUZZER_KEY)
31
32
197
#define FZ_FLAG_ALLOCATED (1 << 0)
33
typedef struct fuzzer_value_st {
34
    uint64_t flags;
35
    uint64_t value;
36
} FUZZER_VALUE;
37
38
1.72k
IMPLEMENT_HT_VALUE_TYPE_FNS(FUZZER_VALUE, fz, static)
hashtable.c:ossl_ht_fz_FUZZER_VALUE_from_value
Line
Count
Source
38
IMPLEMENT_HT_VALUE_TYPE_FNS(FUZZER_VALUE, fz, static)
hashtable.c:ossl_ht_fz_FUZZER_VALUE_insert
Line
Count
Source
38
IMPLEMENT_HT_VALUE_TYPE_FNS(FUZZER_VALUE, fz, static)
hashtable.c:ossl_ht_fz_FUZZER_VALUE_get
Line
Count
Source
38
IMPLEMENT_HT_VALUE_TYPE_FNS(FUZZER_VALUE, fz, static)
39
40
static size_t skipped_values = 0;
41
static size_t inserts = 0;
42
static size_t replacements = 0;
43
static size_t deletes = 0;
44
static size_t flushes = 0;
45
static size_t lookups = 0;
46
static size_t foreaches = 0;
47
static size_t filters = 0;
48
static int valfound;
49
50
static FUZZER_VALUE *prediction_table = NULL;
51
static HT *fuzzer_table = NULL;
52
53
/*
54
 * Operational values
55
 */
56
46
#define OP_INSERT 0
57
27
#define OP_DELETE 1
58
30
#define OP_LOOKUP 2
59
19
#define OP_FLUSH 3
60
41
#define OP_FOREACH 4
61
32
#define OP_FILTER 5
62
195
#define OP_END 6
63
64
195
#define OP_MASK 0x3f
65
90
#define INSERT_REPLACE_MASK 0x40
66
195
#define OPERATION(x) (((x) & OP_MASK) % OP_END)
67
90
#define IS_REPLACE(x) ((x) & INSERT_REPLACE_MASK)
68
69
static int table_iterator(HT_VALUE *v, void *arg)
70
853
{
71
853
    uint16_t keyval = (*(uint16_t *)arg);
72
853
    FUZZER_VALUE *f = ossl_ht_fz_FUZZER_VALUE_from_value(v);
73
74
853
    if (f != NULL && f == &prediction_table[keyval]) {
75
6
        valfound = 1;
76
6
        return 0;
77
6
    }
78
79
847
    return 1;
80
853
}
81
82
static int filter_iterator(HT_VALUE *v, void *arg)
83
561
{
84
561
    uint16_t keyval = (*(uint16_t *)arg);
85
561
    FUZZER_VALUE *f = ossl_ht_fz_FUZZER_VALUE_from_value(v);
86
87
561
    if (f != NULL && f == &prediction_table[keyval])
88
5
        return 1;
89
90
556
    return 0;
91
561
}
92
93
static void fuzz_free_cb(HT_VALUE *v)
94
50
{
95
50
    FUZZER_VALUE *f = ossl_ht_fz_FUZZER_VALUE_from_value(v);
96
97
50
    if (f != NULL)
98
50
        f->flags &= ~FZ_FLAG_ALLOCATED;
99
50
}
100
101
int FuzzerInitialize(int *argc, char ***argv)
102
12
{
103
12
    HT_CONFIG fuzz_conf = { NULL, fuzz_free_cb, NULL, 0, 1, 0 };
104
105
12
    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
106
12
    ERR_clear_error();
107
12
    prediction_table = OPENSSL_calloc(65537, sizeof(FUZZER_VALUE));
108
12
    if (prediction_table == NULL)
109
0
        return -1;
110
12
    fuzzer_table = ossl_ht_new(&fuzz_conf);
111
12
    if (fuzzer_table == NULL) {
112
0
        OPENSSL_free(prediction_table);
113
0
        return -1;
114
0
    }
115
116
12
    return 0;
117
12
}
118
119
int FuzzerTestOneInput(const uint8_t *buf, size_t len)
120
207
{
121
207
    uint8_t op_flags;
122
207
    uint16_t keyval;
123
207
    int rc;
124
207
    int rc_prediction = 1;
125
207
    size_t i;
126
207
    FUZZER_VALUE *valptr, *lval;
127
207
    FUZZER_KEY key;
128
207
    HT_VALUE *v = NULL;
129
207
    HT_VALUE tv;
130
207
    HT_VALUE_LIST *htvlist;
131
132
    /*
133
     * We need at least 11 bytes to be able to do anything here
134
     * 1 byte to detect the operation to perform, 2 bytes
135
     * for the lookup key, and 8 bytes of value
136
     */
137
207
    if (len < 11) {
138
12
        skipped_values++;
139
12
        return -1;
140
12
    }
141
142
    /*
143
     * parse out our operation flags and key
144
     */
145
195
    op_flags = buf[0];
146
195
    memcpy(&keyval, &buf[1], sizeof(uint16_t));
147
148
    /*
149
     * Initialize our key
150
     */
151
195
    HT_INIT_KEY(&key);
152
153
    /*
154
     * Now do our operation
155
     */
156
195
    switch (OPERATION(op_flags)) {
157
46
    case OP_INSERT:
158
46
        valptr = &prediction_table[keyval];
159
160
        /* reset our key */
161
46
        HT_KEY_RESET(&key);
162
163
        /* set the proper key value */
164
46
        HT_SET_KEY_FIELD(&key, fuzzkey, keyval);
165
166
46
        memcpy(&valptr->value, &buf[3], sizeof(uint64_t));
167
168
        /* lock the table */
169
46
        ossl_ht_write_lock(fuzzer_table);
170
171
        /*
172
         * do the insert/replace
173
         */
174
46
        if (IS_REPLACE(op_flags))
175
21
            rc = ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table, TO_HT_KEY(&key),
176
21
                valptr, &lval);
177
25
        else
178
25
            rc = ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table, TO_HT_KEY(&key),
179
25
                valptr, NULL);
180
181
        /*
182
         * unlock the table
183
         */
184
46
        ossl_ht_write_unlock(fuzzer_table);
185
186
        /*
187
         * mark the entry as being allocated
188
         */
189
46
        if (rc == 1) {
190
44
            valptr->flags |= FZ_FLAG_ALLOCATED;
191
44
            IS_REPLACE(op_flags) ? replacements++ : inserts++;
192
44
        }
193
46
        break;
194
195
27
    case OP_DELETE:
196
27
        valptr = &prediction_table[keyval];
197
198
        /* reset our key */
199
27
        HT_KEY_RESET(&key);
200
201
        /* set the proper key value */
202
27
        HT_SET_KEY_FIELD(&key, fuzzkey, keyval);
203
204
        /* lock the table */
205
27
        ossl_ht_write_lock(fuzzer_table);
206
207
        /*
208
         * do the delete
209
         */
210
27
        rc = ossl_ht_delete(fuzzer_table, TO_HT_KEY(&key));
211
212
        /*
213
         * unlock the table
214
         */
215
27
        ossl_ht_write_unlock(fuzzer_table);
216
217
        /*
218
         * successful deletion if there wasn't a conflict
219
         */
220
27
        if (rc == 1)
221
1
            deletes++;
222
223
27
        break;
224
225
30
    case OP_LOOKUP:
226
30
        valptr = &prediction_table[keyval];
227
30
        lval = NULL;
228
229
        /* reset our key */
230
30
        HT_KEY_RESET(&key);
231
232
        /* set the proper key value */
233
30
        HT_SET_KEY_FIELD(&key, fuzzkey, keyval);
234
235
        /* lock the table for reading */
236
30
        if (!ossl_ht_read_lock(fuzzer_table))
237
0
            return 0;
238
239
        /*
240
         * If the value to find is not already allocated
241
         * then we expect a miss in the lookup
242
         * i.e. we predict a return code of NULL instead
243
         * of a pointer
244
         */
245
30
        if (!(valptr->flags & FZ_FLAG_ALLOCATED))
246
24
            valptr = NULL;
247
248
        /*
249
         * do the lookup
250
         */
251
30
        lval = ossl_ht_fz_FUZZER_VALUE_get(fuzzer_table, TO_HT_KEY(&key), &v);
252
253
        /*
254
         * unlock the table
255
         */
256
30
        ossl_ht_read_unlock(fuzzer_table);
257
258
        /*
259
         * Now check to make sure we did the right thing
260
         */
261
30
        if (valptr == NULL)
262
24
            OPENSSL_assert(lval == NULL);
263
6
        else
264
6
            OPENSSL_assert(lval == NULL || lval == valptr);
265
266
        /*
267
         * if we expect a positive lookup, make sure that
268
         * we can use the _type and to_value functions
269
         */
270
30
        if (valptr != NULL && lval != NULL) {
271
6
            OPENSSL_assert(ossl_ht_fz_FUZZER_VALUE_type(v) == 1);
272
273
6
            v = ossl_ht_fz_FUZZER_VALUE_to_value(lval, &tv);
274
6
            OPENSSL_assert(v->value == lval);
275
6
            lookups++;
276
6
        }
277
278
30
        break;
279
280
19
    case OP_FLUSH:
281
        /*
282
         * only flush the table rarely
283
         */
284
19
        if ((flushes % 100000) != 1) {
285
17
            skipped_values++;
286
17
            flushes++;
287
17
            return 0;
288
17
        }
289
290
        /*
291
         * lock the table
292
         */
293
2
        ossl_ht_write_lock(fuzzer_table);
294
2
        rc = ossl_ht_flush(fuzzer_table);
295
2
        ossl_ht_write_unlock(fuzzer_table);
296
297
        /*
298
         * now check to make sure everything is free
299
         */
300
2
        if (rc == 1) {
301
131k
            for (i = 0; i < USHRT_MAX; i++)
302
131k
                OPENSSL_assert((prediction_table[i].flags & FZ_FLAG_ALLOCATED) == 0);
303
2
            flushes++;
304
2
        }
305
2
        break;
306
307
41
    case OP_FOREACH:
308
41
        valfound = 0;
309
41
        valptr = &prediction_table[keyval];
310
311
41
        rc_prediction = 0;
312
41
        if (valptr->flags & FZ_FLAG_ALLOCATED)
313
3
            rc_prediction = 1;
314
315
41
        ossl_ht_foreach_until(fuzzer_table, table_iterator, &keyval);
316
317
41
        OPENSSL_assert(valfound == rc_prediction);
318
319
41
        foreaches++;
320
41
        break;
321
322
32
    case OP_FILTER:
323
32
        valptr = &prediction_table[keyval];
324
325
32
        rc_prediction = 0;
326
32
        if (valptr->flags & FZ_FLAG_ALLOCATED)
327
1
            rc_prediction = 1;
328
329
32
        htvlist = ossl_ht_filter(fuzzer_table, 1, filter_iterator, &keyval);
330
32
        if (htvlist != NULL) {
331
32
            OPENSSL_assert(htvlist->list_len == (size_t)rc_prediction);
332
32
            ossl_ht_value_list_free(htvlist);
333
32
            filters++;
334
32
        }
335
32
        break;
336
337
0
    default:
338
0
        return -1;
339
195
    }
340
341
178
    return 0;
342
195
}
343
344
void FuzzerCleanup(void)
345
0
{
346
0
    ossl_ht_free(fuzzer_table);
347
0
    OPENSSL_free(prediction_table);
348
349
0
    fprintf(stderr, "skipped_values: %zu inserts: %zu replacements: %zu deletes %zu\n"
350
0
                    "flushes: %zu lookups: %zu foreaches %zu filters %zu\n",
351
0
        skipped_values, inserts, replacements, deletes,
352
0
        flushes, lookups, foreaches, filters);
353
354
0
    OPENSSL_cleanup();
355
0
}