/src/openssl30/crypto/lhash/lhash.c
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | /* | 
| 2 |  |  * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. | 
| 3 |  |  * | 
| 4 |  |  * Licensed under the Apache License 2.0 (the "License").  You may not use | 
| 5 |  |  * this file except in compliance with the License.  You can obtain a copy | 
| 6 |  |  * in the file LICENSE in the source distribution or at | 
| 7 |  |  * https://www.openssl.org/source/license.html | 
| 8 |  |  */ | 
| 9 |  |  | 
| 10 |  | #include <stdio.h> | 
| 11 |  | #include <string.h> | 
| 12 |  | #include <stdlib.h> | 
| 13 |  | #include <openssl/crypto.h> | 
| 14 |  | #include <openssl/lhash.h> | 
| 15 |  | #include <openssl/err.h> | 
| 16 |  | #include "crypto/ctype.h" | 
| 17 |  | #include "crypto/lhash.h" | 
| 18 |  | #include "lhash_local.h" | 
| 19 |  |  | 
| 20 |  | /* | 
| 21 |  |  * A hashing implementation that appears to be based on the linear hashing | 
| 22 |  |  * algorithm: | 
| 23 |  |  * https://en.wikipedia.org/wiki/Linear_hashing | 
| 24 |  |  * | 
| 25 |  |  * Litwin, Witold (1980), "Linear hashing: A new tool for file and table | 
| 26 |  |  * addressing", Proc. 6th Conference on Very Large Databases: 212-223 | 
| 27 |  |  * https://hackthology.com/pdfs/Litwin-1980-Linear_Hashing.pdf | 
| 28 |  |  * | 
| 29 |  |  * From the Wikipedia article "Linear hashing is used in the BDB Berkeley | 
| 30 |  |  * database system, which in turn is used by many software systems such as | 
| 31 |  |  * OpenLDAP, using a C implementation derived from the CACM article and first | 
| 32 |  |  * published on the Usenet in 1988 by Esmond Pitt." | 
| 33 |  |  * | 
| 34 |  |  * The CACM paper is available here: | 
| 35 |  |  * https://pdfs.semanticscholar.org/ff4d/1c5deca6269cc316bfd952172284dbf610ee.pdf | 
| 36 |  |  */ | 
| 37 |  |  | 
| 38 |  | #undef MIN_NODES | 
| 39 | 228k | #define MIN_NODES       16 | 
| 40 | 48.8k | #define UP_LOAD         (2*LH_LOAD_MULT) /* load times 256 (default 2) */ | 
| 41 | 48.8k | #define DOWN_LOAD       (LH_LOAD_MULT) /* load times 256 (default 1) */ | 
| 42 |  |  | 
| 43 |  | static int expand(OPENSSL_LHASH *lh); | 
| 44 |  | static void contract(OPENSSL_LHASH *lh); | 
| 45 |  | static OPENSSL_LH_NODE **getrn(OPENSSL_LHASH *lh, const void *data, unsigned long *rhash); | 
| 46 |  |  | 
| 47 |  | OPENSSL_LHASH *OPENSSL_LH_new(OPENSSL_LH_HASHFUNC h, OPENSSL_LH_COMPFUNC c) | 
| 48 | 48.8k | { | 
| 49 | 48.8k |     OPENSSL_LHASH *ret; | 
| 50 |  |  | 
| 51 | 48.8k |     if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) { | 
| 52 |  |         /* | 
| 53 |  |          * Do not set the error code, because the ERR code uses LHASH | 
| 54 |  |          * and we want to avoid possible endless error loop. | 
| 55 |  |          * ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE); | 
| 56 |  |          */ | 
| 57 | 0 |         return NULL; | 
| 58 | 0 |     } | 
| 59 | 48.8k |     if ((ret->b = OPENSSL_zalloc(sizeof(*ret->b) * MIN_NODES)) == NULL) | 
| 60 | 0 |         goto err; | 
| 61 | 48.8k |     ret->comp = ((c == NULL) ? (OPENSSL_LH_COMPFUNC)strcmp : c); | 
| 62 | 48.8k |     ret->hash = ((h == NULL) ? (OPENSSL_LH_HASHFUNC)OPENSSL_LH_strhash : h); | 
| 63 | 48.8k |     ret->num_nodes = MIN_NODES / 2; | 
| 64 | 48.8k |     ret->num_alloc_nodes = MIN_NODES; | 
| 65 | 48.8k |     ret->pmax = MIN_NODES / 2; | 
| 66 | 48.8k |     ret->up_load = UP_LOAD; | 
| 67 | 48.8k |     ret->down_load = DOWN_LOAD; | 
| 68 | 48.8k |     return ret; | 
| 69 |  |  | 
| 70 | 0 | err: | 
| 71 | 0 |     OPENSSL_free(ret->b); | 
| 72 | 0 |     OPENSSL_free(ret); | 
| 73 | 0 |     return NULL; | 
| 74 | 48.8k | } | 
| 75 |  |  | 
| 76 |  | void OPENSSL_LH_free(OPENSSL_LHASH *lh) | 
| 77 | 32.7k | { | 
| 78 | 32.7k |     if (lh == NULL) | 
| 79 | 54 |         return; | 
| 80 |  |  | 
| 81 | 32.7k |     OPENSSL_LH_flush(lh); | 
| 82 | 32.7k |     OPENSSL_free(lh->b); | 
| 83 | 32.7k |     OPENSSL_free(lh); | 
| 84 | 32.7k | } | 
| 85 |  |  | 
| 86 |  | void OPENSSL_LH_flush(OPENSSL_LHASH *lh) | 
| 87 | 33.6k | { | 
| 88 | 33.6k |     unsigned int i; | 
| 89 | 33.6k |     OPENSSL_LH_NODE *n, *nn; | 
| 90 |  |  | 
| 91 | 33.6k |     if (lh == NULL) | 
| 92 | 0 |         return; | 
| 93 |  |  | 
| 94 | 382k |     for (i = 0; i < lh->num_nodes; i++) { | 
| 95 | 348k |         n = lh->b[i]; | 
| 96 | 471k |         while (n != NULL) { | 
| 97 | 122k |             nn = n->next; | 
| 98 | 122k |             OPENSSL_free(n); | 
| 99 | 122k |             n = nn; | 
| 100 | 122k |         } | 
| 101 | 348k |         lh->b[i] = NULL; | 
| 102 | 348k |     } | 
| 103 |  |  | 
| 104 | 33.6k |     lh->num_items = 0; | 
| 105 | 33.6k | } | 
| 106 |  |  | 
| 107 |  | void *OPENSSL_LH_insert(OPENSSL_LHASH *lh, void *data) | 
| 108 | 571k | { | 
| 109 | 571k |     unsigned long hash; | 
| 110 | 571k |     OPENSSL_LH_NODE *nn, **rn; | 
| 111 | 571k |     void *ret; | 
| 112 |  |  | 
| 113 | 571k |     lh->error = 0; | 
| 114 | 571k |     if ((lh->up_load <= (lh->num_items * LH_LOAD_MULT / lh->num_nodes)) && !expand(lh)) | 
| 115 | 0 |         return NULL;        /* 'lh->error++' already done in 'expand' */ | 
| 116 |  |  | 
| 117 | 571k |     rn = getrn(lh, data, &hash); | 
| 118 |  |  | 
| 119 | 571k |     if (*rn == NULL) { | 
| 120 | 276k |         if ((nn = OPENSSL_malloc(sizeof(*nn))) == NULL) { | 
| 121 | 0 |             lh->error++; | 
| 122 | 0 |             return NULL; | 
| 123 | 0 |         } | 
| 124 | 276k |         nn->data = data; | 
| 125 | 276k |         nn->next = NULL; | 
| 126 | 276k |         nn->hash = hash; | 
| 127 | 276k |         *rn = nn; | 
| 128 | 276k |         ret = NULL; | 
| 129 | 276k |         lh->num_items++; | 
| 130 | 294k |     } else {                    /* replace same key */ | 
| 131 | 294k |         ret = (*rn)->data; | 
| 132 | 294k |         (*rn)->data = data; | 
| 133 | 294k |     } | 
| 134 | 571k |     return ret; | 
| 135 | 571k | } | 
| 136 |  |  | 
| 137 |  | void *OPENSSL_LH_delete(OPENSSL_LHASH *lh, const void *data) | 
| 138 | 82.4k | { | 
| 139 | 82.4k |     unsigned long hash; | 
| 140 | 82.4k |     OPENSSL_LH_NODE *nn, **rn; | 
| 141 | 82.4k |     void *ret; | 
| 142 |  |  | 
| 143 | 82.4k |     lh->error = 0; | 
| 144 | 82.4k |     rn = getrn(lh, data, &hash); | 
| 145 |  |  | 
| 146 | 82.4k |     if (*rn == NULL) { | 
| 147 | 0 |         return NULL; | 
| 148 | 82.4k |     } else { | 
| 149 | 82.4k |         nn = *rn; | 
| 150 | 82.4k |         *rn = nn->next; | 
| 151 | 82.4k |         ret = nn->data; | 
| 152 | 82.4k |         OPENSSL_free(nn); | 
| 153 | 82.4k |     } | 
| 154 |  |  | 
| 155 | 82.4k |     lh->num_items--; | 
| 156 | 82.4k |     if ((lh->num_nodes > MIN_NODES) && | 
| 157 | 82.4k |         (lh->down_load >= (lh->num_items * LH_LOAD_MULT / lh->num_nodes))) | 
| 158 | 31 |         contract(lh); | 
| 159 |  |  | 
| 160 | 82.4k |     return ret; | 
| 161 | 82.4k | } | 
| 162 |  |  | 
| 163 |  | void *OPENSSL_LH_retrieve(OPENSSL_LHASH *lh, const void *data) | 
| 164 | 240M | { | 
| 165 | 240M |     unsigned long hash; | 
| 166 | 240M |     OPENSSL_LH_NODE **rn; | 
| 167 |  |  | 
| 168 | 240M |     if (lh->error != 0) | 
| 169 | 0 |         lh->error = 0; | 
| 170 |  |  | 
| 171 | 240M |     rn = getrn(lh, data, &hash); | 
| 172 |  |  | 
| 173 | 240M |     return *rn == NULL ? NULL : (*rn)->data; | 
| 174 | 240M | } | 
| 175 |  |  | 
| 176 |  | static void doall_util_fn(OPENSSL_LHASH *lh, int use_arg, | 
| 177 |  |                           OPENSSL_LH_DOALL_FUNC func, | 
| 178 |  |                           OPENSSL_LH_DOALL_FUNCARG func_arg, void *arg) | 
| 179 | 7.10M | { | 
| 180 | 7.10M |     int i; | 
| 181 | 7.10M |     OPENSSL_LH_NODE *a, *n; | 
| 182 |  |  | 
| 183 | 7.10M |     if (lh == NULL) | 
| 184 | 0 |         return; | 
| 185 |  |  | 
| 186 |  |     /* | 
| 187 |  |      * reverse the order so we search from 'top to bottom' We were having | 
| 188 |  |      * memory leaks otherwise | 
| 189 |  |      */ | 
| 190 | 429M |     for (i = lh->num_nodes - 1; i >= 0; i--) { | 
| 191 | 422M |         a = lh->b[i]; | 
| 192 | 1.26G |         while (a != NULL) { | 
| 193 | 840M |             n = a->next; | 
| 194 | 840M |             if (use_arg) | 
| 195 | 840M |                 func_arg(a->data, arg); | 
| 196 | 69.6k |             else | 
| 197 | 69.6k |                 func(a->data); | 
| 198 | 840M |             a = n; | 
| 199 | 840M |         } | 
| 200 | 422M |     } | 
| 201 | 7.10M | } | 
| 202 |  |  | 
| 203 |  | void OPENSSL_LH_doall(OPENSSL_LHASH *lh, OPENSSL_LH_DOALL_FUNC func) | 
| 204 | 11.8k | { | 
| 205 | 11.8k |     doall_util_fn(lh, 0, func, (OPENSSL_LH_DOALL_FUNCARG)0, NULL); | 
| 206 | 11.8k | } | 
| 207 |  |  | 
| 208 |  | void OPENSSL_LH_doall_arg(OPENSSL_LHASH *lh, OPENSSL_LH_DOALL_FUNCARG func, void *arg) | 
| 209 | 7.09M | { | 
| 210 | 7.09M |     doall_util_fn(lh, 1, (OPENSSL_LH_DOALL_FUNC)0, func, arg); | 
| 211 | 7.09M | } | 
| 212 |  |  | 
| 213 |  | static int expand(OPENSSL_LHASH *lh) | 
| 214 | 123k | { | 
| 215 | 123k |     OPENSSL_LH_NODE **n, **n1, **n2, *np; | 
| 216 | 123k |     unsigned int p, pmax, nni, j; | 
| 217 | 123k |     unsigned long hash; | 
| 218 |  |  | 
| 219 | 123k |     nni = lh->num_alloc_nodes; | 
| 220 | 123k |     p = lh->p; | 
| 221 | 123k |     pmax = lh->pmax; | 
| 222 | 123k |     if (p + 1 >= pmax) { | 
| 223 | 1.80k |         j = nni * 2; | 
| 224 | 1.80k |         n = OPENSSL_realloc(lh->b, sizeof(OPENSSL_LH_NODE *) * j); | 
| 225 | 1.80k |         if (n == NULL) { | 
| 226 | 0 |             lh->error++; | 
| 227 | 0 |             return 0; | 
| 228 | 0 |         } | 
| 229 | 1.80k |         lh->b = n; | 
| 230 | 1.80k |         memset(n + nni, 0, sizeof(*n) * (j - nni)); | 
| 231 | 1.80k |         lh->pmax = nni; | 
| 232 | 1.80k |         lh->num_alloc_nodes = j; | 
| 233 | 1.80k |         lh->p = 0; | 
| 234 | 121k |     } else { | 
| 235 | 121k |         lh->p++; | 
| 236 | 121k |     } | 
| 237 |  |  | 
| 238 | 123k |     lh->num_nodes++; | 
| 239 | 123k |     n1 = &(lh->b[p]); | 
| 240 | 123k |     n2 = &(lh->b[p + pmax]); | 
| 241 | 123k |     *n2 = NULL; | 
| 242 |  |  | 
| 243 | 522k |     for (np = *n1; np != NULL;) { | 
| 244 | 399k |         hash = np->hash; | 
| 245 | 399k |         if ((hash % nni) != p) { /* move it */ | 
| 246 | 100k |             *n1 = (*n1)->next; | 
| 247 | 100k |             np->next = *n2; | 
| 248 | 100k |             *n2 = np; | 
| 249 | 100k |         } else | 
| 250 | 298k |             n1 = &((*n1)->next); | 
| 251 | 399k |         np = *n1; | 
| 252 | 399k |     } | 
| 253 |  |  | 
| 254 | 123k |     return 1; | 
| 255 | 123k | } | 
| 256 |  |  | 
| 257 |  | static void contract(OPENSSL_LHASH *lh) | 
| 258 | 25 | { | 
| 259 | 25 |     OPENSSL_LH_NODE **n, *n1, *np; | 
| 260 |  |  | 
| 261 | 25 |     np = lh->b[lh->p + lh->pmax - 1]; | 
| 262 | 25 |     lh->b[lh->p + lh->pmax - 1] = NULL; /* 24/07-92 - eay - weird but :-( */ | 
| 263 | 25 |     if (lh->p == 0) { | 
| 264 | 0 |         n = OPENSSL_realloc(lh->b, | 
| 265 | 0 |                             (unsigned int)(sizeof(OPENSSL_LH_NODE *) * lh->pmax)); | 
| 266 | 0 |         if (n == NULL) { | 
| 267 |  |             /* fputs("realloc error in lhash",stderr); */ | 
| 268 | 0 |             lh->error++; | 
| 269 | 0 |         } else { | 
| 270 | 0 |             lh->b = n; | 
| 271 | 0 |         } | 
| 272 | 0 |         lh->num_alloc_nodes /= 2; | 
| 273 | 0 |         lh->pmax /= 2; | 
| 274 | 0 |         lh->p = lh->pmax - 1; | 
| 275 | 0 |     } else | 
| 276 | 25 |         lh->p--; | 
| 277 |  |  | 
| 278 | 25 |     lh->num_nodes--; | 
| 279 |  |  | 
| 280 | 25 |     n1 = lh->b[(int)lh->p]; | 
| 281 | 25 |     if (n1 == NULL) | 
| 282 | 25 |         lh->b[(int)lh->p] = np; | 
| 283 | 0 |     else { | 
| 284 | 0 |         while (n1->next != NULL) | 
| 285 | 0 |             n1 = n1->next; | 
| 286 | 0 |         n1->next = np; | 
| 287 | 0 |     } | 
| 288 | 25 | } | 
| 289 |  |  | 
| 290 |  | static OPENSSL_LH_NODE **getrn(OPENSSL_LHASH *lh, | 
| 291 |  |                                const void *data, unsigned long *rhash) | 
| 292 | 241M | { | 
| 293 | 241M |     OPENSSL_LH_NODE **ret, *n1; | 
| 294 | 241M |     unsigned long hash, nn; | 
| 295 | 241M |     OPENSSL_LH_COMPFUNC cf; | 
| 296 |  |  | 
| 297 | 241M |     hash = (*(lh->hash)) (data); | 
| 298 | 241M |     *rhash = hash; | 
| 299 |  |  | 
| 300 | 241M |     nn = hash % lh->pmax; | 
| 301 | 241M |     if (nn < lh->p) | 
| 302 | 155M |         nn = hash % lh->num_alloc_nodes; | 
| 303 |  |  | 
| 304 | 241M |     cf = lh->comp; | 
| 305 | 241M |     ret = &(lh->b[(int)nn]); | 
| 306 | 904M |     for (n1 = *ret; n1 != NULL; n1 = n1->next) { | 
| 307 | 863M |         if (n1->hash != hash) { | 
| 308 | 662M |             ret = &(n1->next); | 
| 309 | 662M |             continue; | 
| 310 | 662M |         } | 
| 311 | 201M |         if (cf(n1->data, data) == 0) | 
| 312 | 201M |             break; | 
| 313 | 134k |         ret = &(n1->next); | 
| 314 | 134k |     } | 
| 315 | 241M |     return ret; | 
| 316 | 241M | } | 
| 317 |  |  | 
| 318 |  | /* | 
| 319 |  |  * The following hash seems to work very well on normal text strings no | 
| 320 |  |  * collisions on /usr/dict/words and it distributes on %2^n quite well, not | 
| 321 |  |  * as good as MD5, but still good. | 
| 322 |  |  */ | 
| 323 |  | unsigned long OPENSSL_LH_strhash(const char *c) | 
| 324 | 10.5M | { | 
| 325 | 10.5M |     unsigned long ret = 0; | 
| 326 | 10.5M |     long n; | 
| 327 | 10.5M |     unsigned long v; | 
| 328 | 10.5M |     int r; | 
| 329 |  |  | 
| 330 | 10.5M |     if ((c == NULL) || (*c == '\0')) | 
| 331 | 3.22M |         return ret; | 
| 332 |  |  | 
| 333 | 7.34M |     n = 0x100; | 
| 334 | 287M |     while (*c) { | 
| 335 | 280M |         v = n | (*c); | 
| 336 | 280M |         n += 0x100; | 
| 337 | 280M |         r = (int)((v >> 2) ^ v) & 0x0f; | 
| 338 |  |         /* cast to uint64_t to avoid 32 bit shift of 32 bit value */ | 
| 339 | 280M |         ret = (ret << r) | (unsigned long)((uint64_t)ret >> (32 - r)); | 
| 340 | 280M |         ret &= 0xFFFFFFFFL; | 
| 341 | 280M |         ret ^= v * v; | 
| 342 | 280M |         c++; | 
| 343 | 280M |     } | 
| 344 | 7.34M |     return (ret >> 16) ^ ret; | 
| 345 | 10.5M | } | 
| 346 |  |  | 
| 347 |  | unsigned long ossl_lh_strcasehash(const char *c) | 
| 348 | 231M | { | 
| 349 | 231M |     unsigned long ret = 0; | 
| 350 | 231M |     long n; | 
| 351 | 231M |     unsigned long v; | 
| 352 | 231M |     int r; | 
| 353 |  |  | 
| 354 | 231M |     if (c == NULL || *c == '\0') | 
| 355 | 0 |         return ret; | 
| 356 |  |  | 
| 357 | 1.43G |     for (n = 0x100; *c != '\0'; n += 0x100) { | 
| 358 | 1.19G |         v = n | ossl_tolower(*c); | 
| 359 | 1.19G |         r = (int)((v >> 2) ^ v) & 0x0f; | 
| 360 |  |         /* cast to uint64_t to avoid 32 bit shift of 32 bit value */ | 
| 361 | 1.19G |         ret = (ret << r) | (unsigned long)((uint64_t)ret >> (32 - r)); | 
| 362 | 1.19G |         ret &= 0xFFFFFFFFL; | 
| 363 | 1.19G |         ret ^= v * v; | 
| 364 | 1.19G |         c++; | 
| 365 | 1.19G |     } | 
| 366 | 231M |     return (ret >> 16) ^ ret; | 
| 367 | 231M | } | 
| 368 |  |  | 
| 369 |  | unsigned long OPENSSL_LH_num_items(const OPENSSL_LHASH *lh) | 
| 370 | 2.05M | { | 
| 371 | 2.05M |     return lh ? lh->num_items : 0; | 
| 372 | 2.05M | } | 
| 373 |  |  | 
| 374 |  | unsigned long OPENSSL_LH_get_down_load(const OPENSSL_LHASH *lh) | 
| 375 | 37.9k | { | 
| 376 | 37.9k |     return lh->down_load; | 
| 377 | 37.9k | } | 
| 378 |  |  | 
| 379 |  | void OPENSSL_LH_set_down_load(OPENSSL_LHASH *lh, unsigned long down_load) | 
| 380 | 84.5k | { | 
| 381 | 84.5k |     lh->down_load = down_load; | 
| 382 | 84.5k | } | 
| 383 |  |  | 
| 384 |  | int OPENSSL_LH_error(OPENSSL_LHASH *lh) | 
| 385 | 62.0k | { | 
| 386 | 62.0k |     return lh->error; | 
| 387 | 62.0k | } |