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