Coverage Report

Created: 2025-11-11 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/htslib/htscodecs/htscodecs/utils.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2022 Genome Research Ltd.
3
 * Author(s): James Bonfield
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 *    1. Redistributions of source code must retain the above copyright notice,
9
 *       this list of conditions and the following disclaimer.
10
 *
11
 *    2. Redistributions in binary form must reproduce the above
12
 *       copyright notice, this list of conditions and the following
13
 *       disclaimer in the documentation and/or other materials provided
14
 *       with the distribution.
15
 *
16
 *    3. Neither the names Genome Research Ltd and Wellcome Trust Sanger
17
 *       Institute nor the names of its contributors may be used to endorse
18
 *       or promote products derived from this software without specific
19
 *       prior written permission.
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY GENOME RESEARCH LTD AND CONTRIBUTORS "AS
22
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENOME RESEARCH
25
 * LTD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
#include "config.h"
35
36
#include <stdlib.h>
37
#include <stdio.h>
38
#include <string.h>
39
#include <inttypes.h>
40
41
#include "utils.h"
42
43
#ifndef NO_THREADS
44
#include <pthread.h>
45
#endif
46
47
//#define TLS_DEBUG
48
49
#ifndef NO_THREADS
50
/*
51
 * Thread local storage per thread in the pool.
52
 *
53
 * We have some large memory blocks for rANS which we cannot store on the
54
 * stack due to various system limitations.  Allocaitng them can be
55
 * expensive as some OSes use mmap and will pass the pages back to the OS
56
 * on each free.  This unfortunately then means zeroing the pages out again
57
 * on each new malloc, plus additional switching into the kernel.
58
 *
59
 * Instead where available, we use pthread_once to allocate a small arena
60
 * of memory buffers and we continually reuse these same buffers.  We don't
61
 * need to memset it (calloc equivalent) either as we're sure that any
62
 * leakage of data is simply an earlier set of precomputed frequency
63
 * lookups, and not something more sinister such as an encryption key.
64
 *
65
 * If we don't have pthreads, then we have to fall back to the slow
66
 * traditional calloc instead.
67
 */
68
69
829k
#define MAX_TLS_BUFS 10
70
typedef struct {
71
    void   *bufs[MAX_TLS_BUFS];
72
    size_t sizes[MAX_TLS_BUFS];
73
    int     used[MAX_TLS_BUFS];
74
} tls_pool;
75
76
static pthread_once_t rans_once = PTHREAD_ONCE_INIT;
77
static pthread_key_t rans_key;
78
79
/*
80
 * Frees all local storage for this thread.
81
 * Note: this isn't a function to free a specific allocated item.
82
 */
83
0
static void htscodecs_tls_free_all(void *ptr) {
84
0
    tls_pool *tls = (tls_pool *)ptr;
85
0
    if (!tls)
86
0
        return;
87
88
0
    int i;
89
0
    for (i = 0; i < MAX_TLS_BUFS; i++) {
90
#ifdef TLS_DEBUG
91
        if (tls->bufs[i])
92
            fprintf(stderr, "Free %ld = %p\n", tls->sizes[i], tls->bufs[i]);
93
#endif
94
0
        if (tls->used[i]) {
95
0
            fprintf(stderr, "Closing thread while TLS data is in use\n");
96
0
        }
97
0
        free(tls->bufs[i]);
98
0
    }
99
100
0
    free(tls);
101
0
}
102
103
1
static void htscodecs_tls_init(void) {
104
1
    pthread_key_create(&rans_key, htscodecs_tls_free_all);
105
1
}
106
107
/*
108
 * Allocates size bytes from the global Thread Local Storage pool.
109
 * This is shared by all subsequent calls within this thread.
110
 *
111
 * An simpler alternative could be possible where we have a fixed number
112
 * of types of alloc, say 5, and specify the correct label when allocating.
113
 * Eg histogram, name_context, fqzcomp, rans.  We can have multiple types
114
 * in use in different stack frames (such name_context + hist + rans), but
115
 * the number is very limited.  That then paves the way to simply check and
116
 * realloc without needing to keep track of use status or overflowing
117
 * the maximum number permitted.
118
 */
119
203k
void *htscodecs_tls_alloc(size_t size) {
120
203k
    int i;
121
122
203k
    int err = pthread_once(&rans_once, htscodecs_tls_init);
123
203k
    if (err != 0) {
124
0
        fprintf(stderr, "Initialising TLS data failed: pthread_once: %s\n",
125
0
                strerror(err));
126
0
        return NULL;
127
0
    }
128
129
    // Initialise tls_pool on first usage
130
203k
    tls_pool *tls = pthread_getspecific(rans_key);
131
203k
    if (!tls) {
132
1
        if (!(tls = calloc(1, sizeof(*tls))))
133
0
            return NULL;
134
1
        pthread_setspecific(rans_key, tls);
135
1
    }
136
137
    // Query pool for size
138
203k
    int avail = -1;
139
313k
    for (i = 0; i < MAX_TLS_BUFS; i++) {
140
313k
        if (!tls->used[i]) {
141
205k
            if (size <= tls->sizes[i]) {
142
203k
                tls->used[i] = 1;
143
#ifdef TLS_DEBUG
144
                fprintf(stderr, "Reuse %d: %ld/%ld = %p\n",
145
                        i, size, tls->sizes[i], tls->bufs[i]);
146
#endif
147
203k
                return tls->bufs[i];
148
203k
            } else if (avail == -1) {
149
1.74k
                avail = i;
150
1.74k
            }
151
205k
        }
152
313k
    }
153
154
13
    if (i == MAX_TLS_BUFS && avail == -1) {
155
        // Shouldn't happen given our very limited use of this function
156
0
        fprintf(stderr, "Error: out of rans_tls_alloc slots\n");
157
0
        return NULL;
158
0
    }
159
160
13
    if (tls->bufs[avail])
161
10
        free(tls->bufs[avail]);
162
13
    if (!(tls->bufs[avail] = calloc(1, size)))
163
0
        return NULL;
164
#ifdef TLS_DEBUG
165
    fprintf(stderr, "Alloc %d: %ld = %p\n", avail, size, tls->bufs[avail]);
166
#endif
167
13
    tls->sizes[avail] = size;
168
13
    tls->used[avail] = 1;
169
170
13
    return tls->bufs[avail];
171
13
}
172
173
84.4k
void *htscodecs_tls_calloc(size_t nmemb, size_t size) {
174
#ifdef TLS_DEBUG
175
    fprintf(stderr, "htscodecs_tls_calloc(%ld)\n", nmemb*size);
176
#endif
177
84.4k
    void *ptr = htscodecs_tls_alloc(nmemb * size);
178
84.4k
    if (ptr)
179
84.4k
        memset(ptr, 0, nmemb * size);
180
84.4k
    return ptr;
181
84.4k
}
182
183
203k
void htscodecs_tls_free(void *ptr) {
184
203k
    if (!ptr)
185
0
        return;
186
187
203k
    tls_pool *tls = pthread_getspecific(rans_key);
188
189
203k
    int i;
190
313k
    for (i = 0; i < MAX_TLS_BUFS; i++) {
191
313k
        if (tls->bufs[i] == ptr)
192
203k
            break;
193
313k
    }
194
#ifdef TLS_DEBUG
195
    fprintf(stderr, "Fake free %d size %ld ptr %p\n",
196
            i, tls->sizes[i], tls->bufs[i]);
197
#endif
198
203k
    if (i == MAX_TLS_BUFS) {
199
0
        fprintf(stderr, "Attempt to htscodecs_tls_free a buffer not allocated"
200
0
                " with htscodecs_tls_alloc\n");
201
0
        return;
202
0
    }
203
203k
    if (!tls->used[i]) {
204
0
        fprintf(stderr, "Attempt to htscodecs_tls_free a buffer twice\n");
205
0
        return;
206
0
    }
207
203k
    tls->used[i] = 0;
208
203k
}
209
210
#else
211
/*
212
 * Calloc/free equivalents instead.
213
 *
214
 * We use calloc instead of malloc as a sufficiently malformed set of input
215
 * frequencies may not sum to the expected total frequency size, leaving
216
 * some elements uninitialised.  It's unlikely, but potentially a crafty
217
 * attacker could somehow exploit this to pull out parts of this allocated
218
 * buffer and leak them into the decompressed data stream, potentially
219
 * compromising previous buffers such as encryption keys.  (Although
220
 * frankly any well-written crypto library should be zeroing such memory
221
 * before freeing it to ensure it's never visible to a subsequent malloc.)
222
 */
223
void *htscodecs_tls_alloc(size_t size) {
224
    return calloc(1, size);
225
}
226
227
void *htscodecs_tls_calloc(size_t nmemb, size_t size) {
228
    return calloc(nmemb, size);
229
}
230
231
void htscodecs_tls_free(void *ptr) {
232
    free(ptr);
233
}
234
#endif