Coverage Report

Created: 2024-05-18 02:13

/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
35.3M
#define MAX_HASH_LEN 3
63
1.13G
#define MIN_DICT_SIZE 128
64
21.0k
#define MAX_DICT_HASH 8 * 2048
65
#define WITH_BIG_KEY
66
67
#ifdef WITH_BIG_KEY
68
#define xmlDictComputeKey(dict, name, len)                              \
69
1.11G
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
70
1.11G
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
71
1.11G
     xmlDictComputeBigKey(name, len, (dict)->seed))
72
73
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
74
700k
    (((prefix) == NULL) ?                                               \
75
700k
      (xmlDictComputeKey(dict, name, len)) :                             \
76
700k
      (((dict)->size == MIN_DICT_SIZE) ?                                \
77
700k
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
78
700k
       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
9.59k
int xmlInitializeDict(void) {
160
9.59k
    return(0);
161
9.59k
}
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
9.59k
int __xmlInitializeDict(void) {
176
9.59k
    if (xmlDictInitialized)
177
0
        return(1);
178
179
9.59k
    if ((xmlDictMutex = xmlNewMutex()) == NULL)
180
0
        return(0);
181
9.59k
    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
9.59k
    xmlDictInitialized = 1;
192
9.59k
    xmlMutexUnlock(xmlDictMutex);
193
9.59k
    return(1);
194
9.59k
}
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
35.1M
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
247
35.1M
    xmlDictStringsPtr pool;
248
35.1M
    const xmlChar *ret;
249
35.1M
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
250
35.1M
    size_t limit = 0;
251
252
#ifdef DICT_DEBUG_PATTERNS
253
    fprintf(stderr, "-");
254
#endif
255
35.1M
    pool = dict->strings;
256
35.1M
    while (pool != NULL) {
257
28.2M
  if ((size_t)(pool->end - pool->free) > namelen)
258
28.2M
      goto found_pool;
259
32.1k
  if (pool->size > size) size = pool->size;
260
32.1k
        limit += pool->size;
261
32.1k
  pool = pool->next;
262
32.1k
    }
263
    /*
264
     * Not found, need to allocate
265
     */
266
6.88M
    if (pool == NULL) {
267
6.88M
        if ((dict->limit > 0) && (limit > dict->limit)) {
268
0
            return(NULL);
269
0
        }
270
271
6.88M
        if (size == 0) size = 1000;
272
30.9k
  else size *= 4; /* exponential growth */
273
6.88M
        if (size < 4 * namelen)
274
12.9k
      size = 4 * namelen; /* just in case ! */
275
6.88M
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
276
6.88M
  if (pool == NULL)
277
0
      return(NULL);
278
6.88M
  pool->size = size;
279
6.88M
  pool->nbStrings = 0;
280
6.88M
  pool->free = &pool->array[0];
281
6.88M
  pool->end = &pool->array[size];
282
6.88M
  pool->next = dict->strings;
283
6.88M
  dict->strings = pool;
284
#ifdef DICT_DEBUG_PATTERNS
285
        fprintf(stderr, "+");
286
#endif
287
6.88M
    }
288
35.1M
found_pool:
289
35.1M
    ret = pool->free;
290
35.1M
    memcpy(pool->free, name, namelen);
291
35.1M
    pool->free += namelen;
292
35.1M
    *(pool->free++) = 0;
293
35.1M
    pool->nbStrings++;
294
35.1M
    return(ret);
295
6.88M
}
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
141k
{
313
141k
    xmlDictStringsPtr pool;
314
141k
    const xmlChar *ret;
315
141k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
316
141k
    size_t limit = 0;
317
318
141k
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
319
320
#ifdef DICT_DEBUG_PATTERNS
321
    fprintf(stderr, "=");
322
#endif
323
141k
    pool = dict->strings;
324
142k
    while (pool != NULL) {
325
142k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
326
141k
      goto found_pool;
327
1.05k
  if (pool->size > size) size = pool->size;
328
1.05k
        limit += pool->size;
329
1.05k
  pool = pool->next;
330
1.05k
    }
331
    /*
332
     * Not found, need to allocate
333
     */
334
554
    if (pool == NULL) {
335
554
        if ((dict->limit > 0) && (limit > dict->limit)) {
336
0
            return(NULL);
337
0
        }
338
339
554
        if (size == 0) size = 1000;
340
554
  else size *= 4; /* exponential growth */
341
554
        if (size < 4 * (namelen + plen + 1))
342
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
343
554
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
344
554
  if (pool == NULL)
345
0
      return(NULL);
346
554
  pool->size = size;
347
554
  pool->nbStrings = 0;
348
554
  pool->free = &pool->array[0];
349
554
  pool->end = &pool->array[size];
350
554
  pool->next = dict->strings;
351
554
  dict->strings = pool;
352
#ifdef DICT_DEBUG_PATTERNS
353
        fprintf(stderr, "+");
354
#endif
355
554
    }
356
141k
found_pool:
357
141k
    ret = pool->free;
358
141k
    memcpy(pool->free, prefix, plen);
359
141k
    pool->free += plen;
360
141k
    *(pool->free++) = ':';
361
141k
    memcpy(pool->free, name, namelen);
362
141k
    pool->free += namelen;
363
141k
    *(pool->free++) = 0;
364
141k
    pool->nbStrings++;
365
141k
    return(ret);
366
554
}
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
193M
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
384
193M
    uint32_t hash;
385
193M
    int i;
386
387
193M
    if (namelen <= 0 || data == NULL) return(0);
388
389
193M
    hash = seed;
390
391
1.34G
    for (i = 0;i < namelen; i++) {
392
1.15G
        hash += data[i];
393
1.15G
  hash += (hash << 10);
394
1.15G
  hash ^= (hash >> 6);
395
1.15G
    }
396
193M
    hash += (hash << 3);
397
193M
    hash ^= (hash >> 11);
398
193M
    hash += (hash << 15);
399
400
193M
    return hash;
401
193M
}
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
288k
{
421
288k
    uint32_t hash;
422
288k
    int i;
423
424
288k
    hash = seed;
425
426
1.01M
    for (i = 0;i < plen; i++) {
427
731k
        hash += prefix[i];
428
731k
  hash += (hash << 10);
429
731k
  hash ^= (hash >> 6);
430
731k
    }
431
288k
    hash += ':';
432
288k
    hash += (hash << 10);
433
288k
    hash ^= (hash >> 6);
434
435
2.35M
    for (i = 0;i < len; i++) {
436
2.06M
        hash += name[i];
437
2.06M
  hash += (hash << 10);
438
2.06M
  hash ^= (hash >> 6);
439
2.06M
    }
440
288k
    hash += (hash << 3);
441
288k
    hash ^= (hash >> 11);
442
288k
    hash += (hash << 15);
443
444
288k
    return hash;
445
288k
}
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
916M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
456
916M
    unsigned long value = seed;
457
458
916M
    if (name == NULL) return(0);
459
916M
    value += *name;
460
916M
    value <<= 5;
461
916M
    if (namelen > 10) {
462
66.8M
        value += name[namelen - 1];
463
66.8M
        namelen = 10;
464
66.8M
    }
465
916M
    switch (namelen) {
466
93.0M
        case 10: value += name[9];
467
        /* Falls through. */
468
114M
        case 9: value += name[8];
469
        /* Falls through. */
470
179M
        case 8: value += name[7];
471
        /* Falls through. */
472
276M
        case 7: value += name[6];
473
        /* Falls through. */
474
307M
        case 6: value += name[5];
475
        /* Falls through. */
476
363M
        case 5: value += name[4];
477
        /* Falls through. */
478
544M
        case 4: value += name[3];
479
        /* Falls through. */
480
685M
        case 3: value += name[2];
481
        /* Falls through. */
482
731M
        case 2: value += name[1];
483
        /* Falls through. */
484
916M
        default: break;
485
916M
    }
486
916M
    return(value);
487
916M
}
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
411k
{
501
411k
    unsigned long value = seed;
502
503
411k
    if (plen == 0)
504
0
  value += 30 * ':';
505
411k
    else
506
411k
  value += 30 * (*prefix);
507
508
411k
    if (len > 10) {
509
15.0k
        int offset = len - (plen + 1 + 1);
510
15.0k
  if (offset < 0)
511
996
      offset = len - (10 + 1);
512
15.0k
  value += name[offset];
513
15.0k
        len = 10;
514
15.0k
  if (plen > 10)
515
1.28k
      plen = 10;
516
15.0k
    }
517
411k
    switch (plen) {
518
1.64k
        case 10: value += prefix[9];
519
        /* Falls through. */
520
2.44k
        case 9: value += prefix[8];
521
        /* Falls through. */
522
16.8k
        case 8: value += prefix[7];
523
        /* Falls through. */
524
18.6k
        case 7: value += prefix[6];
525
        /* Falls through. */
526
21.4k
        case 6: value += prefix[5];
527
        /* Falls through. */
528
47.4k
        case 5: value += prefix[4];
529
        /* Falls through. */
530
61.6k
        case 4: value += prefix[3];
531
        /* Falls through. */
532
218k
        case 3: value += prefix[2];
533
        /* Falls through. */
534
352k
        case 2: value += prefix[1];
535
        /* Falls through. */
536
406k
        case 1: value += prefix[0];
537
        /* Falls through. */
538
411k
        default: break;
539
411k
    }
540
411k
    len -= plen;
541
411k
    if (len > 0) {
542
299k
        value += ':';
543
299k
  len--;
544
299k
    }
545
411k
    switch (len) {
546
0
        case 10: value += name[9];
547
        /* Falls through. */
548
0
        case 9: value += name[8];
549
        /* Falls through. */
550
3.34k
        case 8: value += name[7];
551
        /* Falls through. */
552
7.45k
        case 7: value += name[6];
553
        /* Falls through. */
554
21.8k
        case 6: value += name[5];
555
        /* Falls through. */
556
116k
        case 5: value += name[4];
557
        /* Falls through. */
558
188k
        case 4: value += name[3];
559
        /* Falls through. */
560
198k
        case 3: value += name[2];
561
        /* Falls through. */
562
223k
        case 2: value += name[1];
563
        /* Falls through. */
564
255k
        case 1: value += name[0];
565
        /* Falls through. */
566
411k
        default: break;
567
411k
    }
568
411k
    return(value);
569
411k
}
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
7.55M
xmlDictCreate(void) {
580
7.55M
    xmlDictPtr dict;
581
582
7.55M
    if (!xmlDictInitialized)
583
0
        if (!__xmlInitializeDict())
584
0
            return(NULL);
585
586
#ifdef DICT_DEBUG_PATTERNS
587
    fprintf(stderr, "C");
588
#endif
589
590
7.55M
    dict = xmlMalloc(sizeof(xmlDict));
591
7.55M
    if (dict) {
592
7.55M
        dict->ref_counter = 1;
593
7.55M
        dict->limit = 0;
594
595
7.55M
        dict->size = MIN_DICT_SIZE;
596
7.55M
  dict->nbElems = 0;
597
7.55M
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
598
7.55M
  dict->strings = NULL;
599
7.55M
  dict->subdict = NULL;
600
7.55M
        if (dict->dict) {
601
7.55M
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
602
#ifdef DICT_RANDOMIZATION
603
            dict->seed = __xmlRandom();
604
#else
605
7.55M
            dict->seed = 0;
606
7.55M
#endif
607
7.55M
      return(dict);
608
7.55M
        }
609
0
        xmlFree(dict);
610
0
    }
611
0
    return(NULL);
612
7.55M
}
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
0
xmlDictCreateSub(xmlDictPtr sub) {
627
0
    xmlDictPtr dict = xmlDictCreate();
628
629
0
    if ((dict != NULL) && (sub != NULL)) {
630
#ifdef DICT_DEBUG_PATTERNS
631
        fprintf(stderr, "R");
632
#endif
633
0
        dict->seed = sub->seed;
634
0
        dict->subdict = sub;
635
0
  xmlDictReference(dict->subdict);
636
0
    }
637
0
    return(dict);
638
0
}
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
4.76M
xmlDictReference(xmlDictPtr dict) {
650
4.76M
    if (!xmlDictInitialized)
651
0
        if (!__xmlInitializeDict())
652
0
            return(-1);
653
654
4.76M
    if (dict == NULL) return -1;
655
4.52M
    xmlMutexLock(xmlDictMutex);
656
4.52M
    dict->ref_counter++;
657
4.52M
    xmlMutexUnlock(xmlDictMutex);
658
4.52M
    return(0);
659
4.76M
}
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
21.0k
xmlDictGrow(xmlDictPtr dict, size_t size) {
672
21.0k
    unsigned long key, okey;
673
21.0k
    size_t oldsize, i;
674
21.0k
    xmlDictEntryPtr iter, next;
675
21.0k
    struct _xmlDictEntry *olddict;
676
#ifdef DEBUG_GROW
677
    unsigned long nbElem = 0;
678
#endif
679
21.0k
    int ret = 0;
680
21.0k
    int keep_keys = 1;
681
682
21.0k
    if (dict == NULL)
683
0
  return(-1);
684
21.0k
    if (size < 8)
685
0
        return(-1);
686
21.0k
    if (size > 8 * 2048)
687
0
  return(-1);
688
689
#ifdef DICT_DEBUG_PATTERNS
690
    fprintf(stderr, "*");
691
#endif
692
693
21.0k
    oldsize = dict->size;
694
21.0k
    olddict = dict->dict;
695
21.0k
    if (olddict == NULL)
696
0
        return(-1);
697
21.0k
    if (oldsize == MIN_DICT_SIZE)
698
21.0k
        keep_keys = 0;
699
700
21.0k
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
701
21.0k
    if (dict->dict == NULL) {
702
0
  dict->dict = olddict;
703
0
  return(-1);
704
0
    }
705
21.0k
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
706
21.0k
    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
2.73M
    for (i = 0; i < oldsize; i++) {
715
2.71M
  if (olddict[i].valid == 0)
716
1.93M
      continue;
717
718
779k
  if (keep_keys)
719
11.4k
      okey = olddict[i].okey;
720
767k
  else
721
767k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
722
779k
  key = okey % dict->size;
723
724
779k
  if (dict->dict[key].valid == 0) {
725
742k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
726
742k
      dict->dict[key].next = NULL;
727
742k
      dict->dict[key].okey = okey;
728
742k
  } else {
729
37.0k
      xmlDictEntryPtr entry;
730
731
37.0k
      entry = xmlMalloc(sizeof(xmlDictEntry));
732
37.0k
      if (entry != NULL) {
733
37.0k
    entry->name = olddict[i].name;
734
37.0k
    entry->len = olddict[i].len;
735
37.0k
    entry->okey = okey;
736
37.0k
    entry->next = dict->dict[key].next;
737
37.0k
    entry->valid = 1;
738
37.0k
    dict->dict[key].next = entry;
739
37.0k
      } 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
37.0k
  }
747
#ifdef DEBUG_GROW
748
  nbElem++;
749
#endif
750
779k
    }
751
752
2.73M
    for (i = 0; i < oldsize; i++) {
753
2.71M
  iter = olddict[i].next;
754
3.30M
  while (iter) {
755
588k
      next = iter->next;
756
757
      /*
758
       * put back the entry in the new dict
759
       */
760
761
588k
      if (keep_keys)
762
3.28k
    okey = iter->okey;
763
584k
      else
764
584k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
765
588k
      key = okey % dict->size;
766
588k
      if (dict->dict[key].valid == 0) {
767
500k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
768
500k
    dict->dict[key].next = NULL;
769
500k
    dict->dict[key].valid = 1;
770
500k
    dict->dict[key].okey = okey;
771
500k
    xmlFree(iter);
772
500k
      } else {
773
87.5k
    iter->next = dict->dict[key].next;
774
87.5k
    iter->okey = okey;
775
87.5k
    dict->dict[key].next = iter;
776
87.5k
      }
777
778
#ifdef DEBUG_GROW
779
      nbElem++;
780
#endif
781
782
588k
      iter = next;
783
588k
  }
784
2.71M
    }
785
786
21.0k
    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
21.0k
    return(ret);
794
21.0k
}
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
12.0M
xmlDictFree(xmlDictPtr dict) {
805
12.0M
    size_t i;
806
12.0M
    xmlDictEntryPtr iter;
807
12.0M
    xmlDictEntryPtr next;
808
12.0M
    int inside_dict = 0;
809
12.0M
    xmlDictStringsPtr pool, nextp;
810
811
12.0M
    if (dict == NULL)
812
0
  return;
813
814
12.0M
    if (!xmlDictInitialized)
815
0
        if (!__xmlInitializeDict())
816
0
            return;
817
818
    /* decrement the counter, it may be shared by a parser and docs */
819
12.0M
    xmlMutexLock(xmlDictMutex);
820
12.0M
    dict->ref_counter--;
821
12.0M
    if (dict->ref_counter > 0) {
822
4.51M
        xmlMutexUnlock(xmlDictMutex);
823
4.51M
        return;
824
4.51M
    }
825
826
7.55M
    xmlMutexUnlock(xmlDictMutex);
827
828
7.55M
    if (dict->subdict != NULL) {
829
0
        xmlDictFree(dict->subdict);
830
0
    }
831
832
7.55M
    if (dict->dict) {
833
656M
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
834
649M
      iter = &(dict->dict[i]);
835
649M
      if (iter->valid == 0)
836
623M
    continue;
837
25.8M
      inside_dict = 1;
838
61.0M
      while (iter) {
839
35.2M
    next = iter->next;
840
35.2M
    if (!inside_dict)
841
9.36M
        xmlFree(iter);
842
35.2M
    dict->nbElems--;
843
35.2M
    inside_dict = 0;
844
35.2M
    iter = next;
845
35.2M
      }
846
25.8M
  }
847
7.55M
  xmlFree(dict->dict);
848
7.55M
    }
849
7.55M
    pool = dict->strings;
850
14.4M
    while (pool != NULL) {
851
6.88M
        nextp = pool->next;
852
6.88M
  xmlFree(pool);
853
6.88M
  pool = nextp;
854
6.88M
    }
855
7.55M
    xmlFree(dict);
856
7.55M
}
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
1.11G
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
870
1.11G
    unsigned long key, okey, nbi = 0;
871
1.11G
    xmlDictEntryPtr entry;
872
1.11G
    xmlDictEntryPtr insert;
873
1.11G
    const xmlChar *ret;
874
1.11G
    unsigned int l;
875
876
1.11G
    if ((dict == NULL) || (name == NULL))
877
1.33M
  return(NULL);
878
879
1.10G
    if (len < 0)
880
657M
        l = strlen((const char *) name);
881
451M
    else
882
451M
        l = len;
883
884
1.10G
    if (((dict->limit > 0) && (l >= dict->limit)) ||
885
1.10G
        (l > INT_MAX / 2))
886
0
        return(NULL);
887
888
    /*
889
     * Check for duplicate and insertion location.
890
     */
891
1.10G
    okey = xmlDictComputeKey(dict, name, l);
892
1.10G
    key = okey % dict->size;
893
1.10G
    if (dict->dict[key].valid == 0) {
894
25.3M
  insert = NULL;
895
1.08G
    } else {
896
1.30G
  for (insert = &(dict->dict[key]); insert->next != NULL;
897
1.08G
       insert = insert->next) {
898
359M
#ifdef __GNUC__
899
359M
      if ((insert->okey == okey) && (insert->len == l)) {
900
134M
    if (!memcmp(insert->name, name, l))
901
133M
        return(insert->name);
902
134M
      }
903
#else
904
      if ((insert->okey == okey) && (insert->len == l) &&
905
          (!xmlStrncmp(insert->name, name, l)))
906
    return(insert->name);
907
#endif
908
225M
      nbi++;
909
225M
  }
910
950M
#ifdef __GNUC__
911
950M
  if ((insert->okey == okey) && (insert->len == l)) {
912
930M
      if (!memcmp(insert->name, name, l))
913
940M
    return(insert->name);
914
930M
  }
915
#else
916
  if ((insert->okey == okey) && (insert->len == l) &&
917
      (!xmlStrncmp(insert->name, name, l)))
918
      return(insert->name);
919
#endif
920
950M
    }
921
922
35.1M
    if (dict->subdict) {
923
0
        unsigned long skey;
924
925
        /* we cannot always reuse the same okey for the subdict */
926
0
        if (((dict->size == MIN_DICT_SIZE) &&
927
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
928
0
            ((dict->size != MIN_DICT_SIZE) &&
929
0
       (dict->subdict->size == MIN_DICT_SIZE)))
930
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
931
0
  else
932
0
      skey = okey;
933
934
0
  key = skey % dict->subdict->size;
935
0
  if (dict->subdict->dict[key].valid != 0) {
936
0
      xmlDictEntryPtr tmp;
937
938
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
939
0
     tmp = tmp->next) {
940
0
#ifdef __GNUC__
941
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
942
0
        if (!memcmp(tmp->name, name, l))
943
0
      return(tmp->name);
944
0
    }
945
#else
946
    if ((tmp->okey == skey) && (tmp->len == l) &&
947
        (!xmlStrncmp(tmp->name, name, l)))
948
        return(tmp->name);
949
#endif
950
0
    nbi++;
951
0
      }
952
0
#ifdef __GNUC__
953
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
954
0
    if (!memcmp(tmp->name, name, l))
955
0
        return(tmp->name);
956
0
      }
957
#else
958
      if ((tmp->okey == skey) && (tmp->len == l) &&
959
    (!xmlStrncmp(tmp->name, name, l)))
960
    return(tmp->name);
961
#endif
962
0
  }
963
0
  key = okey % dict->size;
964
0
    }
965
966
35.1M
    ret = xmlDictAddString(dict, name, l);
967
35.1M
    if (ret == NULL)
968
0
        return(NULL);
969
35.1M
    if (insert == NULL) {
970
25.3M
  entry = &(dict->dict[key]);
971
25.3M
    } else {
972
9.80M
  entry = xmlMalloc(sizeof(xmlDictEntry));
973
9.80M
  if (entry == NULL)
974
0
       return(NULL);
975
9.80M
    }
976
35.1M
    entry->name = ret;
977
35.1M
    entry->len = l;
978
35.1M
    entry->next = NULL;
979
35.1M
    entry->valid = 1;
980
35.1M
    entry->okey = okey;
981
982
983
35.1M
    if (insert != NULL)
984
9.80M
  insert->next = entry;
985
986
35.1M
    dict->nbElems++;
987
988
35.1M
    if ((nbi > MAX_HASH_LEN) &&
989
35.1M
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
990
20.1k
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
991
0
      return(NULL);
992
20.1k
    }
993
    /* Note that entry may have been freed at this point by xmlDictGrow */
994
995
35.1M
    return(ret);
996
35.1M
}
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, nbi = 0;
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
      nbi++;
1046
0
  }
1047
0
#ifdef __GNUC__
1048
0
  if ((insert->okey == okey) && (insert->len == l)) {
1049
0
      if (!memcmp(insert->name, name, l))
1050
0
    return(insert->name);
1051
0
  }
1052
#else
1053
  if ((insert->okey == okey) && (insert->len == l) &&
1054
      (!xmlStrncmp(insert->name, name, l)))
1055
      return(insert->name);
1056
#endif
1057
0
    }
1058
1059
0
    if (dict->subdict) {
1060
0
        unsigned long skey;
1061
1062
        /* we cannot always reuse the same okey for the subdict */
1063
0
        if (((dict->size == MIN_DICT_SIZE) &&
1064
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1065
0
            ((dict->size != MIN_DICT_SIZE) &&
1066
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1067
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1068
0
  else
1069
0
      skey = okey;
1070
1071
0
  key = skey % dict->subdict->size;
1072
0
  if (dict->subdict->dict[key].valid != 0) {
1073
0
      xmlDictEntryPtr tmp;
1074
1075
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1076
0
     tmp = tmp->next) {
1077
0
#ifdef __GNUC__
1078
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1079
0
        if (!memcmp(tmp->name, name, l))
1080
0
      return(tmp->name);
1081
0
    }
1082
#else
1083
    if ((tmp->okey == skey) && (tmp->len == l) &&
1084
        (!xmlStrncmp(tmp->name, name, l)))
1085
        return(tmp->name);
1086
#endif
1087
0
    nbi++;
1088
0
      }
1089
0
#ifdef __GNUC__
1090
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1091
0
    if (!memcmp(tmp->name, name, l))
1092
0
        return(tmp->name);
1093
0
      }
1094
#else
1095
      if ((tmp->okey == skey) && (tmp->len == l) &&
1096
    (!xmlStrncmp(tmp->name, name, l)))
1097
    return(tmp->name);
1098
#endif
1099
0
  }
1100
0
    }
1101
1102
    /* not found */
1103
0
    return(NULL);
1104
0
}
1105
1106
/**
1107
 * xmlDictQLookup:
1108
 * @dict: the dictionary
1109
 * @prefix: the prefix
1110
 * @name: the name
1111
 *
1112
 * Add the QName @prefix:@name to the hash @dict if not present.
1113
 *
1114
 * Returns the internal copy of the QName or NULL in case of internal error
1115
 */
1116
const xmlChar *
1117
700k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1118
700k
    unsigned long okey, key, nbi = 0;
1119
700k
    xmlDictEntryPtr entry;
1120
700k
    xmlDictEntryPtr insert;
1121
700k
    const xmlChar *ret;
1122
700k
    unsigned int len, plen, l;
1123
1124
700k
    if ((dict == NULL) || (name == NULL))
1125
0
  return(NULL);
1126
700k
    if (prefix == NULL)
1127
0
        return(xmlDictLookup(dict, name, -1));
1128
1129
700k
    l = len = strlen((const char *) name);
1130
700k
    plen = strlen((const char *) prefix);
1131
700k
    len += 1 + plen;
1132
1133
    /*
1134
     * Check for duplicate and insertion location.
1135
     */
1136
700k
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1137
700k
    key = okey % dict->size;
1138
700k
    if (dict->dict[key].valid == 0) {
1139
106k
  insert = NULL;
1140
593k
    } else {
1141
691k
  for (insert = &(dict->dict[key]); insert->next != NULL;
1142
593k
       insert = insert->next) {
1143
162k
      if ((insert->okey == okey) && (insert->len == len) &&
1144
162k
          (xmlStrQEqual(prefix, name, insert->name)))
1145
64.6k
    return(insert->name);
1146
97.3k
      nbi++;
1147
97.3k
  }
1148
529k
  if ((insert->okey == okey) && (insert->len == len) &&
1149
529k
      (xmlStrQEqual(prefix, name, insert->name)))
1150
494k
      return(insert->name);
1151
529k
    }
1152
1153
141k
    if (dict->subdict) {
1154
0
        unsigned long skey;
1155
1156
        /* we cannot always reuse the same okey for the subdict */
1157
0
        if (((dict->size == MIN_DICT_SIZE) &&
1158
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1159
0
            ((dict->size != MIN_DICT_SIZE) &&
1160
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1161
0
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1162
0
  else
1163
0
      skey = okey;
1164
1165
0
  key = skey % dict->subdict->size;
1166
0
  if (dict->subdict->dict[key].valid != 0) {
1167
0
      xmlDictEntryPtr tmp;
1168
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1169
0
     tmp = tmp->next) {
1170
0
    if ((tmp->okey == skey) && (tmp->len == len) &&
1171
0
        (xmlStrQEqual(prefix, name, tmp->name)))
1172
0
        return(tmp->name);
1173
0
    nbi++;
1174
0
      }
1175
0
      if ((tmp->okey == skey) && (tmp->len == len) &&
1176
0
    (xmlStrQEqual(prefix, name, tmp->name)))
1177
0
    return(tmp->name);
1178
0
  }
1179
0
  key = okey % dict->size;
1180
0
    }
1181
1182
141k
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1183
141k
    if (ret == NULL)
1184
0
        return(NULL);
1185
141k
    if (insert == NULL) {
1186
106k
  entry = &(dict->dict[key]);
1187
106k
    } else {
1188
34.9k
  entry = xmlMalloc(sizeof(xmlDictEntry));
1189
34.9k
  if (entry == NULL)
1190
0
       return(NULL);
1191
34.9k
    }
1192
141k
    entry->name = ret;
1193
141k
    entry->len = len;
1194
141k
    entry->next = NULL;
1195
141k
    entry->valid = 1;
1196
141k
    entry->okey = okey;
1197
1198
141k
    if (insert != NULL)
1199
34.9k
  insert->next = entry;
1200
1201
141k
    dict->nbElems++;
1202
1203
141k
    if ((nbi > MAX_HASH_LEN) &&
1204
141k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1205
852
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1206
    /* Note that entry may have been freed at this point by xmlDictGrow */
1207
1208
141k
    return(ret);
1209
141k
}
1210
1211
/**
1212
 * xmlDictOwns:
1213
 * @dict: the dictionary
1214
 * @str: the string
1215
 *
1216
 * check if a string is owned by the dictionary
1217
 *
1218
 * Returns 1 if true, 0 if false and -1 in case of error
1219
 * -1 in case of error
1220
 */
1221
int
1222
544M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1223
544M
    xmlDictStringsPtr pool;
1224
1225
544M
    if ((dict == NULL) || (str == NULL))
1226
0
  return(-1);
1227
544M
    pool = dict->strings;
1228
884M
    while (pool != NULL) {
1229
604M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1230
264M
      return(1);
1231
339M
  pool = pool->next;
1232
339M
    }
1233
279M
    if (dict->subdict)
1234
0
        return(xmlDictOwns(dict->subdict, str));
1235
279M
    return(0);
1236
279M
}
1237
1238
/**
1239
 * xmlDictSize:
1240
 * @dict: the dictionary
1241
 *
1242
 * Query the number of elements installed in the hash @dict.
1243
 *
1244
 * Returns the number of elements in the dictionary or
1245
 * -1 in case of error
1246
 */
1247
int
1248
0
xmlDictSize(xmlDictPtr dict) {
1249
0
    if (dict == NULL)
1250
0
  return(-1);
1251
0
    if (dict->subdict)
1252
0
        return(dict->nbElems + dict->subdict->nbElems);
1253
0
    return(dict->nbElems);
1254
0
}
1255
1256
/**
1257
 * xmlDictSetLimit:
1258
 * @dict: the dictionary
1259
 * @limit: the limit in bytes
1260
 *
1261
 * Set a size limit for the dictionary
1262
 * Added in 2.9.0
1263
 *
1264
 * Returns the previous limit of the dictionary or 0
1265
 */
1266
size_t
1267
8.10M
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1268
8.10M
    size_t ret;
1269
1270
8.10M
    if (dict == NULL)
1271
0
  return(0);
1272
8.10M
    ret = dict->limit;
1273
8.10M
    dict->limit = limit;
1274
8.10M
    return(ret);
1275
8.10M
}
1276
1277
/**
1278
 * xmlDictGetUsage:
1279
 * @dict: the dictionary
1280
 *
1281
 * Get how much memory is used by a dictionary for strings
1282
 * Added in 2.9.0
1283
 *
1284
 * Returns the amount of strings allocated
1285
 */
1286
size_t
1287
0
xmlDictGetUsage(xmlDictPtr dict) {
1288
0
    xmlDictStringsPtr pool;
1289
0
    size_t limit = 0;
1290
1291
0
    if (dict == NULL)
1292
0
  return(0);
1293
0
    pool = dict->strings;
1294
0
    while (pool != NULL) {
1295
0
        limit += pool->size;
1296
0
  pool = pool->next;
1297
0
    }
1298
0
    return(limit);
1299
0
}
1300