Coverage Report

Created: 2022-06-08 06:16

/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 <limits.h>
23
#include <stdlib.h>
24
#include <time.h>
25
26
/*
27
 * Following http://www.ocert.org/advisories/ocert-2011-003.html
28
 * it seems that having hash randomization might be a good idea
29
 * when using XML with untrusted data
30
 * Note1: that it works correctly only if compiled with WITH_BIG_KEY
31
 *  which is the default.
32
 * Note2: the fast function used for a small dict won't protect very
33
 *  well but since the attack is based on growing a very big hash
34
 *  list we will use the BigKey algo as soon as the hash size grows
35
 *  over MIN_DICT_SIZE so this actually works
36
 */
37
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
38
#define DICT_RANDOMIZATION
39
#endif
40
41
#include <string.h>
42
#ifdef HAVE_STDINT_H
43
#include <stdint.h>
44
#else
45
#ifdef HAVE_INTTYPES_H
46
#include <inttypes.h>
47
#elif defined(_WIN32)
48
typedef unsigned __int32 uint32_t;
49
#endif
50
#endif
51
#include <libxml/tree.h>
52
#include <libxml/dict.h>
53
#include <libxml/xmlmemory.h>
54
#include <libxml/xmlerror.h>
55
#include <libxml/globals.h>
56
57
/* #define DEBUG_GROW */
58
/* #define DICT_DEBUG_PATTERNS */
59
60
159k
#define MAX_HASH_LEN 3
61
21.5M
#define MIN_DICT_SIZE 128
62
470
#define MAX_DICT_HASH 8 * 2048
63
#define WITH_BIG_KEY
64
65
#ifdef WITH_BIG_KEY
66
#define xmlDictComputeKey(dict, name, len)                              \
67
20.5M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
68
20.5M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
69
20.5M
     xmlDictComputeBigKey(name, len, (dict)->seed))
70
71
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
72
0
    (((prefix) == NULL) ?                                               \
73
0
      (xmlDictComputeKey(dict, name, len)) :                             \
74
0
      (((dict)->size == MIN_DICT_SIZE) ?                                \
75
0
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
76
0
       xmlDictComputeBigQKey(prefix, plen, name, len, (dict)->seed)))
77
78
#else /* !WITH_BIG_KEY */
79
#define xmlDictComputeKey(dict, name, len)                              \
80
        xmlDictComputeFastKey(name, len, (dict)->seed)
81
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
82
        xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed)
83
#endif /* WITH_BIG_KEY */
84
85
/*
86
 * An entry in the dictionary
87
 */
88
typedef struct _xmlDictEntry xmlDictEntry;
89
typedef xmlDictEntry *xmlDictEntryPtr;
90
struct _xmlDictEntry {
91
    struct _xmlDictEntry *next;
92
    const xmlChar *name;
93
    unsigned int len;
94
    int valid;
95
    unsigned long okey;
96
};
97
98
typedef struct _xmlDictStrings xmlDictStrings;
99
typedef xmlDictStrings *xmlDictStringsPtr;
100
struct _xmlDictStrings {
101
    xmlDictStringsPtr next;
102
    xmlChar *free;
103
    xmlChar *end;
104
    size_t size;
105
    size_t nbStrings;
106
    xmlChar array[1];
107
};
108
/*
109
 * The entire dictionary
110
 */
111
struct _xmlDict {
112
    int ref_counter;
113
114
    struct _xmlDictEntry *dict;
115
    size_t size;
116
    unsigned int nbElems;
117
    xmlDictStringsPtr strings;
118
119
    struct _xmlDict *subdict;
120
    /* used for randomization */
121
    int seed;
122
    /* used to impose a limit on size */
123
    size_t limit;
124
};
125
126
/*
127
 * A mutex for modifying the reference counter for shared
128
 * dictionaries.
129
 */
130
static xmlMutexPtr xmlDictMutex = NULL;
131
132
/*
133
 * Whether the dictionary mutex was initialized.
134
 */
135
static int xmlDictInitialized = 0;
136
137
#ifdef DICT_RANDOMIZATION
138
#ifdef HAVE_RAND_R
139
/*
140
 * Internal data for random function, protected by xmlDictMutex
141
 */
142
static unsigned int rand_seed = 0;
143
#endif
144
#endif
145
146
/**
147
 * xmlInitializeDict:
148
 *
149
 * DEPRECATED: This function will be made private. Call xmlInitParser to
150
 * initialize the library.
151
 *
152
 * Do the dictionary mutex initialization.
153
 *
154
 * Returns 0 if initialization was already done, and 1 if that
155
 * call led to the initialization
156
 */
157
1
int xmlInitializeDict(void) {
158
1
    return(0);
159
1
}
160
161
/**
162
 * __xmlInitializeDict:
163
 *
164
 * This function is not public
165
 * Do the dictionary mutex initialization.
166
 * this function is not thread safe, initialization should
167
 * normally be done once at setup when called from xmlOnceInit()
168
 * we may also land in this code if thread support is not compiled in
169
 *
170
 * Returns 0 if initialization was already done, and 1 if that
171
 * call led to the initialization
172
 */
173
1
int __xmlInitializeDict(void) {
174
1
    if (xmlDictInitialized)
175
0
        return(1);
176
177
1
    if ((xmlDictMutex = xmlNewMutex()) == NULL)
178
0
        return(0);
179
1
    xmlMutexLock(xmlDictMutex);
180
181
#ifdef DICT_RANDOMIZATION
182
#ifdef HAVE_RAND_R
183
    rand_seed = time(NULL);
184
    rand_r(& rand_seed);
185
#else
186
    srand(time(NULL));
187
#endif
188
#endif
189
1
    xmlDictInitialized = 1;
190
1
    xmlMutexUnlock(xmlDictMutex);
191
1
    return(1);
192
1
}
193
194
#ifdef DICT_RANDOMIZATION
195
int __xmlRandom(void) {
196
    int ret;
197
198
    if (xmlDictInitialized == 0)
199
        __xmlInitializeDict();
200
201
    xmlMutexLock(xmlDictMutex);
202
#ifdef HAVE_RAND_R
203
    ret = rand_r(& rand_seed);
204
#else
205
    ret = rand();
206
#endif
207
    xmlMutexUnlock(xmlDictMutex);
208
    return(ret);
209
}
210
#endif
211
212
/**
213
 * xmlDictCleanup:
214
 *
215
 * DEPRECATED: This function will be made private. Call xmlCleanupParser
216
 * to free global state but see the warnings there. xmlCleanupParser
217
 * should be only called once at program exit. In most cases, you don't
218
 * have call cleanup functions at all.
219
 *
220
 * Free the dictionary mutex. Do not call unless sure the library
221
 * is not in use anymore !
222
 */
223
void
224
0
xmlDictCleanup(void) {
225
0
    if (!xmlDictInitialized)
226
0
        return;
227
228
0
    xmlFreeMutex(xmlDictMutex);
229
230
0
    xmlDictInitialized = 0;
231
0
}
232
233
/*
234
 * xmlDictAddString:
235
 * @dict: the dictionary
236
 * @name: the name of the userdata
237
 * @len: the length of the name
238
 *
239
 * Add the string to the array[s]
240
 *
241
 * Returns the pointer of the local string, or NULL in case of error.
242
 */
243
static const xmlChar *
244
158k
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
245
158k
    xmlDictStringsPtr pool;
246
158k
    const xmlChar *ret;
247
158k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
248
158k
    size_t limit = 0;
249
250
#ifdef DICT_DEBUG_PATTERNS
251
    fprintf(stderr, "-");
252
#endif
253
158k
    pool = dict->strings;
254
160k
    while (pool != NULL) {
255
140k
  if ((size_t)(pool->end - pool->free) > namelen)
256
138k
      goto found_pool;
257
2.27k
  if (pool->size > size) size = pool->size;
258
2.27k
        limit += pool->size;
259
2.27k
  pool = pool->next;
260
2.27k
    }
261
    /*
262
     * Not found, need to allocate
263
     */
264
20.2k
    if (pool == NULL) {
265
20.2k
        if ((dict->limit > 0) && (limit > dict->limit)) {
266
0
            return(NULL);
267
0
        }
268
269
20.2k
        if (size == 0) size = 1000;
270
1.26k
  else size *= 4; /* exponential growth */
271
20.2k
        if (size < 4 * namelen)
272
784
      size = 4 * namelen; /* just in case ! */
273
20.2k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
274
20.2k
  if (pool == NULL)
275
0
      return(NULL);
276
20.2k
  pool->size = size;
277
20.2k
  pool->nbStrings = 0;
278
20.2k
  pool->free = &pool->array[0];
279
20.2k
  pool->end = &pool->array[size];
280
20.2k
  pool->next = dict->strings;
281
20.2k
  dict->strings = pool;
282
#ifdef DICT_DEBUG_PATTERNS
283
        fprintf(stderr, "+");
284
#endif
285
20.2k
    }
286
158k
found_pool:
287
158k
    ret = pool->free;
288
158k
    memcpy(pool->free, name, namelen);
289
158k
    pool->free += namelen;
290
158k
    *(pool->free++) = 0;
291
158k
    pool->nbStrings++;
292
158k
    return(ret);
293
20.2k
}
294
295
/*
296
 * xmlDictAddQString:
297
 * @dict: the dictionary
298
 * @prefix: the prefix of the userdata
299
 * @plen: the prefix length
300
 * @name: the name of the userdata
301
 * @len: the length of the name
302
 *
303
 * Add the QName to the array[s]
304
 *
305
 * Returns the pointer of the local string, or NULL in case of error.
306
 */
307
static const xmlChar *
308
xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen,
309
                 const xmlChar *name, unsigned int namelen)
310
0
{
311
0
    xmlDictStringsPtr pool;
312
0
    const xmlChar *ret;
313
0
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
314
0
    size_t limit = 0;
315
316
0
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
317
318
#ifdef DICT_DEBUG_PATTERNS
319
    fprintf(stderr, "=");
320
#endif
321
0
    pool = dict->strings;
322
0
    while (pool != NULL) {
323
0
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
324
0
      goto found_pool;
325
0
  if (pool->size > size) size = pool->size;
326
0
        limit += pool->size;
327
0
  pool = pool->next;
328
0
    }
329
    /*
330
     * Not found, need to allocate
331
     */
332
0
    if (pool == NULL) {
333
0
        if ((dict->limit > 0) && (limit > dict->limit)) {
334
0
            return(NULL);
335
0
        }
336
337
0
        if (size == 0) size = 1000;
338
0
  else size *= 4; /* exponential growth */
339
0
        if (size < 4 * (namelen + plen + 1))
340
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
341
0
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
342
0
  if (pool == NULL)
343
0
      return(NULL);
344
0
  pool->size = size;
345
0
  pool->nbStrings = 0;
346
0
  pool->free = &pool->array[0];
347
0
  pool->end = &pool->array[size];
348
0
  pool->next = dict->strings;
349
0
  dict->strings = pool;
350
#ifdef DICT_DEBUG_PATTERNS
351
        fprintf(stderr, "+");
352
#endif
353
0
    }
354
0
found_pool:
355
0
    ret = pool->free;
356
0
    memcpy(pool->free, prefix, plen);
357
0
    pool->free += plen;
358
0
    *(pool->free++) = ':';
359
0
    memcpy(pool->free, name, namelen);
360
0
    pool->free += namelen;
361
0
    *(pool->free++) = 0;
362
0
    pool->nbStrings++;
363
0
    return(ret);
364
0
}
365
366
#ifdef WITH_BIG_KEY
367
/*
368
 * xmlDictComputeBigKey:
369
 *
370
 * Calculate a hash key using a good hash function that works well for
371
 * larger hash table sizes.
372
 *
373
 * Hash function by "One-at-a-Time Hash" see
374
 * http://burtleburtle.net/bob/hash/doobs.html
375
 */
376
377
#ifdef __clang__
378
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
379
#endif
380
static uint32_t
381
4.69M
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
382
4.69M
    uint32_t hash;
383
4.69M
    int i;
384
385
4.69M
    if (namelen <= 0 || data == NULL) return(0);
386
387
4.69M
    hash = seed;
388
389
211M
    for (i = 0;i < namelen; i++) {
390
206M
        hash += data[i];
391
206M
  hash += (hash << 10);
392
206M
  hash ^= (hash >> 6);
393
206M
    }
394
4.69M
    hash += (hash << 3);
395
4.69M
    hash ^= (hash >> 11);
396
4.69M
    hash += (hash << 15);
397
398
4.69M
    return hash;
399
4.69M
}
400
401
/*
402
 * xmlDictComputeBigQKey:
403
 *
404
 * Calculate a hash key for two strings using a good hash function
405
 * that works well for larger hash table sizes.
406
 *
407
 * Hash function by "One-at-a-Time Hash" see
408
 * http://burtleburtle.net/bob/hash/doobs.html
409
 *
410
 * Neither of the two strings must be NULL.
411
 */
412
#ifdef __clang__
413
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
414
#endif
415
static unsigned long
416
xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
417
                      const xmlChar *name, int len, int seed)
418
0
{
419
0
    uint32_t hash;
420
0
    int i;
421
422
0
    hash = seed;
423
424
0
    for (i = 0;i < plen; i++) {
425
0
        hash += prefix[i];
426
0
  hash += (hash << 10);
427
0
  hash ^= (hash >> 6);
428
0
    }
429
0
    hash += ':';
430
0
    hash += (hash << 10);
431
0
    hash ^= (hash >> 6);
432
433
0
    for (i = 0;i < len; i++) {
434
0
        hash += name[i];
435
0
  hash += (hash << 10);
436
0
  hash ^= (hash >> 6);
437
0
    }
438
0
    hash += (hash << 3);
439
0
    hash ^= (hash >> 11);
440
0
    hash += (hash << 15);
441
442
0
    return hash;
443
0
}
444
#endif /* WITH_BIG_KEY */
445
446
/*
447
 * xmlDictComputeFastKey:
448
 *
449
 * Calculate a hash key using a fast hash function that works well
450
 * for low hash table fill.
451
 */
452
static unsigned long
453
15.8M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
454
15.8M
    unsigned long value = seed;
455
456
15.8M
    if (name == NULL) return(0);
457
15.8M
    value += *name;
458
15.8M
    value <<= 5;
459
15.8M
    if (namelen > 10) {
460
1.03M
        value += name[namelen - 1];
461
1.03M
        namelen = 10;
462
1.03M
    }
463
15.8M
    switch (namelen) {
464
1.05M
        case 10: value += name[9];
465
        /* Falls through. */
466
1.11M
        case 9: value += name[8];
467
        /* Falls through. */
468
1.14M
        case 8: value += name[7];
469
        /* Falls through. */
470
1.23M
        case 7: value += name[6];
471
        /* Falls through. */
472
2.11M
        case 6: value += name[5];
473
        /* Falls through. */
474
2.91M
        case 5: value += name[4];
475
        /* Falls through. */
476
3.01M
        case 4: value += name[3];
477
        /* Falls through. */
478
3.98M
        case 3: value += name[2];
479
        /* Falls through. */
480
5.20M
        case 2: value += name[1];
481
        /* Falls through. */
482
15.8M
        default: break;
483
15.8M
    }
484
15.8M
    return(value);
485
15.8M
}
486
487
/*
488
 * xmlDictComputeFastQKey:
489
 *
490
 * Calculate a hash key for two strings using a fast hash function
491
 * that works well for low hash table fill.
492
 *
493
 * Neither of the two strings must be NULL.
494
 */
495
static unsigned long
496
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
497
                       const xmlChar *name, int len, int seed)
498
0
{
499
0
    unsigned long value = (unsigned long) seed;
500
501
0
    if (plen == 0)
502
0
  value += 30 * (unsigned long) ':';
503
0
    else
504
0
  value += 30 * (*prefix);
505
506
0
    if (len > 10) {
507
0
        int offset = len - (plen + 1 + 1);
508
0
  if (offset < 0)
509
0
      offset = len - (10 + 1);
510
0
  value += name[offset];
511
0
        len = 10;
512
0
  if (plen > 10)
513
0
      plen = 10;
514
0
    }
515
0
    switch (plen) {
516
0
        case 10: value += prefix[9];
517
        /* Falls through. */
518
0
        case 9: value += prefix[8];
519
        /* Falls through. */
520
0
        case 8: value += prefix[7];
521
        /* Falls through. */
522
0
        case 7: value += prefix[6];
523
        /* Falls through. */
524
0
        case 6: value += prefix[5];
525
        /* Falls through. */
526
0
        case 5: value += prefix[4];
527
        /* Falls through. */
528
0
        case 4: value += prefix[3];
529
        /* Falls through. */
530
0
        case 3: value += prefix[2];
531
        /* Falls through. */
532
0
        case 2: value += prefix[1];
533
        /* Falls through. */
534
0
        case 1: value += prefix[0];
535
        /* Falls through. */
536
0
        default: break;
537
0
    }
538
0
    len -= plen;
539
0
    if (len > 0) {
540
0
        value += (unsigned long) ':';
541
0
  len--;
542
0
    }
543
0
    switch (len) {
544
0
        case 10: value += name[9];
545
        /* Falls through. */
546
0
        case 9: value += name[8];
547
        /* Falls through. */
548
0
        case 8: value += name[7];
549
        /* Falls through. */
550
0
        case 7: value += name[6];
551
        /* Falls through. */
552
0
        case 6: value += name[5];
553
        /* Falls through. */
554
0
        case 5: value += name[4];
555
        /* Falls through. */
556
0
        case 4: value += name[3];
557
        /* Falls through. */
558
0
        case 3: value += name[2];
559
        /* Falls through. */
560
0
        case 2: value += name[1];
561
        /* Falls through. */
562
0
        case 1: value += name[0];
563
        /* Falls through. */
564
0
        default: break;
565
0
    }
566
0
    return(value);
567
0
}
568
569
/**
570
 * xmlDictCreate:
571
 *
572
 * Create a new dictionary
573
 *
574
 * Returns the newly created dictionary, or NULL if an error occurred.
575
 */
576
xmlDictPtr
577
319k
xmlDictCreate(void) {
578
319k
    xmlDictPtr dict;
579
580
319k
    if (!xmlDictInitialized)
581
0
        if (!__xmlInitializeDict())
582
0
            return(NULL);
583
584
#ifdef DICT_DEBUG_PATTERNS
585
    fprintf(stderr, "C");
586
#endif
587
588
319k
    dict = xmlMalloc(sizeof(xmlDict));
589
319k
    if (dict) {
590
319k
        dict->ref_counter = 1;
591
319k
        dict->limit = 0;
592
593
319k
        dict->size = MIN_DICT_SIZE;
594
319k
  dict->nbElems = 0;
595
319k
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
596
319k
  dict->strings = NULL;
597
319k
  dict->subdict = NULL;
598
319k
        if (dict->dict) {
599
319k
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
600
#ifdef DICT_RANDOMIZATION
601
            dict->seed = __xmlRandom();
602
#else
603
319k
            dict->seed = 0;
604
319k
#endif
605
319k
      return(dict);
606
319k
        }
607
0
        xmlFree(dict);
608
0
    }
609
0
    return(NULL);
610
319k
}
611
612
/**
613
 * xmlDictCreateSub:
614
 * @sub: an existing dictionary
615
 *
616
 * Create a new dictionary, inheriting strings from the read-only
617
 * dictionary @sub. On lookup, strings are first searched in the
618
 * new dictionary, then in @sub, and if not found are created in the
619
 * new dictionary.
620
 *
621
 * Returns the newly created dictionary, or NULL if an error occurred.
622
 */
623
xmlDictPtr
624
0
xmlDictCreateSub(xmlDictPtr sub) {
625
0
    xmlDictPtr dict = xmlDictCreate();
626
627
0
    if ((dict != NULL) && (sub != NULL)) {
628
#ifdef DICT_DEBUG_PATTERNS
629
        fprintf(stderr, "R");
630
#endif
631
0
        dict->seed = sub->seed;
632
0
        dict->subdict = sub;
633
0
  xmlDictReference(dict->subdict);
634
0
    }
635
0
    return(dict);
636
0
}
637
638
/**
639
 * xmlDictReference:
640
 * @dict: the dictionary
641
 *
642
 * Increment the reference counter of a dictionary
643
 *
644
 * Returns 0 in case of success and -1 in case of error
645
 */
646
int
647
13.8k
xmlDictReference(xmlDictPtr dict) {
648
13.8k
    if (!xmlDictInitialized)
649
0
        if (!__xmlInitializeDict())
650
0
            return(-1);
651
652
13.8k
    if (dict == NULL) return -1;
653
4.40k
    xmlMutexLock(xmlDictMutex);
654
4.40k
    dict->ref_counter++;
655
4.40k
    xmlMutexUnlock(xmlDictMutex);
656
4.40k
    return(0);
657
13.8k
}
658
659
/**
660
 * xmlDictGrow:
661
 * @dict: the dictionary
662
 * @size: the new size of the dictionary
663
 *
664
 * resize the dictionary
665
 *
666
 * Returns 0 in case of success, -1 in case of failure
667
 */
668
static int
669
460
xmlDictGrow(xmlDictPtr dict, size_t size) {
670
460
    unsigned long key, okey;
671
460
    size_t oldsize, i;
672
460
    xmlDictEntryPtr iter, next;
673
460
    struct _xmlDictEntry *olddict;
674
#ifdef DEBUG_GROW
675
    unsigned long nbElem = 0;
676
#endif
677
460
    int ret = 0;
678
460
    int keep_keys = 1;
679
680
460
    if (dict == NULL)
681
0
  return(-1);
682
460
    if (size < 8)
683
0
        return(-1);
684
460
    if (size > 8 * 2048)
685
0
  return(-1);
686
687
#ifdef DICT_DEBUG_PATTERNS
688
    fprintf(stderr, "*");
689
#endif
690
691
460
    oldsize = dict->size;
692
460
    olddict = dict->dict;
693
460
    if (olddict == NULL)
694
0
        return(-1);
695
460
    if (oldsize == MIN_DICT_SIZE)
696
418
        keep_keys = 0;
697
698
460
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
699
460
    if (dict->dict == NULL) {
700
0
  dict->dict = olddict;
701
0
  return(-1);
702
0
    }
703
460
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
704
460
    dict->size = size;
705
706
    /*  If the two loops are merged, there would be situations where
707
  a new entry needs to allocated and data copied into it from
708
  the main dict. It is nicer to run through the array twice, first
709
  copying all the elements in the main array (less probability of
710
  allocate) and then the rest, so we only free in the second loop.
711
    */
712
86.2k
    for (i = 0; i < oldsize; i++) {
713
85.7k
  if (olddict[i].valid == 0)
714
69.9k
      continue;
715
716
15.8k
  if (keep_keys)
717
6.59k
      okey = olddict[i].okey;
718
9.21k
  else
719
9.21k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
720
15.8k
  key = okey % dict->size;
721
722
15.8k
  if (dict->dict[key].valid == 0) {
723
15.5k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
724
15.5k
      dict->dict[key].next = NULL;
725
15.5k
      dict->dict[key].okey = okey;
726
15.5k
  } else {
727
294
      xmlDictEntryPtr entry;
728
729
294
      entry = xmlMalloc(sizeof(xmlDictEntry));
730
294
      if (entry != NULL) {
731
294
    entry->name = olddict[i].name;
732
294
    entry->len = olddict[i].len;
733
294
    entry->okey = okey;
734
294
    entry->next = dict->dict[key].next;
735
294
    entry->valid = 1;
736
294
    dict->dict[key].next = entry;
737
294
      } else {
738
          /*
739
     * we don't have much ways to alert from here
740
     * result is losing an entry and unicity guarantee
741
     */
742
0
          ret = -1;
743
0
      }
744
294
  }
745
#ifdef DEBUG_GROW
746
  nbElem++;
747
#endif
748
15.8k
    }
749
750
86.2k
    for (i = 0; i < oldsize; i++) {
751
85.7k
  iter = olddict[i].next;
752
93.2k
  while (iter) {
753
7.44k
      next = iter->next;
754
755
      /*
756
       * put back the entry in the new dict
757
       */
758
759
7.44k
      if (keep_keys)
760
1.37k
    okey = iter->okey;
761
6.07k
      else
762
6.07k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
763
7.44k
      key = okey % dict->size;
764
7.44k
      if (dict->dict[key].valid == 0) {
765
6.45k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
766
6.45k
    dict->dict[key].next = NULL;
767
6.45k
    dict->dict[key].valid = 1;
768
6.45k
    dict->dict[key].okey = okey;
769
6.45k
    xmlFree(iter);
770
6.45k
      } else {
771
990
    iter->next = dict->dict[key].next;
772
990
    iter->okey = okey;
773
990
    dict->dict[key].next = iter;
774
990
      }
775
776
#ifdef DEBUG_GROW
777
      nbElem++;
778
#endif
779
780
7.44k
      iter = next;
781
7.44k
  }
782
85.7k
    }
783
784
460
    xmlFree(olddict);
785
786
#ifdef DEBUG_GROW
787
    xmlGenericError(xmlGenericErrorContext,
788
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
789
#endif
790
791
460
    return(ret);
792
460
}
793
794
/**
795
 * xmlDictFree:
796
 * @dict: the dictionary
797
 *
798
 * Free the hash @dict and its contents. The userdata is
799
 * deallocated with @f if provided.
800
 */
801
void
802
324k
xmlDictFree(xmlDictPtr dict) {
803
324k
    size_t i;
804
324k
    xmlDictEntryPtr iter;
805
324k
    xmlDictEntryPtr next;
806
324k
    int inside_dict = 0;
807
324k
    xmlDictStringsPtr pool, nextp;
808
809
324k
    if (dict == NULL)
810
0
  return;
811
812
324k
    if (!xmlDictInitialized)
813
0
        if (!__xmlInitializeDict())
814
0
            return;
815
816
    /* decrement the counter, it may be shared by a parser and docs */
817
324k
    xmlMutexLock(xmlDictMutex);
818
324k
    dict->ref_counter--;
819
324k
    if (dict->ref_counter > 0) {
820
4.40k
        xmlMutexUnlock(xmlDictMutex);
821
4.40k
        return;
822
4.40k
    }
823
824
319k
    xmlMutexUnlock(xmlDictMutex);
825
826
319k
    if (dict->subdict != NULL) {
827
0
        xmlDictFree(dict->subdict);
828
0
    }
829
830
319k
    if (dict->dict) {
831
2.61M
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
832
2.29M
      iter = &(dict->dict[i]);
833
2.29M
      if (iter->valid == 0)
834
2.16M
    continue;
835
122k
      inside_dict = 1;
836
280k
      while (iter) {
837
158k
    next = iter->next;
838
158k
    if (!inside_dict)
839
36.0k
        xmlFree(iter);
840
158k
    dict->nbElems--;
841
158k
    inside_dict = 0;
842
158k
    iter = next;
843
158k
      }
844
122k
  }
845
319k
  xmlFree(dict->dict);
846
319k
    }
847
319k
    pool = dict->strings;
848
340k
    while (pool != NULL) {
849
20.2k
        nextp = pool->next;
850
20.2k
  xmlFree(pool);
851
20.2k
  pool = nextp;
852
20.2k
    }
853
319k
    xmlFree(dict);
854
319k
}
855
856
/**
857
 * xmlDictLookup:
858
 * @dict: the dictionary
859
 * @name: the name of the userdata
860
 * @len: the length of the name, if -1 it is recomputed
861
 *
862
 * Add the @name to the dictionary @dict if not present.
863
 *
864
 * Returns the internal copy of the name or NULL in case of internal error
865
 */
866
const xmlChar *
867
20.5M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
868
20.5M
    unsigned long key, okey, nbi = 0;
869
20.5M
    xmlDictEntryPtr entry;
870
20.5M
    xmlDictEntryPtr insert;
871
20.5M
    const xmlChar *ret;
872
20.5M
    unsigned int l;
873
874
20.5M
    if ((dict == NULL) || (name == NULL))
875
0
  return(NULL);
876
877
20.5M
    if (len < 0)
878
69.9k
        l = strlen((const char *) name);
879
20.5M
    else
880
20.5M
        l = len;
881
882
20.5M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
883
20.5M
        (l > INT_MAX / 2))
884
0
        return(NULL);
885
886
    /*
887
     * Check for duplicate and insertion location.
888
     */
889
20.5M
    okey = xmlDictComputeKey(dict, name, l);
890
20.5M
    key = okey % dict->size;
891
20.5M
    if (dict->dict[key].valid == 0) {
892
116k
  insert = NULL;
893
20.4M
    } else {
894
26.9M
  for (insert = &(dict->dict[key]); insert->next != NULL;
895
20.4M
       insert = insert->next) {
896
11.4M
#ifdef __GNUC__
897
11.4M
      if ((insert->okey == okey) && (insert->len == l)) {
898
4.91M
    if (!memcmp(insert->name, name, l))
899
4.91M
        return(insert->name);
900
4.91M
      }
901
#else
902
      if ((insert->okey == okey) && (insert->len == l) &&
903
          (!xmlStrncmp(insert->name, name, l)))
904
    return(insert->name);
905
#endif
906
6.51M
      nbi++;
907
6.51M
  }
908
15.5M
#ifdef __GNUC__
909
15.5M
  if ((insert->okey == okey) && (insert->len == l)) {
910
15.4M
      if (!memcmp(insert->name, name, l))
911
15.4M
    return(insert->name);
912
15.4M
  }
913
#else
914
  if ((insert->okey == okey) && (insert->len == l) &&
915
      (!xmlStrncmp(insert->name, name, l)))
916
      return(insert->name);
917
#endif
918
15.5M
    }
919
920
158k
    if (dict->subdict) {
921
0
        unsigned long skey;
922
923
        /* we cannot always reuse the same okey for the subdict */
924
0
        if (((dict->size == MIN_DICT_SIZE) &&
925
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
926
0
            ((dict->size != MIN_DICT_SIZE) &&
927
0
       (dict->subdict->size == MIN_DICT_SIZE)))
928
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
929
0
  else
930
0
      skey = okey;
931
932
0
  key = skey % dict->subdict->size;
933
0
  if (dict->subdict->dict[key].valid != 0) {
934
0
      xmlDictEntryPtr tmp;
935
936
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
937
0
     tmp = tmp->next) {
938
0
#ifdef __GNUC__
939
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
940
0
        if (!memcmp(tmp->name, name, l))
941
0
      return(tmp->name);
942
0
    }
943
#else
944
    if ((tmp->okey == skey) && (tmp->len == l) &&
945
        (!xmlStrncmp(tmp->name, name, l)))
946
        return(tmp->name);
947
#endif
948
0
    nbi++;
949
0
      }
950
0
#ifdef __GNUC__
951
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
952
0
    if (!memcmp(tmp->name, name, l))
953
0
        return(tmp->name);
954
0
      }
955
#else
956
      if ((tmp->okey == skey) && (tmp->len == l) &&
957
    (!xmlStrncmp(tmp->name, name, l)))
958
    return(tmp->name);
959
#endif
960
0
  }
961
0
  key = okey % dict->size;
962
0
    }
963
964
158k
    ret = xmlDictAddString(dict, name, l);
965
158k
    if (ret == NULL)
966
0
        return(NULL);
967
158k
    if (insert == NULL) {
968
116k
  entry = &(dict->dict[key]);
969
116k
    } else {
970
42.1k
  entry = xmlMalloc(sizeof(xmlDictEntry));
971
42.1k
  if (entry == NULL)
972
0
       return(NULL);
973
42.1k
    }
974
158k
    entry->name = ret;
975
158k
    entry->len = l;
976
158k
    entry->next = NULL;
977
158k
    entry->valid = 1;
978
158k
    entry->okey = okey;
979
980
981
158k
    if (insert != NULL)
982
42.1k
  insert->next = entry;
983
984
158k
    dict->nbElems++;
985
986
158k
    if ((nbi > MAX_HASH_LEN) &&
987
158k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
988
460
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
989
0
      return(NULL);
990
460
    }
991
    /* Note that entry may have been freed at this point by xmlDictGrow */
992
993
158k
    return(ret);
994
158k
}
995
996
/**
997
 * xmlDictExists:
998
 * @dict: the dictionary
999
 * @name: the name of the userdata
1000
 * @len: the length of the name, if -1 it is recomputed
1001
 *
1002
 * Check if the @name exists in the dictionary @dict.
1003
 *
1004
 * Returns the internal copy of the name or NULL if not found.
1005
 */
1006
const xmlChar *
1007
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
1008
0
    unsigned long key, okey, nbi = 0;
1009
0
    xmlDictEntryPtr insert;
1010
0
    unsigned int l;
1011
1012
0
    if ((dict == NULL) || (name == NULL))
1013
0
  return(NULL);
1014
1015
0
    if (len < 0)
1016
0
        l = strlen((const char *) name);
1017
0
    else
1018
0
        l = len;
1019
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
1020
0
        (l > INT_MAX / 2))
1021
0
        return(NULL);
1022
1023
    /*
1024
     * Check for duplicate and insertion location.
1025
     */
1026
0
    okey = xmlDictComputeKey(dict, name, l);
1027
0
    key = okey % dict->size;
1028
0
    if (dict->dict[key].valid == 0) {
1029
0
  insert = NULL;
1030
0
    } else {
1031
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1032
0
       insert = insert->next) {
1033
0
#ifdef __GNUC__
1034
0
      if ((insert->okey == okey) && (insert->len == l)) {
1035
0
    if (!memcmp(insert->name, name, l))
1036
0
        return(insert->name);
1037
0
      }
1038
#else
1039
      if ((insert->okey == okey) && (insert->len == l) &&
1040
          (!xmlStrncmp(insert->name, name, l)))
1041
    return(insert->name);
1042
#endif
1043
0
      nbi++;
1044
0
  }
1045
0
#ifdef __GNUC__
1046
0
  if ((insert->okey == okey) && (insert->len == l)) {
1047
0
      if (!memcmp(insert->name, name, l))
1048
0
    return(insert->name);
1049
0
  }
1050
#else
1051
  if ((insert->okey == okey) && (insert->len == l) &&
1052
      (!xmlStrncmp(insert->name, name, l)))
1053
      return(insert->name);
1054
#endif
1055
0
    }
1056
1057
0
    if (dict->subdict) {
1058
0
        unsigned long skey;
1059
1060
        /* we cannot always reuse the same okey for the subdict */
1061
0
        if (((dict->size == MIN_DICT_SIZE) &&
1062
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1063
0
            ((dict->size != MIN_DICT_SIZE) &&
1064
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1065
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1066
0
  else
1067
0
      skey = okey;
1068
1069
0
  key = skey % dict->subdict->size;
1070
0
  if (dict->subdict->dict[key].valid != 0) {
1071
0
      xmlDictEntryPtr tmp;
1072
1073
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1074
0
     tmp = tmp->next) {
1075
0
#ifdef __GNUC__
1076
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1077
0
        if (!memcmp(tmp->name, name, l))
1078
0
      return(tmp->name);
1079
0
    }
1080
#else
1081
    if ((tmp->okey == skey) && (tmp->len == l) &&
1082
        (!xmlStrncmp(tmp->name, name, l)))
1083
        return(tmp->name);
1084
#endif
1085
0
    nbi++;
1086
0
      }
1087
0
#ifdef __GNUC__
1088
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1089
0
    if (!memcmp(tmp->name, name, l))
1090
0
        return(tmp->name);
1091
0
      }
1092
#else
1093
      if ((tmp->okey == skey) && (tmp->len == l) &&
1094
    (!xmlStrncmp(tmp->name, name, l)))
1095
    return(tmp->name);
1096
#endif
1097
0
  }
1098
0
    }
1099
1100
    /* not found */
1101
0
    return(NULL);
1102
0
}
1103
1104
/**
1105
 * xmlDictQLookup:
1106
 * @dict: the dictionary
1107
 * @prefix: the prefix
1108
 * @name: the name
1109
 *
1110
 * Add the QName @prefix:@name to the hash @dict if not present.
1111
 *
1112
 * Returns the internal copy of the QName or NULL in case of internal error
1113
 */
1114
const xmlChar *
1115
0
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1116
0
    unsigned long okey, key, nbi = 0;
1117
0
    xmlDictEntryPtr entry;
1118
0
    xmlDictEntryPtr insert;
1119
0
    const xmlChar *ret;
1120
0
    unsigned int len, plen, l;
1121
1122
0
    if ((dict == NULL) || (name == NULL))
1123
0
  return(NULL);
1124
0
    if (prefix == NULL)
1125
0
        return(xmlDictLookup(dict, name, -1));
1126
1127
0
    l = len = strlen((const char *) name);
1128
0
    plen = strlen((const char *) prefix);
1129
0
    len += 1 + plen;
1130
1131
    /*
1132
     * Check for duplicate and insertion location.
1133
     */
1134
0
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1135
0
    key = okey % dict->size;
1136
0
    if (dict->dict[key].valid == 0) {
1137
0
  insert = NULL;
1138
0
    } else {
1139
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1140
0
       insert = insert->next) {
1141
0
      if ((insert->okey == okey) && (insert->len == len) &&
1142
0
          (xmlStrQEqual(prefix, name, insert->name)))
1143
0
    return(insert->name);
1144
0
      nbi++;
1145
0
  }
1146
0
  if ((insert->okey == okey) && (insert->len == len) &&
1147
0
      (xmlStrQEqual(prefix, name, insert->name)))
1148
0
      return(insert->name);
1149
0
    }
1150
1151
0
    if (dict->subdict) {
1152
0
        unsigned long skey;
1153
1154
        /* we cannot always reuse the same okey for the subdict */
1155
0
        if (((dict->size == MIN_DICT_SIZE) &&
1156
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1157
0
            ((dict->size != MIN_DICT_SIZE) &&
1158
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1159
0
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1160
0
  else
1161
0
      skey = okey;
1162
1163
0
  key = skey % dict->subdict->size;
1164
0
  if (dict->subdict->dict[key].valid != 0) {
1165
0
      xmlDictEntryPtr tmp;
1166
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1167
0
     tmp = tmp->next) {
1168
0
    if ((tmp->okey == skey) && (tmp->len == len) &&
1169
0
        (xmlStrQEqual(prefix, name, tmp->name)))
1170
0
        return(tmp->name);
1171
0
    nbi++;
1172
0
      }
1173
0
      if ((tmp->okey == skey) && (tmp->len == len) &&
1174
0
    (xmlStrQEqual(prefix, name, tmp->name)))
1175
0
    return(tmp->name);
1176
0
  }
1177
0
  key = okey % dict->size;
1178
0
    }
1179
1180
0
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1181
0
    if (ret == NULL)
1182
0
        return(NULL);
1183
0
    if (insert == NULL) {
1184
0
  entry = &(dict->dict[key]);
1185
0
    } else {
1186
0
  entry = xmlMalloc(sizeof(xmlDictEntry));
1187
0
  if (entry == NULL)
1188
0
       return(NULL);
1189
0
    }
1190
0
    entry->name = ret;
1191
0
    entry->len = len;
1192
0
    entry->next = NULL;
1193
0
    entry->valid = 1;
1194
0
    entry->okey = okey;
1195
1196
0
    if (insert != NULL)
1197
0
  insert->next = entry;
1198
1199
0
    dict->nbElems++;
1200
1201
0
    if ((nbi > MAX_HASH_LEN) &&
1202
0
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1203
0
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1204
    /* Note that entry may have been freed at this point by xmlDictGrow */
1205
1206
0
    return(ret);
1207
0
}
1208
1209
/**
1210
 * xmlDictOwns:
1211
 * @dict: the dictionary
1212
 * @str: the string
1213
 *
1214
 * check if a string is owned by the dictionary
1215
 *
1216
 * Returns 1 if true, 0 if false and -1 in case of error
1217
 * -1 in case of error
1218
 */
1219
int
1220
214k
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1221
214k
    xmlDictStringsPtr pool;
1222
1223
214k
    if ((dict == NULL) || (str == NULL))
1224
0
  return(-1);
1225
214k
    pool = dict->strings;
1226
421k
    while (pool != NULL) {
1227
250k
        if ((str >= &pool->array[0]) && (str <= pool->free))
1228
44.0k
      return(1);
1229
206k
  pool = pool->next;
1230
206k
    }
1231
170k
    if (dict->subdict)
1232
0
        return(xmlDictOwns(dict->subdict, str));
1233
170k
    return(0);
1234
170k
}
1235
1236
/**
1237
 * xmlDictSize:
1238
 * @dict: the dictionary
1239
 *
1240
 * Query the number of elements installed in the hash @dict.
1241
 *
1242
 * Returns the number of elements in the dictionary or
1243
 * -1 in case of error
1244
 */
1245
int
1246
0
xmlDictSize(xmlDictPtr dict) {
1247
0
    if (dict == NULL)
1248
0
  return(-1);
1249
0
    if (dict->subdict)
1250
0
        return(dict->nbElems + dict->subdict->nbElems);
1251
0
    return(dict->nbElems);
1252
0
}
1253
1254
/**
1255
 * xmlDictSetLimit:
1256
 * @dict: the dictionary
1257
 * @limit: the limit in bytes
1258
 *
1259
 * Set a size limit for the dictionary
1260
 * Added in 2.9.0
1261
 *
1262
 * Returns the previous limit of the dictionary or 0
1263
 */
1264
size_t
1265
338k
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1266
338k
    size_t ret;
1267
1268
338k
    if (dict == NULL)
1269
0
  return(0);
1270
338k
    ret = dict->limit;
1271
338k
    dict->limit = limit;
1272
338k
    return(ret);
1273
338k
}
1274
1275
/**
1276
 * xmlDictGetUsage:
1277
 * @dict: the dictionary
1278
 *
1279
 * Get how much memory is used by a dictionary for strings
1280
 * Added in 2.9.0
1281
 *
1282
 * Returns the amount of strings allocated
1283
 */
1284
size_t
1285
0
xmlDictGetUsage(xmlDictPtr dict) {
1286
0
    xmlDictStringsPtr pool;
1287
0
    size_t limit = 0;
1288
1289
0
    if (dict == NULL)
1290
0
  return(0);
1291
0
    pool = dict->strings;
1292
0
    while (pool != NULL) {
1293
0
        limit += pool->size;
1294
0
  pool = pool->next;
1295
0
    }
1296
0
    return(limit);
1297
0
}
1298