Coverage Report

Created: 2026-09-12 06:55

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