xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/base/dll.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /*	$NetBSD: dll.c,v 1.3 2023/06/19 21:41:42 christos Exp $	*/
2 
3 /***********************************************************************
4  * Copyright (c) 2016 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  **********************************************************************/
33 
34 /*
35  * This is an implementation of thread-specific storage with
36  * destructors.  WIN32 doesn't quite have this.  Instead it has
37  * DllMain(), an entry point in every DLL that gets called to notify the
38  * DLL of thread/process "attach"/"detach" events.
39  *
40  * We use __thread (or __declspec(thread)) for the thread-local itself
41  * and DllMain() DLL_THREAD_DETACH events to drive destruction of
42  * thread-local values.
43  *
44  * When building in maintainer mode on non-Windows pthread systems this
45  * uses a single pthread key instead to implement multiple keys.  This
46  * keeps the code from rotting when modified by non-Windows developers.
47  */
48 
49 #include "baselocl.h"
50 
51 #ifdef WIN32
52 #include <windows.h>
53 #endif
54 
55 #ifdef HEIM_WIN32_TLS
56 #include <assert.h>
57 #include <err.h>
58 #include <heim_threads.h>
59 
60 #ifndef WIN32
61 #include <pthread.h>
62 #endif
63 
64 /* Logical array of keys that grows lock-lessly */
65 typedef struct tls_keys tls_keys;
66 struct tls_keys {
67     void (**keys_dtors)(void *);    /* array of destructors         */
68     size_t keys_start_idx;          /* index of first destructor    */
69     size_t keys_num;
70     tls_keys *keys_next;
71 };
72 
73 /*
74  * Well, not quite locklessly.  We need synchronization primitives to do
75  * this locklessly.  An atomic CAS will do.
76  */
77 static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
78 static tls_keys *tls_key_defs;
79 
80 /* Logical array of values (per-thread; no locking needed here) */
81 struct tls_values {
82     void **values; /* realloc()ed */
83     size_t values_num;
84 };
85 
86 static HEIMDAL_THREAD_LOCAL struct tls_values values;
87 
88 static char dead_key[1];
no_dtor(void * d)89 static void no_dtor(void *d) { (void)d; }
90 
91 void
heim_w32_service_thread_detach(void * unused)92 heim_w32_service_thread_detach(void *unused)
93 {
94     tls_keys *key_defs;
95     void (*dtor)(void*);
96     size_t i;
97 
98     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
99     key_defs = tls_key_defs;
100     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
101 
102     if (key_defs == NULL)
103         return;
104 
105     for (i = 0; i < values.values_num; i++) {
106         assert(i >= key_defs->keys_start_idx);
107         if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
108             HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
109             key_defs = key_defs->keys_next;
110             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
111 
112             assert(key_defs != NULL);
113             assert(i >= key_defs->keys_start_idx);
114             assert(i < key_defs->keys_start_idx + key_defs->keys_num);
115         }
116         dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
117         if (values.values[i] != NULL && dtor != NULL && dtor != no_dtor)
118             dtor(values.values[i]);
119         values.values[i] = NULL;
120     }
121 }
122 
123 #if !defined(WIN32)
124 static pthread_key_t pt_key;
125 pthread_once_t pt_once = PTHREAD_ONCE_INIT;
126 
127 static void
atexit_del_tls_for_thread(void)128 atexit_del_tls_for_thread(void)
129 {
130     heim_w32_service_thread_detach(NULL);
131 }
132 
133 static void
create_pt_key(void)134 create_pt_key(void)
135 {
136     int ret;
137 
138     /* The main thread may not execute TLS destructors */
139     atexit(atexit_del_tls_for_thread);
140     ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
141     if (ret != 0)
142         err(1, "pthread_key_create() failed");
143 }
144 
145 #endif
146 
147 int
heim_w32_key_create(HEIM_PRIV_thread_key * key,void (* dtor)(void *))148 heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
149 {
150     tls_keys *key_defs, *new_key_defs;
151     size_t i, k;
152     int ret = ENOMEM;
153 
154 #if !defined(WIN32)
155     (void) pthread_once(&pt_once, create_pt_key);
156     (void) pthread_setspecific(pt_key, dead_key);
157 #endif
158 
159     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
160     if (tls_key_defs == NULL) {
161         /* First key */
162         new_key_defs = calloc(1, sizeof(*new_key_defs));
163         if (new_key_defs == NULL) {
164             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
165             return ENOMEM;
166         }
167         new_key_defs->keys_num = 8;
168         new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
169                                           sizeof(*new_key_defs->keys_dtors));
170         if (new_key_defs->keys_dtors == NULL) {
171             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
172             free(new_key_defs);
173             return ENOMEM;
174         }
175         tls_key_defs = new_key_defs;
176         new_key_defs->keys_dtors[0] = dtor;
177         for (i = 1; i < new_key_defs->keys_num; i++)
178             new_key_defs->keys_dtors[i] = NULL;
179         HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
180         return 0;
181     }
182 
183     for (key_defs = tls_key_defs;
184          key_defs != NULL;
185          key_defs = key_defs->keys_next) {
186         k = key_defs->keys_start_idx;
187         for (i = 0; i < key_defs->keys_num; i++, k++) {
188             if (key_defs->keys_dtors[i] == NULL) {
189                 /* Found free slot; use it */
190                 key_defs->keys_dtors[i] = dtor;
191                 *key = k;
192                 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
193                 return 0;
194             }
195         }
196         if (key_defs->keys_next != NULL)
197             continue;
198 
199         /* Grow the registration array */
200         /* XXX DRY */
201         new_key_defs = calloc(1, sizeof(*new_key_defs));
202         if (new_key_defs == NULL)
203             break;
204 
205         new_key_defs->keys_dtors =
206             calloc(key_defs->keys_num + key_defs->keys_num / 2,
207                    sizeof(*new_key_defs->keys_dtors));
208         if (new_key_defs->keys_dtors == NULL) {
209             free(new_key_defs);
210             break;
211         }
212         new_key_defs->keys_start_idx = key_defs->keys_start_idx +
213             key_defs->keys_num;
214         new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
215         new_key_defs->keys_dtors[i] = dtor;
216         for (i = 1; i < new_key_defs->keys_num; i++)
217             new_key_defs->keys_dtors[i] = NULL;
218         key_defs->keys_next = new_key_defs;
219         ret = 0;
220         break;
221     }
222     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
223     return ret;
224 }
225 
226 static void
key_lookup(HEIM_PRIV_thread_key key,tls_keys ** kd,size_t * dtor_idx,void (** dtor)(void *))227 key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
228            size_t *dtor_idx, void (**dtor)(void *))
229 {
230     tls_keys *key_defs;
231 
232     if (kd != NULL)
233         *kd = NULL;
234     if (dtor_idx != NULL)
235         *dtor_idx = 0;
236     if (dtor != NULL)
237         *dtor = NULL;
238 
239     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
240     key_defs = tls_key_defs;
241     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
242 
243     while (key_defs != NULL) {
244         if (key >= key_defs->keys_start_idx &&
245             key < key_defs->keys_start_idx + key_defs->keys_num) {
246             if (kd != NULL)
247                 *kd = key_defs;
248             if (dtor_idx != NULL)
249                 *dtor_idx = key - key_defs->keys_start_idx;
250             if (dtor != NULL)
251                 *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
252             return;
253         }
254 
255         HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
256         key_defs = key_defs->keys_next;
257         HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
258         assert(key_defs != NULL);
259         assert(key >= key_defs->keys_start_idx);
260     }
261 }
262 
263 int
heim_w32_delete_key(HEIM_PRIV_thread_key key)264 heim_w32_delete_key(HEIM_PRIV_thread_key key)
265 {
266     tls_keys *key_defs;
267     size_t dtor_idx;
268 
269     key_lookup(key, &key_defs, &dtor_idx, NULL);
270     if (key_defs == NULL)
271         return EINVAL;
272     key_defs->keys_dtors[dtor_idx] = no_dtor;
273     return 0;
274 }
275 
276 int
heim_w32_setspecific(HEIM_PRIV_thread_key key,void * value)277 heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
278 {
279     void **new_values;
280     size_t new_num;
281     void (*dtor)(void *);
282     size_t i;
283 
284 #if !defined(WIN32)
285     (void) pthread_setspecific(pt_key, dead_key);
286 #endif
287 
288     key_lookup(key, NULL, NULL, &dtor);
289     if (dtor == NULL)
290         return EINVAL;
291 
292     if (key >= values.values_num) {
293         if (values.values_num == 0) {
294             values.values = NULL;
295             new_num = 8;
296         } else {
297             new_num = (values.values_num + values.values_num / 2);
298         }
299         new_values = realloc(values.values, sizeof(void *) * new_num);
300         if (new_values == NULL)
301             return ENOMEM;
302         for (i = values.values_num; i < new_num; i++)
303             new_values[i] = NULL;
304         values.values = new_values;
305         values.values_num = new_num;
306     }
307 
308     assert(key < values.values_num);
309 
310     if (values.values[key] != NULL && dtor != NULL && dtor != no_dtor)
311         dtor(values.values[key]);
312 
313     values.values[key] = value;
314     return 0;
315 }
316 
317 void *
heim_w32_getspecific(HEIM_PRIV_thread_key key)318 heim_w32_getspecific(HEIM_PRIV_thread_key key)
319 {
320     if (key >= values.values_num)
321         return NULL;
322     return values.values[key];
323 }
324 
325 #else
326 static char dummy;
327 #endif /* HEIM_WIN32_TLS */
328