/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 |