Coverage Report

Created: 2022-11-15 06:34

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