Coverage Report

Created: 2023-06-07 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.17M
#define MAX_HASH_LEN 3
64
26.7M
#define MIN_DICT_SIZE 128
65
5.62k
#define MAX_DICT_HASH 100000000
66
#define WITH_BIG_KEY
67
68
#ifdef WITH_BIG_KEY
69
#define xmlDictComputeKey(dict, name, len)                              \
70
20.5M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
71
20.5M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
72
20.5M
     xmlDictComputeBigKey(name, len, (dict)->seed))
73
74
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
75
178k
    (((prefix) == NULL) ?                                               \
76
178k
      (xmlDictComputeKey(dict, name, len)) :                             \
77
178k
      (((dict)->size == MIN_DICT_SIZE) ?                                \
78
178k
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
79
178k
       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.14M
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
223
1.14M
    xmlDictStringsPtr pool;
224
1.14M
    const xmlChar *ret;
225
1.14M
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
226
1.14M
    size_t limit = 0;
227
228
#ifdef DICT_DEBUG_PATTERNS
229
    fprintf(stderr, "-");
230
#endif
231
1.14M
    pool = dict->strings;
232
1.15M
    while (pool != NULL) {
233
1.07M
  if ((size_t)(pool->end - pool->free) > namelen)
234
1.06M
      goto found_pool;
235
8.77k
  if (pool->size > size) size = pool->size;
236
8.77k
        limit += pool->size;
237
8.77k
  pool = pool->next;
238
8.77k
    }
239
    /*
240
     * Not found, need to allocate
241
     */
242
77.6k
    if (pool == NULL) {
243
77.6k
        if ((dict->limit > 0) && (limit > dict->limit)) {
244
69
            return(NULL);
245
69
        }
246
247
77.6k
        if (size == 0) size = 1000;
248
6.27k
  else size *= 4; /* exponential growth */
249
77.6k
        if (size < 4 * namelen)
250
1.56k
      size = 4 * namelen; /* just in case ! */
251
77.6k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
252
77.6k
  if (pool == NULL)
253
2.58k
      return(NULL);
254
75.0k
  pool->size = size;
255
75.0k
  pool->nbStrings = 0;
256
75.0k
  pool->free = &pool->array[0];
257
75.0k
  pool->end = &pool->array[size];
258
75.0k
  pool->next = dict->strings;
259
75.0k
  dict->strings = pool;
260
#ifdef DICT_DEBUG_PATTERNS
261
        fprintf(stderr, "+");
262
#endif
263
75.0k
    }
264
1.14M
found_pool:
265
1.14M
    ret = pool->free;
266
1.14M
    memcpy(pool->free, name, namelen);
267
1.14M
    pool->free += namelen;
268
1.14M
    *(pool->free++) = 0;
269
1.14M
    pool->nbStrings++;
270
1.14M
    return(ret);
271
77.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.8k
{
289
30.8k
    xmlDictStringsPtr pool;
290
30.8k
    const xmlChar *ret;
291
30.8k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
292
30.8k
    size_t limit = 0;
293
294
30.8k
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
295
296
#ifdef DICT_DEBUG_PATTERNS
297
    fprintf(stderr, "=");
298
#endif
299
30.8k
    pool = dict->strings;
300
31.0k
    while (pool != NULL) {
301
30.6k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
302
30.4k
      goto found_pool;
303
234
  if (pool->size > size) size = pool->size;
304
234
        limit += pool->size;
305
234
  pool = pool->next;
306
234
    }
307
    /*
308
     * Not found, need to allocate
309
     */
310
404
    if (pool == NULL) {
311
404
        if ((dict->limit > 0) && (limit > dict->limit)) {
312
0
            return(NULL);
313
0
        }
314
315
404
        if (size == 0) size = 1000;
316
114
  else size *= 4; /* exponential growth */
317
404
        if (size < 4 * (namelen + plen + 1))
318
2
      size = 4 * (namelen + plen + 1); /* just in case ! */
319
404
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
320
404
  if (pool == NULL)
321
97
      return(NULL);
322
307
  pool->size = size;
323
307
  pool->nbStrings = 0;
324
307
  pool->free = &pool->array[0];
325
307
  pool->end = &pool->array[size];
326
307
  pool->next = dict->strings;
327
307
  dict->strings = pool;
328
#ifdef DICT_DEBUG_PATTERNS
329
        fprintf(stderr, "+");
330
#endif
331
307
    }
332
30.7k
found_pool:
333
30.7k
    ret = pool->free;
334
30.7k
    memcpy(pool->free, prefix, plen);
335
30.7k
    pool->free += plen;
336
30.7k
    *(pool->free++) = ':';
337
30.7k
    memcpy(pool->free, name, namelen);
338
30.7k
    pool->free += namelen;
339
30.7k
    *(pool->free++) = 0;
340
30.7k
    pool->nbStrings++;
341
30.7k
    return(ret);
342
404
}
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
3.85M
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
361
3.85M
    uint32_t hash;
362
3.85M
    int i;
363
364
3.85M
    if (namelen <= 0 || data == NULL) return(0);
365
366
3.82M
    hash = seed;
367
368
804M
    for (i = 0;i < namelen; i++) {
369
800M
        hash += data[i];
370
800M
  hash += (hash << 10);
371
800M
  hash ^= (hash >> 6);
372
800M
    }
373
3.82M
    hash += (hash << 3);
374
3.82M
    hash ^= (hash >> 11);
375
3.82M
    hash += (hash << 15);
376
377
3.82M
    return hash;
378
3.85M
}
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
29.4k
{
399
29.4k
    uint32_t hash;
400
29.4k
    int i;
401
402
29.4k
    hash = seed;
403
404
197k
    for (i = 0;i < plen; i++) {
405
167k
        hash += prefix[i];
406
167k
  hash += (hash << 10);
407
167k
  hash ^= (hash >> 6);
408
167k
    }
409
29.4k
    hash += ':';
410
29.4k
    hash += (hash << 10);
411
29.4k
    hash ^= (hash >> 6);
412
413
2.36M
    for (i = 0;i < len; i++) {
414
2.33M
        hash += name[i];
415
2.33M
  hash += (hash << 10);
416
2.33M
  hash ^= (hash >> 6);
417
2.33M
    }
418
29.4k
    hash += (hash << 3);
419
29.4k
    hash ^= (hash >> 11);
420
29.4k
    hash += (hash << 15);
421
422
29.4k
    return hash;
423
29.4k
}
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
16.7M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
434
16.7M
    unsigned long value = seed;
435
436
16.7M
    if ((name == NULL) || (namelen <= 0))
437
74.6k
        return(value);
438
16.6M
    value += *name;
439
16.6M
    value <<= 5;
440
16.6M
    if (namelen > 10) {
441
2.32M
        value += name[namelen - 1];
442
2.32M
        namelen = 10;
443
2.32M
    }
444
16.6M
    switch (namelen) {
445
2.88M
        case 10: value += name[9];
446
        /* Falls through. */
447
3.32M
        case 9: value += name[8];
448
        /* Falls through. */
449
4.12M
        case 8: value += name[7];
450
        /* Falls through. */
451
5.20M
        case 7: value += name[6];
452
        /* Falls through. */
453
6.29M
        case 6: value += name[5];
454
        /* Falls through. */
455
9.42M
        case 5: value += name[4];
456
        /* Falls through. */
457
11.4M
        case 4: value += name[3];
458
        /* Falls through. */
459
13.0M
        case 3: value += name[2];
460
        /* Falls through. */
461
13.6M
        case 2: value += name[1];
462
        /* Falls through. */
463
16.6M
        default: break;
464
16.6M
    }
465
16.6M
    return(value);
466
16.6M
}
467
468
/*
469
 * xmlDictComputeFastQKey:
470
 *
471
 * Calculate a hash key for two strings using a fast hash function
472
 * that works well for low hash table fill.
473
 *
474
 * Neither of the two strings must be NULL.
475
 */
476
static unsigned long
477
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
478
                       const xmlChar *name, int len, int seed)
479
148k
{
480
148k
    unsigned long value = seed;
481
482
148k
    if (plen == 0)
483
0
  value += 30 * ':';
484
148k
    else
485
148k
  value += 30 * (*prefix);
486
487
148k
    if (len > 10) {
488
18.4k
        int offset = len - (plen + 1 + 1);
489
18.4k
  if (offset < 0)
490
1.17k
      offset = len - (10 + 1);
491
18.4k
  value += name[offset];
492
18.4k
        len = 10;
493
18.4k
  if (plen > 10)
494
2.05k
      plen = 10;
495
18.4k
    }
496
148k
    switch (plen) {
497
2.59k
        case 10: value += prefix[9];
498
        /* Falls through. */
499
4.49k
        case 9: value += prefix[8];
500
        /* Falls through. */
501
8.98k
        case 8: value += prefix[7];
502
        /* Falls through. */
503
13.3k
        case 7: value += prefix[6];
504
        /* Falls through. */
505
21.5k
        case 6: value += prefix[5];
506
        /* Falls through. */
507
24.2k
        case 5: value += prefix[4];
508
        /* Falls through. */
509
30.0k
        case 4: value += prefix[3];
510
        /* Falls through. */
511
51.6k
        case 3: value += prefix[2];
512
        /* Falls through. */
513
97.2k
        case 2: value += prefix[1];
514
        /* Falls through. */
515
144k
        case 1: value += prefix[0];
516
        /* Falls through. */
517
148k
        default: break;
518
148k
    }
519
148k
    len -= plen;
520
148k
    if (len > 0) {
521
111k
        value += ':';
522
111k
  len--;
523
111k
    }
524
148k
    switch (len) {
525
0
        case 10: value += name[9];
526
        /* Falls through. */
527
0
        case 9: value += name[8];
528
        /* Falls through. */
529
10.6k
        case 8: value += name[7];
530
        /* Falls through. */
531
15.4k
        case 7: value += name[6];
532
        /* Falls through. */
533
25.2k
        case 6: value += name[5];
534
        /* Falls through. */
535
35.3k
        case 5: value += name[4];
536
        /* Falls through. */
537
38.6k
        case 4: value += name[3];
538
        /* Falls through. */
539
41.8k
        case 3: value += name[2];
540
        /* Falls through. */
541
54.2k
        case 2: value += name[1];
542
        /* Falls through. */
543
102k
        case 1: value += name[0];
544
        /* Falls through. */
545
148k
        default: break;
546
148k
    }
547
148k
    return(value);
548
148k
}
549
550
/**
551
 * xmlDictCreate:
552
 *
553
 * Create a new dictionary
554
 *
555
 * Returns the newly created dictionary, or NULL if an error occurred.
556
 */
557
xmlDictPtr
558
498k
xmlDictCreate(void) {
559
498k
    xmlDictPtr dict;
560
561
498k
    xmlInitParser();
562
563
#ifdef DICT_DEBUG_PATTERNS
564
    fprintf(stderr, "C");
565
#endif
566
567
498k
    dict = xmlMalloc(sizeof(xmlDict));
568
498k
    if (dict) {
569
498k
        dict->ref_counter = 1;
570
498k
        dict->limit = 0;
571
572
498k
        dict->size = MIN_DICT_SIZE;
573
498k
  dict->nbElems = 0;
574
498k
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
575
498k
  dict->strings = NULL;
576
498k
  dict->subdict = NULL;
577
498k
        if (dict->dict) {
578
498k
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
579
#ifdef DICT_RANDOMIZATION
580
            dict->seed = __xmlRandom();
581
#else
582
498k
            dict->seed = 0;
583
498k
#endif
584
498k
      return(dict);
585
498k
        }
586
32
        xmlFree(dict);
587
32
    }
588
115
    return(NULL);
589
498k
}
590
591
/**
592
 * xmlDictCreateSub:
593
 * @sub: an existing dictionary
594
 *
595
 * Create a new dictionary, inheriting strings from the read-only
596
 * dictionary @sub. On lookup, strings are first searched in the
597
 * new dictionary, then in @sub, and if not found are created in the
598
 * new dictionary.
599
 *
600
 * Returns the newly created dictionary, or NULL if an error occurred.
601
 */
602
xmlDictPtr
603
14.5k
xmlDictCreateSub(xmlDictPtr sub) {
604
14.5k
    xmlDictPtr dict = xmlDictCreate();
605
606
14.5k
    if ((dict != NULL) && (sub != NULL)) {
607
#ifdef DICT_DEBUG_PATTERNS
608
        fprintf(stderr, "R");
609
#endif
610
14.5k
        dict->seed = sub->seed;
611
14.5k
        dict->subdict = sub;
612
14.5k
  xmlDictReference(dict->subdict);
613
14.5k
    }
614
14.5k
    return(dict);
615
14.5k
}
616
617
/**
618
 * xmlDictReference:
619
 * @dict: the dictionary
620
 *
621
 * Increment the reference counter of a dictionary
622
 *
623
 * Returns 0 in case of success and -1 in case of error
624
 */
625
int
626
1.50M
xmlDictReference(xmlDictPtr dict) {
627
1.50M
    if (dict == NULL) return -1;
628
1.50M
    xmlMutexLock(&xmlDictMutex);
629
1.50M
    dict->ref_counter++;
630
1.50M
    xmlMutexUnlock(&xmlDictMutex);
631
1.50M
    return(0);
632
1.50M
}
633
634
/**
635
 * xmlDictGrow:
636
 * @dict: the dictionary
637
 * @size: the new size of the dictionary
638
 *
639
 * resize the dictionary
640
 *
641
 * Returns 0 in case of success, -1 in case of failure
642
 */
643
static int
644
2.74k
xmlDictGrow(xmlDictPtr dict, size_t size) {
645
2.74k
    unsigned long key, okey;
646
2.74k
    size_t oldsize, i;
647
2.74k
    xmlDictEntryPtr iter, next;
648
2.74k
    struct _xmlDictEntry *olddict;
649
#ifdef DEBUG_GROW
650
    unsigned long nbElem = 0;
651
#endif
652
2.74k
    int ret = 0;
653
2.74k
    int keep_keys = 1;
654
655
2.74k
    if (dict == NULL)
656
0
  return(-1);
657
2.74k
    if (size < 8)
658
0
        return(-1);
659
2.74k
    if (size > MAX_DICT_HASH)
660
0
  return(-1);
661
662
#ifdef DICT_DEBUG_PATTERNS
663
    fprintf(stderr, "*");
664
#endif
665
666
2.74k
    oldsize = dict->size;
667
2.74k
    olddict = dict->dict;
668
2.74k
    if (olddict == NULL)
669
0
        return(-1);
670
2.74k
    if (oldsize == MIN_DICT_SIZE)
671
2.65k
        keep_keys = 0;
672
673
2.74k
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
674
2.74k
    if (dict->dict == NULL) {
675
3
  dict->dict = olddict;
676
3
  return(-1);
677
3
    }
678
2.74k
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
679
2.74k
    dict->size = size;
680
681
    /*  If the two loops are merged, there would be situations where
682
  a new entry needs to allocated and data copied into it from
683
  the main dict. It is nicer to run through the array twice, first
684
  copying all the elements in the main array (less probability of
685
  allocate) and then the rest, so we only free in the second loop.
686
    */
687
57.9M
    for (i = 0; i < oldsize; i++) {
688
57.9M
  if (olddict[i].valid == 0)
689
57.8M
      continue;
690
691
80.2k
  if (keep_keys)
692
2.91k
      okey = olddict[i].okey;
693
77.3k
  else
694
77.3k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
695
80.2k
  key = okey % dict->size;
696
697
80.2k
  if (dict->dict[key].valid == 0) {
698
78.5k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
699
78.5k
      dict->dict[key].next = NULL;
700
78.5k
      dict->dict[key].okey = okey;
701
78.5k
  } else {
702
1.69k
      xmlDictEntryPtr entry;
703
704
1.69k
      entry = xmlMalloc(sizeof(xmlDictEntry));
705
1.69k
      if (entry != NULL) {
706
1.67k
    entry->name = olddict[i].name;
707
1.67k
    entry->len = olddict[i].len;
708
1.67k
    entry->okey = okey;
709
1.67k
    entry->next = dict->dict[key].next;
710
1.67k
    entry->valid = 1;
711
1.67k
    dict->dict[key].next = entry;
712
1.67k
      } else {
713
          /*
714
     * we don't have much ways to alert from here
715
     * result is losing an entry and unicity guarantee
716
     */
717
11
          ret = -1;
718
11
      }
719
1.69k
  }
720
#ifdef DEBUG_GROW
721
  nbElem++;
722
#endif
723
80.2k
    }
724
725
57.9M
    for (i = 0; i < oldsize; i++) {
726
57.9M
  iter = olddict[i].next;
727
57.9M
  while (iter) {
728
38.3k
      next = iter->next;
729
730
      /*
731
       * put back the entry in the new dict
732
       */
733
734
38.3k
      if (keep_keys)
735
588
    okey = iter->okey;
736
37.7k
      else
737
37.7k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
738
38.3k
      key = okey % dict->size;
739
38.3k
      if (dict->dict[key].valid == 0) {
740
36.0k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
741
36.0k
    dict->dict[key].next = NULL;
742
36.0k
    dict->dict[key].valid = 1;
743
36.0k
    dict->dict[key].okey = okey;
744
36.0k
    xmlFree(iter);
745
36.0k
      } else {
746
2.36k
    iter->next = dict->dict[key].next;
747
2.36k
    iter->okey = okey;
748
2.36k
    dict->dict[key].next = iter;
749
2.36k
      }
750
751
#ifdef DEBUG_GROW
752
      nbElem++;
753
#endif
754
755
38.3k
      iter = next;
756
38.3k
  }
757
57.9M
    }
758
759
2.74k
    xmlFree(olddict);
760
761
#ifdef DEBUG_GROW
762
    xmlGenericError(xmlGenericErrorContext,
763
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
764
#endif
765
766
2.74k
    return(ret);
767
2.74k
}
768
769
/**
770
 * xmlDictFree:
771
 * @dict: the dictionary
772
 *
773
 * Free the hash @dict and its contents. The userdata is
774
 * deallocated with @f if provided.
775
 */
776
void
777
2.00M
xmlDictFree(xmlDictPtr dict) {
778
2.00M
    size_t i;
779
2.00M
    xmlDictEntryPtr iter;
780
2.00M
    xmlDictEntryPtr next;
781
2.00M
    int inside_dict = 0;
782
2.00M
    xmlDictStringsPtr pool, nextp;
783
784
2.00M
    if (dict == NULL)
785
54
  return;
786
787
    /* decrement the counter, it may be shared by a parser and docs */
788
2.00M
    xmlMutexLock(&xmlDictMutex);
789
2.00M
    dict->ref_counter--;
790
2.00M
    if (dict->ref_counter > 0) {
791
1.50M
        xmlMutexUnlock(&xmlDictMutex);
792
1.50M
        return;
793
1.50M
    }
794
795
498k
    xmlMutexUnlock(&xmlDictMutex);
796
797
498k
    if (dict->subdict != NULL) {
798
14.5k
        xmlDictFree(dict->subdict);
799
14.5k
    }
800
801
498k
    if (dict->dict) {
802
273M
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
803
273M
      iter = &(dict->dict[i]);
804
273M
      if (iter->valid == 0)
805
272M
    continue;
806
942k
      inside_dict = 1;
807
2.11M
      while (iter) {
808
1.17M
    next = iter->next;
809
1.17M
    if (!inside_dict)
810
227k
        xmlFree(iter);
811
1.17M
    dict->nbElems--;
812
1.17M
    inside_dict = 0;
813
1.17M
    iter = next;
814
1.17M
      }
815
942k
  }
816
498k
  xmlFree(dict->dict);
817
498k
    }
818
498k
    pool = dict->strings;
819
573k
    while (pool != NULL) {
820
75.3k
        nextp = pool->next;
821
75.3k
  xmlFree(pool);
822
75.3k
  pool = nextp;
823
75.3k
    }
824
498k
    xmlFree(dict);
825
498k
}
826
827
/**
828
 * xmlDictLookup:
829
 * @dict: the dictionary
830
 * @name: the name of the userdata
831
 * @len: the length of the name, if -1 it is recomputed
832
 *
833
 * Add the @name to the dictionary @dict if not present.
834
 *
835
 * Returns the internal copy of the name or NULL in case of internal error
836
 */
837
const xmlChar *
838
20.1M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
839
20.1M
    unsigned long key, okey, nbi = 0;
840
20.1M
    xmlDictEntryPtr entry;
841
20.1M
    xmlDictEntryPtr insert;
842
20.1M
    const xmlChar *ret;
843
20.1M
    unsigned int l;
844
845
20.1M
    if ((dict == NULL) || (name == NULL))
846
41.5k
  return(NULL);
847
848
20.1M
    if (len < 0)
849
7.97M
        l = strlen((const char *) name);
850
12.1M
    else
851
12.1M
        l = len;
852
853
20.1M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
854
20.1M
        (l > INT_MAX / 2))
855
0
        return(NULL);
856
857
    /*
858
     * Check for duplicate and insertion location.
859
     */
860
20.1M
    okey = xmlDictComputeKey(dict, name, l);
861
20.1M
    key = okey % dict->size;
862
20.1M
    if (dict->dict[key].valid == 0) {
863
2.23M
  insert = NULL;
864
17.8M
    } else {
865
22.3M
  for (insert = &(dict->dict[key]); insert->next != NULL;
866
17.8M
       insert = insert->next) {
867
7.83M
#ifdef __GNUC__
868
7.83M
      if ((insert->okey == okey) && (insert->len == l)) {
869
3.50M
    if (!memcmp(insert->name, name, l))
870
3.37M
        return(insert->name);
871
3.50M
      }
872
#else
873
      if ((insert->okey == okey) && (insert->len == l) &&
874
          (!xmlStrncmp(insert->name, name, l)))
875
    return(insert->name);
876
#endif
877
4.46M
      nbi++;
878
4.46M
  }
879
14.5M
#ifdef __GNUC__
880
14.5M
  if ((insert->okey == okey) && (insert->len == l)) {
881
14.0M
      if (!memcmp(insert->name, name, l))
882
14.0M
    return(insert->name);
883
14.0M
  }
884
#else
885
  if ((insert->okey == okey) && (insert->len == l) &&
886
      (!xmlStrncmp(insert->name, name, l)))
887
      return(insert->name);
888
#endif
889
14.5M
    }
890
891
2.70M
    if (dict->subdict) {
892
1.57M
        unsigned long skey;
893
894
        /* we cannot always reuse the same okey for the subdict */
895
1.57M
        if (((dict->size == MIN_DICT_SIZE) &&
896
1.57M
       (dict->subdict->size != MIN_DICT_SIZE)) ||
897
1.57M
            ((dict->size != MIN_DICT_SIZE) &&
898
1.31M
       (dict->subdict->size == MIN_DICT_SIZE)))
899
323k
      skey = xmlDictComputeKey(dict->subdict, name, l);
900
1.25M
  else
901
1.25M
      skey = okey;
902
903
1.57M
  key = skey % dict->subdict->size;
904
1.57M
  if (dict->subdict->dict[key].valid != 0) {
905
1.57M
      xmlDictEntryPtr tmp;
906
907
2.18M
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
908
1.57M
     tmp = tmp->next) {
909
1.15M
#ifdef __GNUC__
910
1.15M
    if ((tmp->okey == skey) && (tmp->len == l)) {
911
551k
        if (!memcmp(tmp->name, name, l))
912
541k
      return(tmp->name);
913
551k
    }
914
#else
915
    if ((tmp->okey == skey) && (tmp->len == l) &&
916
        (!xmlStrncmp(tmp->name, name, l)))
917
        return(tmp->name);
918
#endif
919
610k
    nbi++;
920
610k
      }
921
1.02M
#ifdef __GNUC__
922
1.02M
      if ((tmp->okey == skey) && (tmp->len == l)) {
923
1.02M
    if (!memcmp(tmp->name, name, l))
924
1.02M
        return(tmp->name);
925
1.02M
      }
926
#else
927
      if ((tmp->okey == skey) && (tmp->len == l) &&
928
    (!xmlStrncmp(tmp->name, name, l)))
929
    return(tmp->name);
930
#endif
931
1.02M
  }
932
10.8k
  key = okey % dict->size;
933
10.8k
    }
934
935
1.14M
    ret = xmlDictAddString(dict, name, l);
936
1.14M
    if (ret == NULL)
937
2.65k
        return(NULL);
938
1.14M
    if (insert == NULL) {
939
883k
  entry = &(dict->dict[key]);
940
883k
    } else {
941
257k
  entry = xmlMalloc(sizeof(xmlDictEntry));
942
257k
  if (entry == NULL)
943
570
       return(NULL);
944
257k
    }
945
1.14M
    entry->name = ret;
946
1.14M
    entry->len = l;
947
1.14M
    entry->next = NULL;
948
1.14M
    entry->valid = 1;
949
1.14M
    entry->okey = okey;
950
951
952
1.14M
    if (insert != NULL)
953
256k
  insert->next = entry;
954
955
1.14M
    dict->nbElems++;
956
957
1.14M
    if ((nbi > MAX_HASH_LEN) &&
958
1.14M
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
959
2.70k
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
960
7
      return(NULL);
961
2.70k
    }
962
    /* Note that entry may have been freed at this point by xmlDictGrow */
963
964
1.14M
    return(ret);
965
1.14M
}
966
967
/**
968
 * xmlDictExists:
969
 * @dict: the dictionary
970
 * @name: the name of the userdata
971
 * @len: the length of the name, if -1 it is recomputed
972
 *
973
 * Check if the @name exists in the dictionary @dict.
974
 *
975
 * Returns the internal copy of the name or NULL if not found.
976
 */
977
const xmlChar *
978
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
979
0
    unsigned long key, okey;
980
0
    xmlDictEntryPtr insert;
981
0
    unsigned int l;
982
983
0
    if ((dict == NULL) || (name == NULL))
984
0
  return(NULL);
985
986
0
    if (len < 0)
987
0
        l = strlen((const char *) name);
988
0
    else
989
0
        l = len;
990
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
991
0
        (l > INT_MAX / 2))
992
0
        return(NULL);
993
994
    /*
995
     * Check for duplicate and insertion location.
996
     */
997
0
    okey = xmlDictComputeKey(dict, name, l);
998
0
    key = okey % dict->size;
999
0
    if (dict->dict[key].valid == 0) {
1000
0
  insert = NULL;
1001
0
    } else {
1002
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1003
0
       insert = insert->next) {
1004
0
#ifdef __GNUC__
1005
0
      if ((insert->okey == okey) && (insert->len == l)) {
1006
0
    if (!memcmp(insert->name, name, l))
1007
0
        return(insert->name);
1008
0
      }
1009
#else
1010
      if ((insert->okey == okey) && (insert->len == l) &&
1011
          (!xmlStrncmp(insert->name, name, l)))
1012
    return(insert->name);
1013
#endif
1014
0
  }
1015
0
#ifdef __GNUC__
1016
0
  if ((insert->okey == okey) && (insert->len == l)) {
1017
0
      if (!memcmp(insert->name, name, l))
1018
0
    return(insert->name);
1019
0
  }
1020
#else
1021
  if ((insert->okey == okey) && (insert->len == l) &&
1022
      (!xmlStrncmp(insert->name, name, l)))
1023
      return(insert->name);
1024
#endif
1025
0
    }
1026
1027
0
    if (dict->subdict) {
1028
0
        unsigned long skey;
1029
1030
        /* we cannot always reuse the same okey for the subdict */
1031
0
        if (((dict->size == MIN_DICT_SIZE) &&
1032
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1033
0
            ((dict->size != MIN_DICT_SIZE) &&
1034
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1035
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1036
0
  else
1037
0
      skey = okey;
1038
1039
0
  key = skey % dict->subdict->size;
1040
0
  if (dict->subdict->dict[key].valid != 0) {
1041
0
      xmlDictEntryPtr tmp;
1042
1043
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1044
0
     tmp = tmp->next) {
1045
0
#ifdef __GNUC__
1046
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1047
0
        if (!memcmp(tmp->name, name, l))
1048
0
      return(tmp->name);
1049
0
    }
1050
#else
1051
    if ((tmp->okey == skey) && (tmp->len == l) &&
1052
        (!xmlStrncmp(tmp->name, name, l)))
1053
        return(tmp->name);
1054
#endif
1055
0
      }
1056
0
#ifdef __GNUC__
1057
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1058
0
    if (!memcmp(tmp->name, name, l))
1059
0
        return(tmp->name);
1060
0
      }
1061
#else
1062
      if ((tmp->okey == skey) && (tmp->len == l) &&
1063
    (!xmlStrncmp(tmp->name, name, l)))
1064
    return(tmp->name);
1065
#endif
1066
0
  }
1067
0
    }
1068
1069
    /* not found */
1070
0
    return(NULL);
1071
0
}
1072
1073
/**
1074
 * xmlDictQLookup:
1075
 * @dict: the dictionary
1076
 * @prefix: the prefix
1077
 * @name: the name
1078
 *
1079
 * Add the QName @prefix:@name to the hash @dict if not present.
1080
 *
1081
 * Returns the internal copy of the QName or NULL in case of internal error
1082
 */
1083
const xmlChar *
1084
177k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1085
177k
    unsigned long okey, key, nbi = 0;
1086
177k
    xmlDictEntryPtr entry;
1087
177k
    xmlDictEntryPtr insert;
1088
177k
    const xmlChar *ret;
1089
177k
    unsigned int len, plen, l;
1090
1091
177k
    if ((dict == NULL) || (name == NULL))
1092
0
  return(NULL);
1093
177k
    if (prefix == NULL)
1094
0
        return(xmlDictLookup(dict, name, -1));
1095
1096
177k
    l = len = strlen((const char *) name);
1097
177k
    plen = strlen((const char *) prefix);
1098
177k
    len += 1 + plen;
1099
1100
    /*
1101
     * Check for duplicate and insertion location.
1102
     */
1103
177k
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1104
177k
    key = okey % dict->size;
1105
177k
    if (dict->dict[key].valid == 0) {
1106
25.5k
  insert = NULL;
1107
152k
    } else {
1108
187k
  for (insert = &(dict->dict[key]); insert->next != NULL;
1109
152k
       insert = insert->next) {
1110
53.5k
      if ((insert->okey == okey) && (insert->len == len) &&
1111
53.5k
          (xmlStrQEqual(prefix, name, insert->name)))
1112
18.3k
    return(insert->name);
1113
35.2k
      nbi++;
1114
35.2k
  }
1115
134k
  if ((insert->okey == okey) && (insert->len == len) &&
1116
134k
      (xmlStrQEqual(prefix, name, insert->name)))
1117
128k
      return(insert->name);
1118
134k
    }
1119
1120
30.8k
    if (dict->subdict) {
1121
432
        unsigned long skey;
1122
1123
        /* we cannot always reuse the same okey for the subdict */
1124
432
        if (((dict->size == MIN_DICT_SIZE) &&
1125
432
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1126
432
            ((dict->size != MIN_DICT_SIZE) &&
1127
386
       (dict->subdict->size == MIN_DICT_SIZE)))
1128
52
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1129
380
  else
1130
380
      skey = okey;
1131
1132
432
  key = skey % dict->subdict->size;
1133
432
  if (dict->subdict->dict[key].valid != 0) {
1134
107
      xmlDictEntryPtr tmp;
1135
130
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1136
107
     tmp = tmp->next) {
1137
27
    if ((tmp->okey == skey) && (tmp->len == len) &&
1138
27
        (xmlStrQEqual(prefix, name, tmp->name)))
1139
4
        return(tmp->name);
1140
23
    nbi++;
1141
23
      }
1142
103
      if ((tmp->okey == skey) && (tmp->len == len) &&
1143
103
    (xmlStrQEqual(prefix, name, tmp->name)))
1144
10
    return(tmp->name);
1145
103
  }
1146
418
  key = okey % dict->size;
1147
418
    }
1148
1149
30.8k
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1150
30.8k
    if (ret == NULL)
1151
97
        return(NULL);
1152
30.7k
    if (insert == NULL) {
1153
25.4k
  entry = &(dict->dict[key]);
1154
25.4k
    } else {
1155
5.27k
  entry = xmlMalloc(sizeof(xmlDictEntry));
1156
5.27k
  if (entry == NULL)
1157
22
       return(NULL);
1158
5.27k
    }
1159
30.7k
    entry->name = ret;
1160
30.7k
    entry->len = len;
1161
30.7k
    entry->next = NULL;
1162
30.7k
    entry->valid = 1;
1163
30.7k
    entry->okey = okey;
1164
1165
30.7k
    if (insert != NULL)
1166
5.25k
  insert->next = entry;
1167
1168
30.7k
    dict->nbElems++;
1169
1170
30.7k
    if ((nbi > MAX_HASH_LEN) &&
1171
30.7k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1172
38
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1173
    /* Note that entry may have been freed at this point by xmlDictGrow */
1174
1175
30.7k
    return(ret);
1176
30.7k
}
1177
1178
/**
1179
 * xmlDictOwns:
1180
 * @dict: the dictionary
1181
 * @str: the string
1182
 *
1183
 * check if a string is owned by the dictionary
1184
 *
1185
 * Returns 1 if true, 0 if false and -1 in case of error
1186
 * -1 in case of error
1187
 */
1188
int
1189
34.4M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1190
34.4M
    xmlDictStringsPtr pool;
1191
1192
34.4M
    if ((dict == NULL) || (str == NULL))
1193
41
  return(-1);
1194
34.4M
    pool = dict->strings;
1195
59.3M
    while (pool != NULL) {
1196
40.9M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1197
16.0M
      return(1);
1198
24.9M
  pool = pool->next;
1199
24.9M
    }
1200
18.3M
    if (dict->subdict)
1201
7.39M
        return(xmlDictOwns(dict->subdict, str));
1202
10.9M
    return(0);
1203
18.3M
}
1204
1205
/**
1206
 * xmlDictSize:
1207
 * @dict: the dictionary
1208
 *
1209
 * Query the number of elements installed in the hash @dict.
1210
 *
1211
 * Returns the number of elements in the dictionary or
1212
 * -1 in case of error
1213
 */
1214
int
1215
0
xmlDictSize(xmlDictPtr dict) {
1216
0
    if (dict == NULL)
1217
0
  return(-1);
1218
0
    if (dict->subdict)
1219
0
        return(dict->nbElems + dict->subdict->nbElems);
1220
0
    return(dict->nbElems);
1221
0
}
1222
1223
/**
1224
 * xmlDictSetLimit:
1225
 * @dict: the dictionary
1226
 * @limit: the limit in bytes
1227
 *
1228
 * Set a size limit for the dictionary
1229
 * Added in 2.9.0
1230
 *
1231
 * Returns the previous limit of the dictionary or 0
1232
 */
1233
size_t
1234
421k
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1235
421k
    size_t ret;
1236
1237
421k
    if (dict == NULL)
1238
0
  return(0);
1239
421k
    ret = dict->limit;
1240
421k
    dict->limit = limit;
1241
421k
    return(ret);
1242
421k
}
1243
1244
/**
1245
 * xmlDictGetUsage:
1246
 * @dict: the dictionary
1247
 *
1248
 * Get how much memory is used by a dictionary for strings
1249
 * Added in 2.9.0
1250
 *
1251
 * Returns the amount of strings allocated
1252
 */
1253
size_t
1254
0
xmlDictGetUsage(xmlDictPtr dict) {
1255
0
    xmlDictStringsPtr pool;
1256
0
    size_t limit = 0;
1257
1258
0
    if (dict == NULL)
1259
0
  return(0);
1260
0
    pool = dict->strings;
1261
0
    while (pool != NULL) {
1262
0
        limit += pool->size;
1263
0
  pool = pool->next;
1264
0
    }
1265
0
    return(limit);
1266
0
}
1267