Coverage Report

Created: 2023-03-26 06:14

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