Coverage Report

Created: 2024-05-29 15:23

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