Coverage Report

Created: 2024-09-06 07:53

/src/libxml2/dict.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * dict.c: dictionary of reusable strings, just used to avoid allocation
3
 *         and freeing operations.
4
 *
5
 * Copyright (C) 2003-2012 Daniel Veillard.
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
15
 *
16
 * Author: daniel@veillard.com
17
 */
18
19
#define IN_LIBXML
20
#include "libxml.h"
21
22
#include <errno.h>
23
#include <limits.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "private/dict.h"
28
#include "private/error.h"
29
#include "private/globals.h"
30
#include "private/threads.h"
31
32
#include <libxml/parser.h>
33
#include <libxml/dict.h>
34
#include <libxml/xmlmemory.h>
35
#include <libxml/xmlstring.h>
36
37
#ifndef SIZE_MAX
38
225k
  #define SIZE_MAX ((size_t) -1)
39
#endif
40
41
211k
#define MAX_FILL_NUM 7
42
211k
#define MAX_FILL_DENOM 8
43
20.2k
#define MIN_HASH_SIZE 8
44
2.79M
#define MAX_HASH_SIZE (1u << 31)
45
46
typedef struct _xmlDictStrings xmlDictStrings;
47
typedef xmlDictStrings *xmlDictStringsPtr;
48
struct _xmlDictStrings {
49
    xmlDictStringsPtr next;
50
    xmlChar *free;
51
    xmlChar *end;
52
    size_t size;
53
    size_t nbStrings;
54
    xmlChar array[1];
55
};
56
57
typedef xmlHashedString xmlDictEntry;
58
59
/*
60
 * The entire dictionary
61
 */
62
struct _xmlDict {
63
    int ref_counter;
64
65
    xmlDictEntry *table;
66
    size_t size;
67
    unsigned int nbElems;
68
    xmlDictStringsPtr strings;
69
70
    struct _xmlDict *subdict;
71
    /* used for randomization */
72
    unsigned seed;
73
    /* used to impose a limit on size */
74
    size_t limit;
75
};
76
77
/*
78
 * A mutex for modifying the reference counter for shared
79
 * dictionaries.
80
 */
81
static xmlMutex xmlDictMutex;
82
83
/**
84
 * xmlInitializeDict:
85
 *
86
 * DEPRECATED: Alias for xmlInitParser.
87
 *
88
 * Returns 0.
89
 */
90
int
91
0
xmlInitializeDict(void) {
92
0
    xmlInitParser();
93
0
    return(0);
94
0
}
95
96
/**
97
 * xmlInitDictInternal:
98
 *
99
 * Initialize mutex.
100
 */
101
void
102
9.89k
xmlInitDictInternal(void) {
103
9.89k
    xmlInitMutex(&xmlDictMutex);
104
9.89k
}
105
106
/**
107
 * xmlDictCleanup:
108
 *
109
 * DEPRECATED: This function is a no-op. Call xmlCleanupParser
110
 * to free global state but see the warnings there. xmlCleanupParser
111
 * should be only called once at program exit. In most cases, you don't
112
 * have call cleanup functions at all.
113
 */
114
void
115
0
xmlDictCleanup(void) {
116
0
}
117
118
/**
119
 * xmlCleanupDictInternal:
120
 *
121
 * Free the dictionary mutex.
122
 */
123
void
124
9.89k
xmlCleanupDictInternal(void) {
125
9.89k
    xmlCleanupMutex(&xmlDictMutex);
126
9.89k
}
127
128
/*
129
 * xmlDictAddString:
130
 * @dict: the dictionary
131
 * @name: the name of the userdata
132
 * @len: the length of the name
133
 *
134
 * Add the string to the array[s]
135
 *
136
 * Returns the pointer of the local string, or NULL in case of error.
137
 */
138
static const xmlChar *
139
210k
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
140
210k
    xmlDictStringsPtr pool;
141
210k
    const xmlChar *ret;
142
210k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
143
210k
    size_t limit = 0;
144
145
210k
    pool = dict->strings;
146
212k
    while (pool != NULL) {
147
190k
  if ((size_t)(pool->end - pool->free) > namelen)
148
188k
      goto found_pool;
149
2.35k
  if (pool->size > size) size = pool->size;
150
2.35k
        limit += pool->size;
151
2.35k
  pool = pool->next;
152
2.35k
    }
153
    /*
154
     * Not found, need to allocate
155
     */
156
21.8k
    if (pool == NULL) {
157
21.8k
        if ((dict->limit > 0) && (limit > dict->limit)) {
158
0
            return(NULL);
159
0
        }
160
161
21.8k
        if (size == 0) {
162
20.2k
            size = 1000;
163
20.2k
        } else {
164
1.59k
            if (size < (SIZE_MAX - sizeof(xmlDictStrings)) / 4)
165
1.59k
                size *= 4; /* exponential growth */
166
0
            else
167
0
                size = SIZE_MAX - sizeof(xmlDictStrings);
168
1.59k
        }
169
21.8k
        if (size / 4 < namelen) {
170
673
            if ((size_t) namelen + 0 < (SIZE_MAX - sizeof(xmlDictStrings)) / 4)
171
673
                size = 4 * (size_t) namelen; /* just in case ! */
172
0
            else
173
0
                return(NULL);
174
673
        }
175
21.8k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
176
21.8k
  if (pool == NULL)
177
0
      return(NULL);
178
21.8k
  pool->size = size;
179
21.8k
  pool->nbStrings = 0;
180
21.8k
  pool->free = &pool->array[0];
181
21.8k
  pool->end = &pool->array[size];
182
21.8k
  pool->next = dict->strings;
183
21.8k
  dict->strings = pool;
184
21.8k
    }
185
210k
found_pool:
186
210k
    ret = pool->free;
187
210k
    memcpy(pool->free, name, namelen);
188
210k
    pool->free += namelen;
189
210k
    *(pool->free++) = 0;
190
210k
    pool->nbStrings++;
191
210k
    return(ret);
192
21.8k
}
193
194
/*
195
 * xmlDictAddQString:
196
 * @dict: the dictionary
197
 * @prefix: the prefix of the userdata
198
 * @plen: the prefix length
199
 * @name: the name of the userdata
200
 * @len: the length of the name
201
 *
202
 * Add the QName to the array[s]
203
 *
204
 * Returns the pointer of the local string, or NULL in case of error.
205
 */
206
static const xmlChar *
207
xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen,
208
                 const xmlChar *name, unsigned int namelen)
209
1.11k
{
210
1.11k
    xmlDictStringsPtr pool;
211
1.11k
    const xmlChar *ret;
212
1.11k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
213
1.11k
    size_t limit = 0;
214
215
1.11k
    pool = dict->strings;
216
1.23k
    while (pool != NULL) {
217
1.18k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
218
1.07k
      goto found_pool;
219
116
  if (pool->size > size) size = pool->size;
220
116
        limit += pool->size;
221
116
  pool = pool->next;
222
116
    }
223
    /*
224
     * Not found, need to allocate
225
     */
226
44
    if (pool == NULL) {
227
44
        if ((dict->limit > 0) && (limit > dict->limit)) {
228
0
            return(NULL);
229
0
        }
230
231
44
        if (size == 0) size = 1000;
232
44
  else size *= 4; /* exponential growth */
233
44
        if (size < 4 * (namelen + plen + 1))
234
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
235
44
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
236
44
  if (pool == NULL)
237
0
      return(NULL);
238
44
  pool->size = size;
239
44
  pool->nbStrings = 0;
240
44
  pool->free = &pool->array[0];
241
44
  pool->end = &pool->array[size];
242
44
  pool->next = dict->strings;
243
44
  dict->strings = pool;
244
44
    }
245
1.11k
found_pool:
246
1.11k
    ret = pool->free;
247
1.11k
    memcpy(pool->free, prefix, plen);
248
1.11k
    pool->free += plen;
249
1.11k
    *(pool->free++) = ':';
250
1.11k
    memcpy(pool->free, name, namelen);
251
1.11k
    pool->free += namelen;
252
1.11k
    *(pool->free++) = 0;
253
1.11k
    pool->nbStrings++;
254
1.11k
    return(ret);
255
44
}
256
257
/**
258
 * xmlDictCreate:
259
 *
260
 * Create a new dictionary
261
 *
262
 * Returns the newly created dictionary, or NULL if an error occurred.
263
 */
264
xmlDictPtr
265
20.2k
xmlDictCreate(void) {
266
20.2k
    xmlDictPtr dict;
267
268
20.2k
    xmlInitParser();
269
270
20.2k
    dict = xmlMalloc(sizeof(xmlDict));
271
20.2k
    if (dict == NULL)
272
0
        return(NULL);
273
20.2k
    dict->ref_counter = 1;
274
20.2k
    dict->limit = 0;
275
276
20.2k
    dict->size = 0;
277
20.2k
    dict->nbElems = 0;
278
20.2k
    dict->table = NULL;
279
20.2k
    dict->strings = NULL;
280
20.2k
    dict->subdict = NULL;
281
20.2k
    dict->seed = xmlRandom();
282
20.2k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
283
20.2k
    dict->seed = 0;
284
20.2k
#endif
285
20.2k
    return(dict);
286
20.2k
}
287
288
/**
289
 * xmlDictCreateSub:
290
 * @sub: an existing dictionary
291
 *
292
 * Create a new dictionary, inheriting strings from the read-only
293
 * dictionary @sub. On lookup, strings are first searched in the
294
 * new dictionary, then in @sub, and if not found are created in the
295
 * new dictionary.
296
 *
297
 * Returns the newly created dictionary, or NULL if an error occurred.
298
 */
299
xmlDictPtr
300
0
xmlDictCreateSub(xmlDictPtr sub) {
301
0
    xmlDictPtr dict = xmlDictCreate();
302
303
0
    if ((dict != NULL) && (sub != NULL)) {
304
0
        dict->seed = sub->seed;
305
0
        dict->subdict = sub;
306
0
  xmlDictReference(dict->subdict);
307
0
    }
308
0
    return(dict);
309
0
}
310
311
/**
312
 * xmlDictReference:
313
 * @dict: the dictionary
314
 *
315
 * Increment the reference counter of a dictionary
316
 *
317
 * Returns 0 in case of success and -1 in case of error
318
 */
319
int
320
27.8k
xmlDictReference(xmlDictPtr dict) {
321
27.8k
    if (dict == NULL) return -1;
322
27.3k
    xmlMutexLock(&xmlDictMutex);
323
27.3k
    dict->ref_counter++;
324
27.3k
    xmlMutexUnlock(&xmlDictMutex);
325
27.3k
    return(0);
326
27.8k
}
327
328
/**
329
 * xmlDictFree:
330
 * @dict: the dictionary
331
 *
332
 * Free the hash @dict and its contents. The userdata is
333
 * deallocated with @f if provided.
334
 */
335
void
336
47.5k
xmlDictFree(xmlDictPtr dict) {
337
47.5k
    xmlDictStringsPtr pool, nextp;
338
339
47.5k
    if (dict == NULL)
340
0
  return;
341
342
    /* decrement the counter, it may be shared by a parser and docs */
343
47.5k
    xmlMutexLock(&xmlDictMutex);
344
47.5k
    dict->ref_counter--;
345
47.5k
    if (dict->ref_counter > 0) {
346
27.3k
        xmlMutexUnlock(&xmlDictMutex);
347
27.3k
        return;
348
27.3k
    }
349
350
20.2k
    xmlMutexUnlock(&xmlDictMutex);
351
352
20.2k
    if (dict->subdict != NULL) {
353
0
        xmlDictFree(dict->subdict);
354
0
    }
355
356
20.2k
    if (dict->table) {
357
20.2k
  xmlFree(dict->table);
358
20.2k
    }
359
20.2k
    pool = dict->strings;
360
42.0k
    while (pool != NULL) {
361
21.8k
        nextp = pool->next;
362
21.8k
  xmlFree(pool);
363
21.8k
  pool = nextp;
364
21.8k
    }
365
20.2k
    xmlFree(dict);
366
20.2k
}
367
368
/**
369
 * xmlDictOwns:
370
 * @dict: the dictionary
371
 * @str: the string
372
 *
373
 * check if a string is owned by the dictionary
374
 *
375
 * Returns 1 if true, 0 if false and -1 in case of error
376
 * -1 in case of error
377
 */
378
int
379
419k
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
380
419k
    xmlDictStringsPtr pool;
381
382
419k
    if ((dict == NULL) || (str == NULL))
383
0
  return(-1);
384
419k
    pool = dict->strings;
385
658k
    while (pool != NULL) {
386
568k
        if ((str >= &pool->array[0]) && (str <= pool->free))
387
329k
      return(1);
388
238k
  pool = pool->next;
389
238k
    }
390
89.5k
    if (dict->subdict)
391
0
        return(xmlDictOwns(dict->subdict, str));
392
89.5k
    return(0);
393
89.5k
}
394
395
/**
396
 * xmlDictSize:
397
 * @dict: the dictionary
398
 *
399
 * Query the number of elements installed in the hash @dict.
400
 *
401
 * Returns the number of elements in the dictionary or
402
 * -1 in case of error
403
 */
404
int
405
0
xmlDictSize(xmlDictPtr dict) {
406
0
    if (dict == NULL)
407
0
  return(-1);
408
0
    if (dict->subdict)
409
0
        return(dict->nbElems + dict->subdict->nbElems);
410
0
    return(dict->nbElems);
411
0
}
412
413
/**
414
 * xmlDictSetLimit:
415
 * @dict: the dictionary
416
 * @limit: the limit in bytes
417
 *
418
 * Set a size limit for the dictionary
419
 * Added in 2.9.0
420
 *
421
 * Returns the previous limit of the dictionary or 0
422
 */
423
size_t
424
20.2k
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
425
20.2k
    size_t ret;
426
427
20.2k
    if (dict == NULL)
428
0
  return(0);
429
20.2k
    ret = dict->limit;
430
20.2k
    dict->limit = limit;
431
20.2k
    return(ret);
432
20.2k
}
433
434
/**
435
 * xmlDictGetUsage:
436
 * @dict: the dictionary
437
 *
438
 * Get how much memory is used by a dictionary for strings
439
 * Added in 2.9.0
440
 *
441
 * Returns the amount of strings allocated
442
 */
443
size_t
444
0
xmlDictGetUsage(xmlDictPtr dict) {
445
0
    xmlDictStringsPtr pool;
446
0
    size_t limit = 0;
447
448
0
    if (dict == NULL)
449
0
  return(0);
450
0
    pool = dict->strings;
451
0
    while (pool != NULL) {
452
0
        limit += pool->size;
453
0
  pool = pool->next;
454
0
    }
455
0
    return(limit);
456
0
}
457
458
/*****************************************************************
459
 *
460
 * The code below was rewritten and is additionally licensed under
461
 * the main license in file 'Copyright'.
462
 *
463
 *****************************************************************/
464
465
ATTRIBUTE_NO_SANITIZE_INTEGER
466
static unsigned
467
xmlDictHashName(unsigned seed, const xmlChar* data, size_t maxLen,
468
2.77M
                size_t *plen) {
469
2.77M
    unsigned h1, h2;
470
2.77M
    size_t i;
471
472
2.77M
    HASH_INIT(h1, h2, seed);
473
474
537M
    for (i = 0; i < maxLen && data[i]; i++) {
475
534M
        HASH_UPDATE(h1, h2, data[i]);
476
534M
    }
477
478
2.77M
    HASH_FINISH(h1, h2);
479
480
2.77M
    *plen = i;
481
2.77M
    return(h2 | MAX_HASH_SIZE);
482
2.77M
}
483
484
ATTRIBUTE_NO_SANITIZE_INTEGER
485
static unsigned
486
xmlDictHashQName(unsigned seed, const xmlChar *prefix, const xmlChar *name,
487
2.25k
                 size_t *pplen, size_t *plen) {
488
2.25k
    unsigned h1, h2;
489
2.25k
    size_t i;
490
491
2.25k
    HASH_INIT(h1, h2, seed);
492
493
1.02M
    for (i = 0; prefix[i] != 0; i++) {
494
1.02M
        HASH_UPDATE(h1, h2, prefix[i]);
495
1.02M
    }
496
2.25k
    *pplen = i;
497
498
2.25k
    HASH_UPDATE(h1, h2, ':');
499
500
1.02M
    for (i = 0; name[i] != 0; i++) {
501
1.02M
        HASH_UPDATE(h1, h2, name[i]);
502
1.02M
    }
503
2.25k
    *plen = i;
504
505
2.25k
    HASH_FINISH(h1, h2);
506
507
    /*
508
     * Always set the upper bit of hash values since 0 means an unoccupied
509
     * bucket.
510
     */
511
2.25k
    return(h2 | MAX_HASH_SIZE);
512
2.25k
}
513
514
/**
515
 * xmlDictComputeHash:
516
 * @dict:  dictionary
517
 * @string:  C string
518
 *
519
 * Compute the hash value of a C string.
520
 *
521
 * Returns the hash value.
522
 */
523
unsigned
524
7.37k
xmlDictComputeHash(const xmlDict *dict, const xmlChar *string) {
525
7.37k
    size_t len;
526
7.37k
    return(xmlDictHashName(dict->seed, string, SIZE_MAX, &len));
527
7.37k
}
528
529
542k
#define HASH_ROL31(x,n) ((x) << (n) | ((x) & 0x7FFFFFFF) >> (31 - (n)))
530
531
/**
532
 * xmlDictCombineHash:
533
 * @v1:  first hash value
534
 * @v2: second hash value
535
 *
536
 * Combine two hash values.
537
 *
538
 * Returns the combined hash value.
539
 */
540
ATTRIBUTE_NO_SANITIZE_INTEGER
541
unsigned
542
542k
xmlDictCombineHash(unsigned v1, unsigned v2) {
543
    /*
544
     * The upper bit of hash values is always set, so we have to operate on
545
     * 31-bit hashes here.
546
     */
547
542k
    v1 ^= v2;
548
542k
    v1 += HASH_ROL31(v2, 5);
549
550
542k
    return((v1 & 0xFFFFFFFF) | 0x80000000);
551
542k
}
552
553
/**
554
 * xmlDictFindEntry:
555
 * @dict: dict
556
 * @prefix: optional QName prefix
557
 * @name: string
558
 * @len: length of string
559
 * @hashValue: valid hash value of string
560
 * @pfound: result of search
561
 *
562
 * Try to find a matching hash table entry. If an entry was found, set
563
 * @found to 1 and return the entry. Otherwise, set @found to 0 and return
564
 * the location where a new entry should be inserted.
565
 */
566
ATTRIBUTE_NO_SANITIZE_INTEGER
567
static xmlDictEntry *
568
xmlDictFindEntry(const xmlDict *dict, const xmlChar *prefix,
569
                 const xmlChar *name, int len, unsigned hashValue,
570
2.75M
                 int *pfound) {
571
2.75M
    xmlDictEntry *entry;
572
2.75M
    unsigned mask, pos, displ;
573
2.75M
    int found = 0;
574
575
2.75M
    mask = dict->size - 1;
576
2.75M
    pos = hashValue & mask;
577
2.75M
    entry = &dict->table[pos];
578
579
2.75M
    if (entry->hashValue != 0) {
580
        /*
581
         * Robin hood hashing: abort if the displacement of the entry
582
         * is smaller than the displacement of the key we look for.
583
         * This also stops at the correct position when inserting.
584
         */
585
2.65M
        displ = 0;
586
587
4.48M
        do {
588
4.48M
            if (entry->hashValue == hashValue) {
589
2.56M
                if (prefix == NULL) {
590
                    /*
591
                     * name is not necessarily null-terminated.
592
                     */
593
2.56M
                    if ((strncmp((const char *) entry->name,
594
2.56M
                                 (const char *) name, len) == 0) &&
595
2.56M
                        (entry->name[len] == 0)) {
596
2.56M
                        found = 1;
597
2.56M
                        break;
598
2.56M
                    }
599
2.56M
                } else {
600
1.14k
                    if (xmlStrQEqual(prefix, name, entry->name)) {
601
1.14k
                        found = 1;
602
1.14k
                        break;
603
1.14k
                    }
604
1.14k
                }
605
2.56M
            }
606
607
1.92M
            displ++;
608
1.92M
            pos++;
609
1.92M
            entry++;
610
1.92M
            if ((pos & mask) == 0)
611
74.1k
                entry = dict->table;
612
1.92M
        } while ((entry->hashValue != 0) &&
613
1.92M
                 (((pos - entry->hashValue) & mask) >= displ));
614
2.65M
    }
615
616
0
    *pfound = found;
617
2.75M
    return(entry);
618
2.75M
}
619
620
/**
621
 * xmlDictGrow:
622
 * @dict: dictionary
623
 * @size: new size of the dictionary
624
 *
625
 * Resize the dictionary hash table.
626
 *
627
 * Returns 0 in case of success, -1 if a memory allocation failed.
628
 */
629
static int
630
31.2k
xmlDictGrow(xmlDictPtr dict, unsigned size) {
631
31.2k
    const xmlDictEntry *oldentry, *oldend, *end;
632
31.2k
    xmlDictEntry *table;
633
31.2k
    unsigned oldsize, i;
634
635
    /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
636
31.2k
    if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
637
0
        return(-1);
638
31.2k
    table = xmlMalloc(size * sizeof(table[0]));
639
31.2k
    if (table == NULL)
640
0
        return(-1);
641
31.2k
    memset(table, 0, size * sizeof(table[0]));
642
643
31.2k
    oldsize = dict->size;
644
31.2k
    if (oldsize == 0)
645
20.2k
        goto done;
646
647
11.0k
    oldend = &dict->table[oldsize];
648
11.0k
    end = &table[size];
649
650
    /*
651
     * Robin Hood sorting order is maintained if we
652
     *
653
     * - compute dict indices with modulo
654
     * - resize by an integer factor
655
     * - start to copy from the beginning of a probe sequence
656
     */
657
11.0k
    oldentry = dict->table;
658
70.8k
    while (oldentry->hashValue != 0) {
659
59.7k
        if (++oldentry >= oldend)
660
0
            oldentry = dict->table;
661
59.7k
    }
662
663
212k
    for (i = 0; i < oldsize; i++) {
664
200k
        if (oldentry->hashValue != 0) {
665
175k
            xmlDictEntry *entry = &table[oldentry->hashValue & (size - 1)];
666
667
231k
            while (entry->hashValue != 0) {
668
56.1k
                if (++entry >= end)
669
3.75k
                    entry = table;
670
56.1k
            }
671
175k
            *entry = *oldentry;
672
175k
        }
673
674
200k
        if (++oldentry >= oldend)
675
11.0k
            oldentry = dict->table;
676
200k
    }
677
678
11.0k
    xmlFree(dict->table);
679
680
31.2k
done:
681
31.2k
    dict->table = table;
682
31.2k
    dict->size = size;
683
684
31.2k
    return(0);
685
11.0k
}
686
687
/**
688
 * xmlDictLookupInternal:
689
 * @dict: dict
690
 * @prefix: optional QName prefix
691
 * @name: string
692
 * @maybeLen: length of string or -1 if unknown
693
 * @update: whether the string should be added
694
 *
695
 * Internal lookup and update function.
696
 */
697
ATTRIBUTE_NO_SANITIZE_INTEGER
698
static const xmlDictEntry *
699
xmlDictLookupInternal(xmlDictPtr dict, const xmlChar *prefix,
700
2.77M
                      const xmlChar *name, int maybeLen, int update) {
701
2.77M
    xmlDictEntry *entry = NULL;
702
2.77M
    const xmlChar *ret;
703
2.77M
    unsigned hashValue;
704
2.77M
    size_t maxLen, len, plen, klen;
705
2.77M
    int found = 0;
706
707
2.77M
    if ((dict == NULL) || (name == NULL))
708
0
  return(NULL);
709
710
2.77M
    maxLen = (maybeLen < 0) ? SIZE_MAX : (size_t) maybeLen;
711
712
2.77M
    if (prefix == NULL) {
713
2.77M
        hashValue = xmlDictHashName(dict->seed, name, maxLen, &len);
714
2.77M
        if (len > INT_MAX / 2)
715
0
            return(NULL);
716
2.77M
        klen = len;
717
2.77M
    } else {
718
2.25k
        hashValue = xmlDictHashQName(dict->seed, prefix, name, &plen, &len);
719
2.25k
        if ((len > INT_MAX / 2) || (plen >= INT_MAX / 2 - len))
720
0
            return(NULL);
721
2.25k
        klen = plen + 1 + len;
722
2.25k
    }
723
724
2.77M
    if ((dict->limit > 0) && (klen >= dict->limit))
725
0
        return(NULL);
726
727
    /*
728
     * Check for an existing entry
729
     */
730
2.77M
    if (dict->size > 0)
731
2.75M
        entry = xmlDictFindEntry(dict, prefix, name, klen, hashValue, &found);
732
2.77M
    if (found)
733
2.56M
        return(entry);
734
735
211k
    if ((dict->subdict != NULL) && (dict->subdict->size > 0)) {
736
0
        xmlDictEntry *subEntry;
737
0
        unsigned subHashValue;
738
739
0
        if (prefix == NULL)
740
0
            subHashValue = xmlDictHashName(dict->subdict->seed, name, len,
741
0
                                           &len);
742
0
        else
743
0
            subHashValue = xmlDictHashQName(dict->subdict->seed, prefix, name,
744
0
                                            &plen, &len);
745
0
        subEntry = xmlDictFindEntry(dict->subdict, prefix, name, klen,
746
0
                                    subHashValue, &found);
747
0
        if (found)
748
0
            return(subEntry);
749
0
    }
750
751
211k
    if (!update)
752
0
        return(NULL);
753
754
    /*
755
     * Grow the hash table if needed
756
     */
757
211k
    if (dict->nbElems + 1 > dict->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
758
31.2k
        unsigned newSize, mask, displ, pos;
759
760
31.2k
        if (dict->size == 0) {
761
20.2k
            newSize = MIN_HASH_SIZE;
762
20.2k
        } else {
763
11.0k
            if (dict->size >= MAX_HASH_SIZE)
764
0
                return(NULL);
765
11.0k
            newSize = dict->size * 2;
766
11.0k
        }
767
31.2k
        if (xmlDictGrow(dict, newSize) != 0)
768
0
            return(NULL);
769
770
        /*
771
         * Find new entry
772
         */
773
31.2k
        mask = dict->size - 1;
774
31.2k
        displ = 0;
775
31.2k
        pos = hashValue & mask;
776
31.2k
        entry = &dict->table[pos];
777
778
37.8k
        while ((entry->hashValue != 0) &&
779
37.8k
               ((pos - entry->hashValue) & mask) >= displ) {
780
6.60k
            displ++;
781
6.60k
            pos++;
782
6.60k
            entry++;
783
6.60k
            if ((pos & mask) == 0)
784
515
                entry = dict->table;
785
6.60k
        }
786
31.2k
    }
787
788
211k
    if (prefix == NULL)
789
210k
        ret = xmlDictAddString(dict, name, len);
790
1.11k
    else
791
1.11k
        ret = xmlDictAddQString(dict, prefix, plen, name, len);
792
211k
    if (ret == NULL)
793
0
        return(NULL);
794
795
    /*
796
     * Shift the remainder of the probe sequence to the right
797
     */
798
211k
    if (entry->hashValue != 0) {
799
47.9k
        const xmlDictEntry *end = &dict->table[dict->size];
800
47.9k
        const xmlDictEntry *cur = entry;
801
802
201k
        do {
803
201k
            cur++;
804
201k
            if (cur >= end)
805
9.60k
                cur = dict->table;
806
201k
        } while (cur->hashValue != 0);
807
808
47.9k
        if (cur < entry) {
809
            /*
810
             * If we traversed the end of the buffer, handle the part
811
             * at the start of the buffer.
812
             */
813
9.60k
            memmove(&dict->table[1], dict->table,
814
9.60k
                    (char *) cur - (char *) dict->table);
815
9.60k
            cur = end - 1;
816
9.60k
            dict->table[0] = *cur;
817
9.60k
        }
818
819
47.9k
        memmove(&entry[1], entry, (char *) cur - (char *) entry);
820
47.9k
    }
821
822
    /*
823
     * Populate entry
824
     */
825
211k
    entry->hashValue = hashValue;
826
211k
    entry->name = ret;
827
828
211k
    dict->nbElems++;
829
830
211k
    return(entry);
831
211k
}
832
833
/**
834
 * xmlDictLookup:
835
 * @dict: dictionary
836
 * @name: string key
837
 * @len: length of the key, if -1 it is recomputed
838
 *
839
 * Lookup a string and add it to the dictionary if it wasn't found.
840
 *
841
 * Returns the interned copy of the string or NULL if a memory allocation
842
 * failed.
843
 */
844
const xmlChar *
845
1.20M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
846
1.20M
    const xmlDictEntry *entry;
847
848
1.20M
    entry = xmlDictLookupInternal(dict, NULL, name, len, 1);
849
1.20M
    if (entry == NULL)
850
0
        return(NULL);
851
1.20M
    return(entry->name);
852
1.20M
}
853
854
/**
855
 * xmlDictLookupHashed:
856
 * @dict: dictionary
857
 * @name: string key
858
 * @len: length of the key, if -1 it is recomputed
859
 *
860
 * Lookup a dictionary entry and add the string to the dictionary if
861
 * it wasn't found.
862
 *
863
 * Returns the dictionary entry.
864
 */
865
xmlHashedString
866
1.57M
xmlDictLookupHashed(xmlDictPtr dict, const xmlChar *name, int len) {
867
1.57M
    const xmlDictEntry *entry;
868
1.57M
    xmlHashedString ret;
869
870
1.57M
    entry = xmlDictLookupInternal(dict, NULL, name, len, 1);
871
872
1.57M
    if (entry == NULL) {
873
0
        ret.name = NULL;
874
0
        ret.hashValue = 0;
875
1.57M
    } else {
876
1.57M
        ret = *entry;
877
1.57M
    }
878
879
1.57M
    return(ret);
880
1.57M
}
881
882
/**
883
 * xmlDictExists:
884
 * @dict: the dictionary
885
 * @name: the name of the userdata
886
 * @len: the length of the name, if -1 it is recomputed
887
 *
888
 * Check if a string exists in the dictionary.
889
 *
890
 * Returns the internal copy of the name or NULL if not found.
891
 */
892
const xmlChar *
893
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
894
0
    const xmlDictEntry *entry;
895
896
0
    entry = xmlDictLookupInternal(dict, NULL, name, len, 0);
897
0
    if (entry == NULL)
898
0
        return(NULL);
899
0
    return(entry->name);
900
0
}
901
902
/**
903
 * xmlDictQLookup:
904
 * @dict: the dictionary
905
 * @prefix: the prefix
906
 * @name: the name
907
 *
908
 * Lookup the QName @prefix:@name and add it to the dictionary if
909
 * it wasn't found.
910
 *
911
 * Returns the interned copy of the string or NULL if a memory allocation
912
 * failed.
913
 */
914
const xmlChar *
915
2.25k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
916
2.25k
    const xmlDictEntry *entry;
917
918
2.25k
    entry = xmlDictLookupInternal(dict, prefix, name, -1, 1);
919
2.25k
    if (entry == NULL)
920
0
        return(NULL);
921
2.25k
    return(entry->name);
922
2.25k
}
923
924
/*
925
 * Pseudo-random generator
926
 */
927
928
#ifdef _WIN32
929
  #define WIN32_LEAN_AND_MEAN
930
  #include <windows.h>
931
  #include <bcrypt.h>
932
#elif HAVE_DECL_GETENTROPY
933
  #include <unistd.h>
934
  #include <sys/random.h>
935
#else
936
  #include <time.h>
937
#endif
938
939
static xmlMutex xmlRngMutex;
940
941
static unsigned globalRngState[2];
942
943
/*
944
 * xmlInitRandom:
945
 *
946
 * Initialize the PRNG.
947
 */
948
ATTRIBUTE_NO_SANITIZE_INTEGER
949
void
950
9.89k
xmlInitRandom(void) {
951
9.89k
    xmlInitMutex(&xmlRngMutex);
952
953
9.89k
    {
954
#ifdef _WIN32
955
        NTSTATUS status;
956
957
        status = BCryptGenRandom(NULL, (unsigned char *) globalRngState,
958
                                 sizeof(globalRngState),
959
                                 BCRYPT_USE_SYSTEM_PREFERRED_RNG);
960
        if (!BCRYPT_SUCCESS(status))
961
            xmlAbort("libxml2: BCryptGenRandom failed with error code %lu\n",
962
                     GetLastError());
963
#elif HAVE_DECL_GETENTROPY
964
9.89k
        while (1) {
965
9.89k
            if (getentropy(globalRngState, sizeof(globalRngState)) == 0)
966
9.89k
                break;
967
968
0
            if (errno != EINTR)
969
0
                xmlAbort("libxml2: getentropy failed with error code %d\n",
970
0
                         errno);
971
0
        }
972
#else
973
        int var;
974
975
        globalRngState[0] =
976
                (unsigned) time(NULL) ^
977
                HASH_ROL((unsigned) ((size_t) &xmlInitRandom & 0xFFFFFFFF), 8);
978
        globalRngState[1] =
979
                HASH_ROL((unsigned) ((size_t) &xmlRngMutex & 0xFFFFFFFF), 16) ^
980
                HASH_ROL((unsigned) ((size_t) &var & 0xFFFFFFFF), 24);
981
#endif
982
9.89k
    }
983
9.89k
}
984
985
/*
986
 * xmlCleanupRandom:
987
 *
988
 * Clean up PRNG globals.
989
 */
990
void
991
9.89k
xmlCleanupRandom(void) {
992
9.89k
    xmlCleanupMutex(&xmlRngMutex);
993
9.89k
}
994
995
ATTRIBUTE_NO_SANITIZE_INTEGER
996
static unsigned
997
52.7k
xoroshiro64ss(unsigned *s) {
998
52.7k
    unsigned s0 = s[0];
999
52.7k
    unsigned s1 = s[1];
1000
52.7k
    unsigned result = HASH_ROL(s0 * 0x9E3779BB, 5) * 5;
1001
1002
52.7k
    s1 ^= s0;
1003
52.7k
    s[0] = HASH_ROL(s0, 26) ^ s1 ^ (s1 << 9);
1004
52.7k
    s[1] = HASH_ROL(s1, 13);
1005
1006
52.7k
    return(result & 0xFFFFFFFF);
1007
52.7k
}
1008
1009
/*
1010
 * xmlGlobalRandom:
1011
 *
1012
 * Generate a pseudo-random value using the global PRNG.
1013
 *
1014
 * Returns a random value.
1015
 */
1016
unsigned
1017
19.7k
xmlGlobalRandom(void) {
1018
19.7k
    unsigned ret;
1019
1020
19.7k
    xmlMutexLock(&xmlRngMutex);
1021
19.7k
    ret = xoroshiro64ss(globalRngState);
1022
19.7k
    xmlMutexUnlock(&xmlRngMutex);
1023
1024
19.7k
    return(ret);
1025
19.7k
}
1026
1027
/*
1028
 * xmlRandom:
1029
 *
1030
 * Generate a pseudo-random value using the thread-local PRNG.
1031
 *
1032
 * Returns a random value.
1033
 */
1034
unsigned
1035
32.9k
xmlRandom(void) {
1036
32.9k
#ifdef LIBXML_THREAD_ENABLED
1037
32.9k
    return(xoroshiro64ss(xmlGetLocalRngState()));
1038
#else
1039
    return(xmlGlobalRandom());
1040
#endif
1041
32.9k
}
1042