123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- /*
- * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * https://www.openssl.org/source/license.html
- * or in the file LICENSE in the source distribution.
- */
- /*
- * Test hashtable operation.
- */
- #include <limits.h>
- #include <openssl/err.h>
- #include <openssl/bio.h>
- #include <internal/common.h>
- #include <internal/hashtable.h>
- #include "fuzzer.h"
- /*
- * Make the key space very small here to make lookups
- * easy to predict for the purposes of validation
- * A two byte key gives us 65536 possible entries
- * so we can allocate a flat table to compare to
- */
- HT_START_KEY_DEFN(fuzzer_key)
- HT_DEF_KEY_FIELD(fuzzkey, uint16_t)
- HT_END_KEY_DEFN(FUZZER_KEY)
- #define FZ_FLAG_ALLOCATED (1 << 0)
- typedef struct fuzzer_value_st {
- uint64_t flags;
- uint64_t value;
- } FUZZER_VALUE;
- IMPLEMENT_HT_VALUE_TYPE_FNS(FUZZER_VALUE, fz, static)
- static size_t skipped_values = 0;
- static size_t inserts = 0;
- static size_t replacements = 0;
- static size_t deletes = 0;
- static size_t flushes = 0;
- static size_t lookups = 0;
- static size_t foreaches = 0;
- static size_t filters = 0;
- static int valfound;
- static FUZZER_VALUE *prediction_table = NULL;
- static HT *fuzzer_table = NULL;
- /*
- * Operational values
- */
- #define OP_INSERT 0
- #define OP_DELETE 1
- #define OP_LOOKUP 2
- #define OP_FLUSH 3
- #define OP_FOREACH 4
- #define OP_FILTER 5
- #define OP_END 6
- #define OP_MASK 0x3f
- #define INSERT_REPLACE_MASK 0x40
- #define OPERATION(x) (((x) & OP_MASK) % OP_END)
- #define IS_REPLACE(x) ((x) & INSERT_REPLACE_MASK)
- static int table_iterator(HT_VALUE *v, void *arg)
- {
- uint16_t keyval = (*(uint16_t *)arg);
- FUZZER_VALUE *f = ossl_ht_fz_FUZZER_VALUE_from_value(v);
- if (f != NULL && f == &prediction_table[keyval]) {
- valfound = 1;
- return 0;
- }
- return 1;
- }
- static int filter_iterator(HT_VALUE *v, void *arg)
- {
- uint16_t keyval = (*(uint16_t *)arg);
- FUZZER_VALUE *f = ossl_ht_fz_FUZZER_VALUE_from_value(v);
- if (f != NULL && f == &prediction_table[keyval])
- return 1;
- return 0;
- }
- static void fuzz_free_cb(HT_VALUE *v)
- {
- FUZZER_VALUE *f = ossl_ht_fz_FUZZER_VALUE_from_value(v);
- if (f != NULL)
- f->flags &= ~FZ_FLAG_ALLOCATED;
- }
- int FuzzerInitialize(int *argc, char ***argv)
- {
- HT_CONFIG fuzz_conf = {NULL, fuzz_free_cb, NULL, 0};
- OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
- ERR_clear_error();
- prediction_table = OPENSSL_zalloc(sizeof(FUZZER_VALUE) * 65537);
- if (prediction_table == NULL)
- return -1;
- fuzzer_table = ossl_ht_new(&fuzz_conf);
- if (fuzzer_table == NULL) {
- OPENSSL_free(prediction_table);
- return -1;
- }
- return 0;
- }
- int FuzzerTestOneInput(const uint8_t *buf, size_t len)
- {
- uint8_t op_flags;
- uint16_t keyval;
- int rc;
- int rc_prediction = 1;
- size_t i;
- FUZZER_VALUE *valptr, *lval;
- FUZZER_KEY key;
- HT_VALUE *v = NULL;
- HT_VALUE tv;
- HT_VALUE_LIST *htvlist;
- /*
- * We need at least 11 bytes to be able to do anything here
- * 1 byte to detect the operation to preform, 2 bytes
- * for the lookup key, and 8 bytes of value
- */
- if (len < 11) {
- skipped_values++;
- return -1;
- }
- /*
- * parse out our operation flags and key
- */
- op_flags = buf[0];
- keyval = *((uint16_t *)&buf[1]);
- /*
- * Initialize our key
- */
- HT_INIT_KEY(&key);
- /*
- * Now do our operation
- */
- switch(OPERATION(op_flags)) {
- case OP_INSERT:
- valptr = &prediction_table[keyval];
- /* reset our key */
- HT_KEY_RESET(&key);
- /* set the proper key value */
- HT_SET_KEY_FIELD(&key, fuzzkey, keyval);
- /* lock the table */
- ossl_ht_write_lock(fuzzer_table);
- /*
- * If the value to insert is already allocated
- * then we expect a conflict in the insert
- * i.e. we predict a return code of 0 instead
- * of 1. On replacement, we expect it to succeed
- * always
- */
- if (valptr->flags & FZ_FLAG_ALLOCATED) {
- if (!IS_REPLACE(op_flags))
- rc_prediction = 0;
- }
- valptr->value = *(uint64_t *)&buf[3];
- /*
- * do the insert/replace
- */
- if (IS_REPLACE(op_flags))
- rc = ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table, TO_HT_KEY(&key),
- valptr, &lval);
- else
- rc = ossl_ht_fz_FUZZER_VALUE_insert(fuzzer_table, TO_HT_KEY(&key),
- valptr, NULL);
- /*
- * mark the entry as being allocated
- */
- valptr->flags |= FZ_FLAG_ALLOCATED;
- /*
- * unlock the table
- */
- ossl_ht_write_unlock(fuzzer_table);
- /*
- * Now check to make sure we did the right thing
- */
- OPENSSL_assert(rc == rc_prediction);
- /*
- * successful insertion if there wasn't a conflict
- */
- if (rc_prediction == 1)
- IS_REPLACE(op_flags) ? replacements++ : inserts++;
- break;
- case OP_DELETE:
- valptr = &prediction_table[keyval];
- /* reset our key */
- HT_KEY_RESET(&key);
- /* set the proper key value */
- HT_SET_KEY_FIELD(&key, fuzzkey, keyval);
- /* lock the table */
- ossl_ht_write_lock(fuzzer_table);
- /*
- * If the value to delete is not already allocated
- * then we expect a miss in the delete
- * i.e. we predict a return code of 0 instead
- * of 1
- */
- if (!(valptr->flags & FZ_FLAG_ALLOCATED))
- rc_prediction = 0;
- /*
- * do the delete
- */
- rc = ossl_ht_delete(fuzzer_table, TO_HT_KEY(&key));
- /*
- * unlock the table
- */
- ossl_ht_write_unlock(fuzzer_table);
- /*
- * Now check to make sure we did the right thing
- */
- OPENSSL_assert(rc == rc_prediction);
- /*
- * once the unlock is done, the table rcu will have synced
- * meaning the free function has run, so we can confirm now
- * that the valptr is no longer allocated
- */
- OPENSSL_assert(!(valptr->flags & FZ_FLAG_ALLOCATED));
- /*
- * successful deletion if there wasn't a conflict
- */
- if (rc_prediction == 1)
- deletes++;
- break;
- case OP_LOOKUP:
- valptr = &prediction_table[keyval];
- lval = NULL;
- /* reset our key */
- HT_KEY_RESET(&key);
- /* set the proper key value */
- HT_SET_KEY_FIELD(&key, fuzzkey, keyval);
- /* lock the table for reading */
- ossl_ht_read_lock(fuzzer_table);
- /*
- * If the value to find is not already allocated
- * then we expect a miss in the lookup
- * i.e. we predict a return code of NULL instead
- * of a pointer
- */
- if (!(valptr->flags & FZ_FLAG_ALLOCATED))
- valptr = NULL;
- /*
- * do the lookup
- */
- lval = ossl_ht_fz_FUZZER_VALUE_get(fuzzer_table, TO_HT_KEY(&key), &v);
- /*
- * unlock the table
- */
- ossl_ht_read_unlock(fuzzer_table);
- /*
- * Now check to make sure we did the right thing
- */
- OPENSSL_assert(lval == valptr);
- /*
- * if we expect a positive lookup, make sure that
- * we can use the _type and to_value functions
- */
- if (valptr != NULL) {
- OPENSSL_assert(ossl_ht_fz_FUZZER_VALUE_type(v) == 1);
- v = ossl_ht_fz_FUZZER_VALUE_to_value(lval, &tv);
- OPENSSL_assert(v->value == lval);
- }
- /*
- * successful lookup if we didn't expect a miss
- */
- if (valptr != NULL)
- lookups++;
- break;
- case OP_FLUSH:
- /*
- * only flush the table rarely
- */
- if ((flushes % 100000) != 1) {
- skipped_values++;
- flushes++;
- return 0;
- }
- /*
- * lock the table
- */
- ossl_ht_write_lock(fuzzer_table);
- ossl_ht_flush(fuzzer_table);
- ossl_ht_write_unlock(fuzzer_table);
- /*
- * now check to make sure everything is free
- */
- for (i = 0; i < USHRT_MAX; i++)
- OPENSSL_assert((prediction_table[i].flags & FZ_FLAG_ALLOCATED) == 0);
- /* good flush */
- flushes++;
- break;
- case OP_FOREACH:
- valfound = 0;
- valptr = &prediction_table[keyval];
- rc_prediction = 0;
- if (valptr->flags & FZ_FLAG_ALLOCATED)
- rc_prediction = 1;
- ossl_ht_foreach_until(fuzzer_table, table_iterator, &keyval);
- OPENSSL_assert(valfound == rc_prediction);
- foreaches++;
- break;
- case OP_FILTER:
- valptr = &prediction_table[keyval];
- rc_prediction = 0;
- if (valptr->flags & FZ_FLAG_ALLOCATED)
- rc_prediction = 1;
- htvlist = ossl_ht_filter(fuzzer_table, 1, filter_iterator, &keyval);
- OPENSSL_assert(htvlist->list_len == (size_t)rc_prediction);
- ossl_ht_value_list_free(htvlist);
- filters++;
- break;
- default:
- return -1;
- }
- return 0;
- }
- void FuzzerCleanup(void)
- {
- ossl_ht_free(fuzzer_table);
- OPENSSL_free(prediction_table);
- OPENSSL_cleanup();
- }
|