2 * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
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.
12 * Test hashtable operation.
15 #include <openssl/err.h>
16 #include <openssl/bio.h>
17 #include <internal/common.h>
18 #include <internal/hashtable.h>
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
27 HT_START_KEY_DEFN(fuzzer_key
)
28 HT_DEF_KEY_FIELD(fuzzkey
, uint16_t)
29 HT_END_KEY_DEFN(FUZZER_KEY
)
31 #define FZ_FLAG_ALLOCATED (1 << 0)
32 typedef struct fuzzer_value_st
{
37 IMPLEMENT_HT_VALUE_TYPE_FNS(FUZZER_VALUE
, fz
, static)
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;
49 static FUZZER_VALUE
*prediction_table
= NULL
;
50 static HT
*fuzzer_table
= NULL
;
64 #define INSERT_REPLACE_MASK 0x40
65 #define OPERATION(x) (((x) & OP_MASK) % OP_END)
66 #define IS_REPLACE(x) ((x) & INSERT_REPLACE_MASK)
68 static int table_iterator(HT_VALUE
*v
, void *arg
)
70 uint16_t keyval
= (*(uint16_t *)arg
);
71 FUZZER_VALUE
*f
= ossl_ht_fz_FUZZER_VALUE_from_value(v
);
73 if (f
!= NULL
&& f
== &prediction_table
[keyval
]) {
81 static int filter_iterator(HT_VALUE
*v
, void *arg
)
83 uint16_t keyval
= (*(uint16_t *)arg
);
84 FUZZER_VALUE
*f
= ossl_ht_fz_FUZZER_VALUE_from_value(v
);
86 if (f
!= NULL
&& f
== &prediction_table
[keyval
])
92 static void fuzz_free_cb(HT_VALUE
*v
)
94 FUZZER_VALUE
*f
= ossl_ht_fz_FUZZER_VALUE_from_value(v
);
97 f
->flags
&= ~FZ_FLAG_ALLOCATED
;
100 int FuzzerInitialize(int *argc
, char ***argv
)
102 HT_CONFIG fuzz_conf
= {NULL
, fuzz_free_cb
, NULL
, 0};
104 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, NULL
);
106 prediction_table
= OPENSSL_zalloc(sizeof(FUZZER_VALUE
) * 65537);
107 if (prediction_table
== NULL
)
109 fuzzer_table
= ossl_ht_new(&fuzz_conf
);
110 if (fuzzer_table
== NULL
) {
111 OPENSSL_free(prediction_table
);
118 int FuzzerTestOneInput(const uint8_t *buf
, size_t len
)
123 int rc_prediction
= 1;
125 FUZZER_VALUE
*valptr
, *lval
;
129 HT_VALUE_LIST
*htvlist
;
132 * We need at least 11 bytes to be able to do anything here
133 * 1 byte to detect the operation to preform, 2 bytes
134 * for the lookup key, and 8 bytes of value
142 * parse out our operation flags and key
145 memcpy(&keyval
, &buf
[1], sizeof(uint16_t));
153 * Now do our operation
155 switch(OPERATION(op_flags
)) {
157 valptr
= &prediction_table
[keyval
];
162 /* set the proper key value */
163 HT_SET_KEY_FIELD(&key
, fuzzkey
, keyval
);
166 ossl_ht_write_lock(fuzzer_table
);
169 * If the value to insert is already allocated
170 * then we expect a conflict in the insert
171 * i.e. we predict a return code of 0 instead
172 * of 1. On replacement, we expect it to succeed
175 if (valptr
->flags
& FZ_FLAG_ALLOCATED
) {
176 if (!IS_REPLACE(op_flags
))
180 memcpy(&valptr
->value
, &buf
[3], sizeof(uint64_t));
182 * do the insert/replace
184 if (IS_REPLACE(op_flags
))
185 rc
= ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table
, TO_HT_KEY(&key
),
188 rc
= ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table
, TO_HT_KEY(&key
),
192 * mark the entry as being allocated
194 valptr
->flags
|= FZ_FLAG_ALLOCATED
;
199 ossl_ht_write_unlock(fuzzer_table
);
202 * Now check to make sure we did the right thing
204 OPENSSL_assert(rc
== rc_prediction
);
207 * successful insertion if there wasn't a conflict
209 if (rc_prediction
== 1)
210 IS_REPLACE(op_flags
) ? replacements
++ : inserts
++;
214 valptr
= &prediction_table
[keyval
];
219 /* set the proper key value */
220 HT_SET_KEY_FIELD(&key
, fuzzkey
, keyval
);
223 ossl_ht_write_lock(fuzzer_table
);
226 * If the value to delete is not already allocated
227 * then we expect a miss in the delete
228 * i.e. we predict a return code of 0 instead
231 if (!(valptr
->flags
& FZ_FLAG_ALLOCATED
))
237 rc
= ossl_ht_delete(fuzzer_table
, TO_HT_KEY(&key
));
242 ossl_ht_write_unlock(fuzzer_table
);
245 * Now check to make sure we did the right thing
247 OPENSSL_assert(rc
== rc_prediction
);
250 * once the unlock is done, the table rcu will have synced
251 * meaning the free function has run, so we can confirm now
252 * that the valptr is no longer allocated
254 OPENSSL_assert(!(valptr
->flags
& FZ_FLAG_ALLOCATED
));
257 * successful deletion if there wasn't a conflict
259 if (rc_prediction
== 1)
265 valptr
= &prediction_table
[keyval
];
271 /* set the proper key value */
272 HT_SET_KEY_FIELD(&key
, fuzzkey
, keyval
);
274 /* lock the table for reading */
275 ossl_ht_read_lock(fuzzer_table
);
278 * If the value to find is not already allocated
279 * then we expect a miss in the lookup
280 * i.e. we predict a return code of NULL instead
283 if (!(valptr
->flags
& FZ_FLAG_ALLOCATED
))
289 lval
= ossl_ht_fz_FUZZER_VALUE_get(fuzzer_table
, TO_HT_KEY(&key
), &v
);
294 ossl_ht_read_unlock(fuzzer_table
);
297 * Now check to make sure we did the right thing
299 OPENSSL_assert(lval
== valptr
);
302 * if we expect a positive lookup, make sure that
303 * we can use the _type and to_value functions
305 if (valptr
!= NULL
) {
306 OPENSSL_assert(ossl_ht_fz_FUZZER_VALUE_type(v
) == 1);
308 v
= ossl_ht_fz_FUZZER_VALUE_to_value(lval
, &tv
);
309 OPENSSL_assert(v
->value
== lval
);
313 * successful lookup if we didn't expect a miss
322 * only flush the table rarely
324 if ((flushes
% 100000) != 1) {
333 ossl_ht_write_lock(fuzzer_table
);
334 ossl_ht_flush(fuzzer_table
);
335 ossl_ht_write_unlock(fuzzer_table
);
338 * now check to make sure everything is free
340 for (i
= 0; i
< USHRT_MAX
; i
++)
341 OPENSSL_assert((prediction_table
[i
].flags
& FZ_FLAG_ALLOCATED
) == 0);
349 valptr
= &prediction_table
[keyval
];
352 if (valptr
->flags
& FZ_FLAG_ALLOCATED
)
355 ossl_ht_foreach_until(fuzzer_table
, table_iterator
, &keyval
);
357 OPENSSL_assert(valfound
== rc_prediction
);
363 valptr
= &prediction_table
[keyval
];
366 if (valptr
->flags
& FZ_FLAG_ALLOCATED
)
369 htvlist
= ossl_ht_filter(fuzzer_table
, 1, filter_iterator
, &keyval
);
371 OPENSSL_assert(htvlist
->list_len
== (size_t)rc_prediction
);
373 ossl_ht_value_list_free(htvlist
);
384 void FuzzerCleanup(void)
386 ossl_ht_free(fuzzer_table
);
387 OPENSSL_free(prediction_table
);