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