Coverage Report

Created: 2023-06-08 06:40

/src/openssl111/crypto/mem_dbg.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the OpenSSL license (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <time.h>
13
#include "internal/cryptlib.h"
14
#include "internal/thread_once.h"
15
#include <openssl/crypto.h>
16
#include <openssl/buffer.h>
17
#include "internal/bio.h"
18
#include <openssl/lhash.h>
19
20
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
21
# include <execinfo.h>
22
#endif
23
24
/*
25
 * The state changes to CRYPTO_MEM_CHECK_ON | CRYPTO_MEM_CHECK_ENABLE when
26
 * the application asks for it (usually after library initialisation for
27
 * which no book-keeping is desired). State CRYPTO_MEM_CHECK_ON exists only
28
 * temporarily when the library thinks that certain allocations should not be
29
 * checked (e.g. the data structures used for memory checking).  It is not
30
 * suitable as an initial state: the library will unexpectedly enable memory
31
 * checking when it executes one of those sections that want to disable
32
 * checking temporarily. State CRYPTO_MEM_CHECK_ENABLE without ..._ON makes
33
 * no sense whatsoever.
34
 */
35
#ifndef OPENSSL_NO_CRYPTO_MDEBUG
36
static int mh_mode = CRYPTO_MEM_CHECK_OFF;
37
#endif
38
39
#ifndef OPENSSL_NO_CRYPTO_MDEBUG
40
static unsigned long order = 0; /* number of memory requests */
41
42
/*-
43
 * For application-defined information (static C-string `info')
44
 * to be displayed in memory leak list.
45
 * Each thread has its own stack.  For applications, there is
46
 *   OPENSSL_mem_debug_push("...")     to push an entry,
47
 *   OPENSSL_mem_debug_pop()     to pop an entry,
48
 */
49
struct app_mem_info_st {
50
    CRYPTO_THREAD_ID threadid;
51
    const char *file;
52
    int line;
53
    const char *info;
54
    struct app_mem_info_st *next; /* tail of thread's stack */
55
    int references;
56
};
57
58
static CRYPTO_ONCE memdbg_init = CRYPTO_ONCE_STATIC_INIT;
59
CRYPTO_RWLOCK *memdbg_lock;
60
static CRYPTO_RWLOCK *long_memdbg_lock;
61
static CRYPTO_THREAD_LOCAL appinfokey;
62
63
/* memory-block description */
64
struct mem_st {
65
    void *addr;
66
    int num;
67
    const char *file;
68
    int line;
69
    CRYPTO_THREAD_ID threadid;
70
    unsigned long order;
71
    time_t time;
72
    APP_INFO *app_info;
73
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
74
    void *array[30];
75
    size_t array_siz;
76
#endif
77
};
78
79
/*
80
 * hash-table of memory requests (address as * key); access requires
81
 * long_memdbg_lock lock
82
 */
83
static LHASH_OF(MEM) *mh = NULL;
84
85
/* num_disable > 0 iff mh_mode == CRYPTO_MEM_CHECK_ON (w/o ..._ENABLE) */
86
static unsigned int num_disable = 0;
87
88
/*
89
 * Valid iff num_disable > 0.  long_memdbg_lock is locked exactly in this
90
 * case (by the thread named in disabling_thread).
91
 */
92
static CRYPTO_THREAD_ID disabling_threadid;
93
94
DEFINE_RUN_ONCE_STATIC(do_memdbg_init)
95
{
96
    memdbg_lock = CRYPTO_THREAD_lock_new();
97
    long_memdbg_lock = CRYPTO_THREAD_lock_new();
98
    if (memdbg_lock == NULL || long_memdbg_lock == NULL
99
        || !CRYPTO_THREAD_init_local(&appinfokey, NULL)) {
100
        CRYPTO_THREAD_lock_free(memdbg_lock);
101
        memdbg_lock = NULL;
102
        CRYPTO_THREAD_lock_free(long_memdbg_lock);
103
        long_memdbg_lock = NULL;
104
        return 0;
105
    }
106
    return 1;
107
}
108
109
static void app_info_free(APP_INFO *inf)
110
{
111
    if (inf == NULL)
112
        return;
113
    if (--(inf->references) <= 0) {
114
        app_info_free(inf->next);
115
        OPENSSL_free(inf);
116
    }
117
}
118
#endif
119
120
int CRYPTO_mem_ctrl(int mode)
121
0
{
122
0
#ifdef OPENSSL_NO_CRYPTO_MDEBUG
123
0
    return mode - mode;
124
#else
125
    int ret = mh_mode;
126
127
    if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
128
        return -1;
129
130
    CRYPTO_THREAD_write_lock(memdbg_lock);
131
    switch (mode) {
132
    default:
133
        break;
134
135
    case CRYPTO_MEM_CHECK_ON:
136
        mh_mode = CRYPTO_MEM_CHECK_ON | CRYPTO_MEM_CHECK_ENABLE;
137
        num_disable = 0;
138
        break;
139
140
    case CRYPTO_MEM_CHECK_OFF:
141
        mh_mode = 0;
142
        num_disable = 0;
143
        break;
144
145
    /* switch off temporarily (for library-internal use): */
146
    case CRYPTO_MEM_CHECK_DISABLE:
147
        if (mh_mode & CRYPTO_MEM_CHECK_ON) {
148
            CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id();
149
            /* see if we don't have long_memdbg_lock already */
150
            if (!num_disable
151
                || !CRYPTO_THREAD_compare_id(disabling_threadid, cur)) {
152
                /*
153
                 * Long-time lock long_memdbg_lock must not be claimed
154
                 * while we're holding memdbg_lock, or we'll deadlock
155
                 * if somebody else holds long_memdbg_lock (and cannot
156
                 * release it because we block entry to this function). Give
157
                 * them a chance, first, and then claim the locks in
158
                 * appropriate order (long-time lock first).
159
                 */
160
                CRYPTO_THREAD_unlock(memdbg_lock);
161
                /*
162
                 * Note that after we have waited for long_memdbg_lock and
163
                 * memdbg_lock, we'll still be in the right "case" and
164
                 * "if" branch because MemCheck_start and MemCheck_stop may
165
                 * never be used while there are multiple OpenSSL threads.
166
                 */
167
                CRYPTO_THREAD_write_lock(long_memdbg_lock);
168
                CRYPTO_THREAD_write_lock(memdbg_lock);
169
                mh_mode &= ~CRYPTO_MEM_CHECK_ENABLE;
170
                disabling_threadid = cur;
171
            }
172
            num_disable++;
173
        }
174
        break;
175
176
    case CRYPTO_MEM_CHECK_ENABLE:
177
        if (mh_mode & CRYPTO_MEM_CHECK_ON) {
178
            if (num_disable) {  /* always true, or something is going wrong */
179
                num_disable--;
180
                if (num_disable == 0) {
181
                    mh_mode |= CRYPTO_MEM_CHECK_ENABLE;
182
                    CRYPTO_THREAD_unlock(long_memdbg_lock);
183
                }
184
            }
185
        }
186
        break;
187
    }
188
    CRYPTO_THREAD_unlock(memdbg_lock);
189
    return ret;
190
#endif
191
0
}
192
193
#ifndef OPENSSL_NO_CRYPTO_MDEBUG
194
195
static int mem_check_on(void)
196
{
197
    int ret = 0;
198
    CRYPTO_THREAD_ID cur;
199
200
    if (mh_mode & CRYPTO_MEM_CHECK_ON) {
201
        if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
202
            return 0;
203
204
        cur = CRYPTO_THREAD_get_current_id();
205
        CRYPTO_THREAD_read_lock(memdbg_lock);
206
207
        ret = (mh_mode & CRYPTO_MEM_CHECK_ENABLE)
208
            || !CRYPTO_THREAD_compare_id(disabling_threadid, cur);
209
210
        CRYPTO_THREAD_unlock(memdbg_lock);
211
    }
212
    return ret;
213
}
214
215
static int mem_cmp(const MEM *a, const MEM *b)
216
{
217
#ifdef _WIN64
218
    const char *ap = (const char *)a->addr, *bp = (const char *)b->addr;
219
    if (ap == bp)
220
        return 0;
221
    else if (ap > bp)
222
        return 1;
223
    else
224
        return -1;
225
#else
226
    return (const char *)a->addr - (const char *)b->addr;
227
#endif
228
}
229
230
static unsigned long mem_hash(const MEM *a)
231
{
232
    size_t ret;
233
234
    ret = (size_t)a->addr;
235
236
    ret = ret * 17851 + (ret >> 14) * 7 + (ret >> 4) * 251;
237
    return ret;
238
}
239
240
/* returns 1 if there was an info to pop, 0 if the stack was empty. */
241
static int pop_info(void)
242
{
243
    APP_INFO *current = NULL;
244
245
    if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
246
        return 0;
247
248
    current = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
249
    if (current != NULL) {
250
        APP_INFO *next = current->next;
251
252
        if (next != NULL) {
253
            next->references++;
254
            CRYPTO_THREAD_set_local(&appinfokey, next);
255
        } else {
256
            CRYPTO_THREAD_set_local(&appinfokey, NULL);
257
        }
258
        if (--(current->references) <= 0) {
259
            current->next = NULL;
260
            if (next != NULL)
261
                next->references--;
262
            OPENSSL_free(current);
263
        }
264
        return 1;
265
    }
266
    return 0;
267
}
268
269
int CRYPTO_mem_debug_push(const char *info, const char *file, int line)
270
{
271
    APP_INFO *ami, *amim;
272
    int ret = 0;
273
274
    if (mem_check_on()) {
275
        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
276
277
        if (!RUN_ONCE(&memdbg_init, do_memdbg_init)
278
            || (ami = OPENSSL_malloc(sizeof(*ami))) == NULL)
279
            goto err;
280
281
        ami->threadid = CRYPTO_THREAD_get_current_id();
282
        ami->file = file;
283
        ami->line = line;
284
        ami->info = info;
285
        ami->references = 1;
286
        ami->next = NULL;
287
288
        amim = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
289
        CRYPTO_THREAD_set_local(&appinfokey, ami);
290
291
        if (amim != NULL)
292
            ami->next = amim;
293
        ret = 1;
294
 err:
295
        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
296
    }
297
298
    return ret;
299
}
300
301
int CRYPTO_mem_debug_pop(void)
302
{
303
    int ret = 0;
304
305
    if (mem_check_on()) {
306
        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
307
        ret = pop_info();
308
        CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
309
    }
310
    return ret;
311
}
312
313
static unsigned long break_order_num = 0;
314
315
void CRYPTO_mem_debug_malloc(void *addr, size_t num, int before_p,
316
                             const char *file, int line)
317
{
318
    MEM *m, *mm;
319
    APP_INFO *amim;
320
321
    switch (before_p & 127) {
322
    case 0:
323
        break;
324
    case 1:
325
        if (addr == NULL)
326
            break;
327
328
        if (mem_check_on()) {
329
            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
330
331
            if (!RUN_ONCE(&memdbg_init, do_memdbg_init)
332
                || (m = OPENSSL_malloc(sizeof(*m))) == NULL) {
333
                OPENSSL_free(addr);
334
                CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
335
                return;
336
            }
337
            if (mh == NULL) {
338
                if ((mh = lh_MEM_new(mem_hash, mem_cmp)) == NULL) {
339
                    OPENSSL_free(addr);
340
                    OPENSSL_free(m);
341
                    addr = NULL;
342
                    goto err;
343
                }
344
            }
345
346
            m->addr = addr;
347
            m->file = file;
348
            m->line = line;
349
            m->num = num;
350
            m->threadid = CRYPTO_THREAD_get_current_id();
351
352
            if (order == break_order_num) {
353
                /* BREAK HERE */
354
                m->order = order;
355
            }
356
            m->order = order++;
357
# ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
358
            m->array_siz = backtrace(m->array, OSSL_NELEM(m->array));
359
# endif
360
            m->time = time(NULL);
361
362
            amim = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
363
            m->app_info = amim;
364
            if (amim != NULL)
365
                amim->references++;
366
367
            if ((mm = lh_MEM_insert(mh, m)) != NULL) {
368
                /* Not good, but don't sweat it */
369
                if (mm->app_info != NULL) {
370
                    mm->app_info->references--;
371
                }
372
                OPENSSL_free(mm);
373
            }
374
 err:
375
            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
376
        }
377
        break;
378
    }
379
    return;
380
}
381
382
void CRYPTO_mem_debug_free(void *addr, int before_p,
383
        const char *file, int line)
384
{
385
    MEM m, *mp;
386
387
    switch (before_p) {
388
    case 0:
389
        if (addr == NULL)
390
            break;
391
392
        if (mem_check_on() && (mh != NULL)) {
393
            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
394
395
            m.addr = addr;
396
            mp = lh_MEM_delete(mh, &m);
397
            if (mp != NULL) {
398
                app_info_free(mp->app_info);
399
                OPENSSL_free(mp);
400
            }
401
402
            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
403
        }
404
        break;
405
    case 1:
406
        break;
407
    }
408
}
409
410
void CRYPTO_mem_debug_realloc(void *addr1, void *addr2, size_t num,
411
                              int before_p, const char *file, int line)
412
{
413
    MEM m, *mp;
414
415
    switch (before_p) {
416
    case 0:
417
        break;
418
    case 1:
419
        if (addr2 == NULL)
420
            break;
421
422
        if (addr1 == NULL) {
423
            CRYPTO_mem_debug_malloc(addr2, num, 128 | before_p, file, line);
424
            break;
425
        }
426
427
        if (mem_check_on()) {
428
            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
429
430
            m.addr = addr1;
431
            mp = lh_MEM_delete(mh, &m);
432
            if (mp != NULL) {
433
                mp->addr = addr2;
434
                mp->num = num;
435
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
436
                mp->array_siz = backtrace(mp->array, OSSL_NELEM(mp->array));
437
#endif
438
                (void)lh_MEM_insert(mh, mp);
439
            }
440
441
            CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
442
        }
443
        break;
444
    }
445
    return;
446
}
447
448
typedef struct mem_leak_st {
449
    int (*print_cb) (const char *str, size_t len, void *u);
450
    void *print_cb_arg;
451
    int chunks;
452
    long bytes;
453
} MEM_LEAK;
454
455
static void print_leak(const MEM *m, MEM_LEAK *l)
456
{
457
    char buf[1024];
458
    char *bufp = buf;
459
    size_t len = sizeof(buf), ami_cnt;
460
    APP_INFO *amip;
461
    int n;
462
    struct tm *lcl = NULL;
463
    /*
464
     * Convert between CRYPTO_THREAD_ID (which could be anything at all) and
465
     * a long. This may not be meaningful depending on what CRYPTO_THREAD_ID is
466
     * but hopefully should give something sensible on most platforms
467
     */
468
    union {
469
        CRYPTO_THREAD_ID tid;
470
        unsigned long ltid;
471
    } tid;
472
    CRYPTO_THREAD_ID ti;
473
474
    lcl = localtime(&m->time);
475
    n = BIO_snprintf(bufp, len, "[%02d:%02d:%02d] ",
476
                     lcl->tm_hour, lcl->tm_min, lcl->tm_sec);
477
    if (n <= 0) {
478
        bufp[0] = '\0';
479
        return;
480
    }
481
    bufp += n;
482
    len -= n;
483
484
    n = BIO_snprintf(bufp, len, "%5lu file=%s, line=%d, ",
485
                     m->order, m->file, m->line);
486
    if (n <= 0)
487
        return;
488
    bufp += n;
489
    len -= n;
490
491
    tid.ltid = 0;
492
    tid.tid = m->threadid;
493
    n = BIO_snprintf(bufp, len, "thread=%lu, ", tid.ltid);
494
    if (n <= 0)
495
        return;
496
    bufp += n;
497
    len -= n;
498
499
    n = BIO_snprintf(bufp, len, "number=%d, address=%p\n", m->num, m->addr);
500
    if (n <= 0)
501
        return;
502
    bufp += n;
503
    len -= n;
504
505
    l->print_cb(buf, (size_t)(bufp - buf), l->print_cb_arg);
506
507
    l->chunks++;
508
    l->bytes += m->num;
509
510
    amip = m->app_info;
511
    ami_cnt = 0;
512
513
    if (amip) {
514
        ti = amip->threadid;
515
516
        do {
517
            int buf_len;
518
            int info_len;
519
520
            ami_cnt++;
521
            if (ami_cnt >= sizeof(buf) - 1)
522
                break;
523
            memset(buf, '>', ami_cnt);
524
            buf[ami_cnt] = '\0';
525
            tid.ltid = 0;
526
            tid.tid = amip->threadid;
527
            n = BIO_snprintf(buf + ami_cnt, sizeof(buf) - ami_cnt,
528
                             " thread=%lu, file=%s, line=%d, info=\"",
529
                             tid.ltid, amip->file, amip->line);
530
            if (n <= 0)
531
                break;
532
            buf_len = ami_cnt + n;
533
            info_len = strlen(amip->info);
534
            if (128 - buf_len - 3 < info_len) {
535
                memcpy(buf + buf_len, amip->info, 128 - buf_len - 3);
536
                buf_len = 128 - 3;
537
            } else {
538
                n = BIO_snprintf(buf + buf_len, sizeof(buf) - buf_len, "%s",
539
                                 amip->info);
540
                if (n < 0)
541
                    break;
542
                buf_len += n;
543
            }
544
            n = BIO_snprintf(buf + buf_len, sizeof(buf) - buf_len, "\"\n");
545
            if (n <= 0)
546
                break;
547
548
            l->print_cb(buf, buf_len + n, l->print_cb_arg);
549
550
            amip = amip->next;
551
        }
552
        while (amip && CRYPTO_THREAD_compare_id(amip->threadid, ti));
553
    }
554
555
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
556
    {
557
        size_t i;
558
        char **strings = backtrace_symbols(m->array, m->array_siz);
559
560
        for (i = 0; i < m->array_siz; i++)
561
            fprintf(stderr, "##> %s\n", strings[i]);
562
        free(strings);
563
    }
564
#endif
565
}
566
567
IMPLEMENT_LHASH_DOALL_ARG_CONST(MEM, MEM_LEAK);
568
569
int CRYPTO_mem_leaks_cb(int (*cb) (const char *str, size_t len, void *u),
570
                        void *u)
571
{
572
    MEM_LEAK ml;
573
574
    /* Ensure all resources are released */
575
    OPENSSL_cleanup();
576
577
    if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
578
        return -1;
579
580
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
581
582
    ml.print_cb = cb;
583
    ml.print_cb_arg = u;
584
    ml.bytes = 0;
585
    ml.chunks = 0;
586
    if (mh != NULL)
587
        lh_MEM_doall_MEM_LEAK(mh, print_leak, &ml);
588
589
    if (ml.chunks != 0) {
590
        char buf[256];
591
592
        BIO_snprintf(buf, sizeof(buf), "%ld bytes leaked in %d chunks\n",
593
                     ml.bytes, ml.chunks);
594
        cb(buf, strlen(buf), u);
595
    } else {
596
        /*
597
         * Make sure that, if we found no leaks, memory-leak debugging itself
598
         * does not introduce memory leaks (which might irritate external
599
         * debugging tools). (When someone enables leak checking, but does not
600
         * call this function, we declare it to be their fault.)
601
         */
602
        int old_mh_mode;
603
604
        CRYPTO_THREAD_write_lock(memdbg_lock);
605
606
        /*
607
         * avoid deadlock when lh_free() uses CRYPTO_mem_debug_free(), which uses
608
         * mem_check_on
609
         */
610
        old_mh_mode = mh_mode;
611
        mh_mode = CRYPTO_MEM_CHECK_OFF;
612
613
        lh_MEM_free(mh);
614
        mh = NULL;
615
616
        mh_mode = old_mh_mode;
617
        CRYPTO_THREAD_unlock(memdbg_lock);
618
    }
619
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
620
621
    /* Clean up locks etc */
622
    CRYPTO_THREAD_cleanup_local(&appinfokey);
623
    CRYPTO_THREAD_lock_free(memdbg_lock);
624
    CRYPTO_THREAD_lock_free(long_memdbg_lock);
625
    memdbg_lock = NULL;
626
    long_memdbg_lock = NULL;
627
628
    return ml.chunks == 0 ? 1 : 0;
629
}
630
631
static int print_bio(const char *str, size_t len, void *b)
632
{
633
    return BIO_write((BIO *)b, str, len);
634
}
635
636
int CRYPTO_mem_leaks(BIO *b)
637
{
638
    /*
639
     * OPENSSL_cleanup() will free the ex_data locks so we can't have any
640
     * ex_data hanging around
641
     */
642
    bio_free_ex_data(b);
643
644
    return CRYPTO_mem_leaks_cb(print_bio, b);
645
}
646
647
# ifndef OPENSSL_NO_STDIO
648
int CRYPTO_mem_leaks_fp(FILE *fp)
649
{
650
    BIO *b;
651
    int ret;
652
653
    /*
654
     * Need to turn off memory checking when allocated BIOs ... especially as
655
     * we're creating them at a time when we're trying to check we've not
656
     * left anything un-free()'d!!
657
     */
658
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
659
    b = BIO_new(BIO_s_file());
660
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
661
    if (b == NULL)
662
        return -1;
663
    BIO_set_fp(b, fp, BIO_NOCLOSE);
664
    ret = CRYPTO_mem_leaks_cb(print_bio, b);
665
    BIO_free(b);
666
    return ret;
667
}
668
# endif
669
670
#endif