1/*	$NetBSD: t_tls_extern.c,v 1.16 2024/07/23 18:11:53 riastradh Exp $	*/
2
3/*-
4 * Copyright (c) 2023 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/types.h>
30
31#include <atf-c.h>
32#include <dlfcn.h>
33
34#define	ATF_REQUIRE_DL(x) ATF_REQUIRE_MSG((x) != 0, "%s: %s", #x, dlerror())
35
36enum order {
37	DEF_USE_EAGER,
38	DEF_USE_LAZY,
39	USE_DEF,
40	USE_DEF_NOLOAD,
41};
42
43static void
44tls_extern(const char *libdef, const char *libuse, enum order order)
45{
46	void *def, *use;
47	int *(*fdef)(void), *(*fuse)(void);
48	int *pdef, *puse;
49
50	(void)dlerror();
51
52	switch (order) {
53	case DEF_USE_EAGER:
54		ATF_REQUIRE_DL(def = dlopen(libdef, 0));
55		ATF_REQUIRE_DL(fdef = dlsym(def, "fdef"));
56		pdef = (*fdef)();
57		ATF_REQUIRE_DL(use = dlopen(libuse, 0));
58		ATF_REQUIRE_DL(fuse = dlsym(use, "fuse"));
59		puse = (*fuse)();
60		break;
61	case DEF_USE_LAZY:
62		ATF_REQUIRE_DL(def = dlopen(libdef, 0));
63		ATF_REQUIRE_DL(use = dlopen(libuse, 0));
64		goto lazy;
65	case USE_DEF:
66		ATF_REQUIRE_DL(use = dlopen(libuse, 0));
67		ATF_REQUIRE_DL(def = dlopen(libdef, 0));
68		goto lazy;
69	case USE_DEF_NOLOAD:
70		ATF_REQUIRE_DL(use = dlopen(libuse, 0));
71		ATF_REQUIRE_DL(def = dlopen(libdef, RTLD_NOLOAD));
72lazy:		ATF_REQUIRE_DL(fdef = dlsym(def, "fdef"));
73		ATF_REQUIRE_DL(fuse = dlsym(use, "fuse"));
74		pdef = (*fdef)();
75		puse = (*fuse)();
76		break;
77	}
78
79	ATF_CHECK_EQ_MSG(pdef, puse,
80	    "%p in defining library != %p in using library",
81	    pdef, puse);
82}
83
84ATF_TC(dynamic_abusedef);
85ATF_TC_HEAD(dynamic_abusedef, tc)
86{
87	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
88	    " loading static use than dynamic def");
89}
90ATF_TC_BODY(dynamic_abusedef, tc)
91{
92	tls_extern("libh_def_dynamic.so", "libh_abuse_dynamic.so", USE_DEF);
93}
94
95ATF_TC(dynamic_abusedefnoload);
96ATF_TC_HEAD(dynamic_abusedefnoload, tc)
97{
98	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
99	    " loading static use then dynamic def with RTLD_NOLOAD");
100}
101ATF_TC_BODY(dynamic_abusedefnoload, tc)
102{
103	tls_extern("libh_def_dynamic.so", "libh_abuse_dynamic.so",
104	    USE_DEF_NOLOAD);
105}
106
107ATF_TC(dynamic_defabuse_eager);
108ATF_TC_HEAD(dynamic_defabuse_eager, tc)
109{
110	atf_tc_set_md_var(tc, "descr", "dlopen refuses extern __thread for TLS,"
111	    " loading dynamic def then static use eagerly");
112}
113ATF_TC_BODY(dynamic_defabuse_eager, tc)
114{
115	void *def;
116	int *(*fdef)(void);
117
118	ATF_REQUIRE_DL(def = dlopen("libh_def_dynamic.so", 0));
119	ATF_REQUIRE_DL(fdef = dlsym(def, "fdef"));
120	(void)(*fdef)();
121	ATF_CHECK_EQ_MSG(NULL, dlopen("libh_abuse_dynamic.so", 0),
122	    "dlopen failed to detect static-then-dynamic abuse");
123}
124
125ATF_TC(dynamic_defabuse_lazy);
126ATF_TC_HEAD(dynamic_defabuse_lazy, tc)
127{
128	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
129	    " loading dynamic def then static use lazily");
130}
131ATF_TC_BODY(dynamic_defabuse_lazy, tc)
132{
133	tls_extern("libh_def_dynamic.so", "libh_abuse_dynamic.so",
134	    DEF_USE_LAZY);
135}
136
137ATF_TC(dynamic_defuse_eager);
138ATF_TC_HEAD(dynamic_defuse_eager, tc)
139{
140	atf_tc_set_md_var(tc, "descr", "extern __thread for dynamic TLS works,"
141	    " loading def then use eagerly");
142}
143ATF_TC_BODY(dynamic_defuse_eager, tc)
144{
145	tls_extern("libh_def_dynamic.so", "libh_use_dynamic.so",
146	    DEF_USE_EAGER);
147}
148
149ATF_TC(dynamic_defuse_lazy);
150ATF_TC_HEAD(dynamic_defuse_lazy, tc)
151{
152	atf_tc_set_md_var(tc, "descr", "extern __thread for dynamic TLS works,"
153	    " loading def then use lazyly");
154}
155ATF_TC_BODY(dynamic_defuse_lazy, tc)
156{
157	tls_extern("libh_def_dynamic.so", "libh_use_dynamic.so",
158	    DEF_USE_LAZY);
159}
160
161ATF_TC(dynamic_usedef);
162ATF_TC_HEAD(dynamic_usedef, tc)
163{
164	atf_tc_set_md_var(tc, "descr", "extern __thread for dynamic TLS works,"
165	    " loading use then def");
166}
167ATF_TC_BODY(dynamic_usedef, tc)
168{
169	tls_extern("libh_def_dynamic.so", "libh_use_dynamic.so",
170	    USE_DEF);
171}
172
173ATF_TC(dynamic_usedefnoload);
174ATF_TC_HEAD(dynamic_usedefnoload, tc)
175{
176	atf_tc_set_md_var(tc, "descr", "extern __thread for dynamic TLS works,"
177	    " loading use then def with RTLD_NOLOAD");
178}
179ATF_TC_BODY(dynamic_usedefnoload, tc)
180{
181	tls_extern("libh_def_dynamic.so", "libh_use_dynamic.so",
182	    USE_DEF_NOLOAD);
183}
184
185ATF_TC(static_abusedef);
186ATF_TC_HEAD(static_abusedef, tc)
187{
188	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
189	    " loading dynamic use then static def");
190}
191ATF_TC_BODY(static_abusedef, tc)
192{
193	tls_extern("libh_def_static.so", "libh_abuse_static.so", USE_DEF);
194}
195
196ATF_TC(static_abusedefnoload);
197ATF_TC_HEAD(static_abusedefnoload, tc)
198{
199	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
200	    " loading dynamic use then static def with RTLD_NOLOAD");
201}
202ATF_TC_BODY(static_abusedefnoload, tc)
203{
204	tls_extern("libh_def_static.so", "libh_abuse_static.so",
205	    USE_DEF_NOLOAD);
206}
207
208ATF_TC(static_defabuse_eager);
209ATF_TC_HEAD(static_defabuse_eager, tc)
210{
211	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
212	    " loading static def then dynamic use eagerly");
213}
214ATF_TC_BODY(static_defabuse_eager, tc)
215{
216	tls_extern("libh_def_static.so", "libh_abuse_static.so",
217	    DEF_USE_EAGER);
218}
219
220ATF_TC(static_defabuse_lazy);
221ATF_TC_HEAD(static_defabuse_lazy, tc)
222{
223	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
224	    " loading static def then dynamic use lazyly");
225}
226ATF_TC_BODY(static_defabuse_lazy, tc)
227{
228	tls_extern("libh_def_static.so", "libh_abuse_static.so",
229	    DEF_USE_LAZY);
230}
231
232ATF_TC(static_defuse_eager);
233ATF_TC_HEAD(static_defuse_eager, tc)
234{
235	atf_tc_set_md_var(tc, "descr", "extern __thread for static TLS works,"
236	    " loading def then use eagerly");
237}
238ATF_TC_BODY(static_defuse_eager, tc)
239{
240	tls_extern("libh_def_static.so", "libh_use_static.so",
241	    DEF_USE_EAGER);
242}
243
244ATF_TC(static_defuse_lazy);
245ATF_TC_HEAD(static_defuse_lazy, tc)
246{
247	atf_tc_set_md_var(tc, "descr", "extern __thread for static TLS works,"
248	    " loading def then use lazyly");
249}
250ATF_TC_BODY(static_defuse_lazy, tc)
251{
252	tls_extern("libh_def_static.so", "libh_use_static.so",
253	    DEF_USE_LAZY);
254}
255
256ATF_TC(static_usedef);
257ATF_TC_HEAD(static_usedef, tc)
258{
259	atf_tc_set_md_var(tc, "descr", "extern __thread for static TLS works,"
260	    " loading use then def");
261}
262ATF_TC_BODY(static_usedef, tc)
263{
264	tls_extern("libh_def_static.so", "libh_use_static.so",
265	    USE_DEF);
266}
267
268ATF_TC(static_usedefnoload);
269ATF_TC_HEAD(static_usedefnoload, tc)
270{
271	atf_tc_set_md_var(tc, "descr", "extern __thread for static TLS works,"
272	    " loading use then def with RTLD_NOLOAD");
273}
274ATF_TC_BODY(static_usedefnoload, tc)
275{
276	tls_extern("libh_def_static.so", "libh_use_static.so",
277	    USE_DEF_NOLOAD);
278}
279
280ATF_TC(onlydef_dynamic_static_ctor);
281ATF_TC_HEAD(onlydef_dynamic_static_ctor, tc)
282{
283	atf_tc_set_md_var(tc, "descr", "definition-only library,"
284	    " dynamic load and use in ctor, then static load fails");
285}
286ATF_TC_BODY(onlydef_dynamic_static_ctor, tc)
287{
288
289	ATF_REQUIRE_DL(dlopen("libh_onlydef.so", 0));
290	ATF_REQUIRE_DL(dlopen("libh_onlyctor_dynamic.so", 0));
291	ATF_CHECK_EQ_MSG(NULL, dlopen("libh_onlyuse_static.so", 0),
292	    "dlopen failed to detect dynamic-then-static abuse");
293}
294
295ATF_TC(onlydef_dynamic_static_eager);
296ATF_TC_HEAD(onlydef_dynamic_static_eager, tc)
297{
298	atf_tc_set_md_var(tc, "descr", "definition-only library,"
299	    " dynamic load and use, then static load fails");
300}
301ATF_TC_BODY(onlydef_dynamic_static_eager, tc)
302{
303	void *use_dynamic;
304	int *(*fdynamic)(void);
305
306	ATF_REQUIRE_DL(use_dynamic = dlopen("libh_onlyuse_dynamic.so", 0));
307	ATF_REQUIRE_DL(fdynamic = dlsym(use_dynamic, "fdynamic"));
308	(void)(*fdynamic)();
309	ATF_CHECK_EQ_MSG(NULL, dlopen("libh_onlyuse_static.so", 0),
310	    "dlopen failed to detect dynamic-then-static abuse");
311}
312
313ATF_TC(onlydef_dynamic_static_lazy);
314ATF_TC_HEAD(onlydef_dynamic_static_lazy, tc)
315{
316	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
317	    " with definition-only library, dynamic and static load and use");
318}
319ATF_TC_BODY(onlydef_dynamic_static_lazy, tc)
320{
321	void *use_dynamic, *use_static;
322	int *(*fdynamic)(void), *(*fstatic)(void);
323	int *pdynamic, *pstatic;
324
325	ATF_REQUIRE_DL(use_dynamic = dlopen("libh_onlyuse_dynamic.so", 0));
326	ATF_REQUIRE_DL(use_static = dlopen("libh_onlyuse_static.so", 0));
327	ATF_REQUIRE_DL(fdynamic = dlsym(use_dynamic, "fdynamic"));
328	ATF_REQUIRE_DL(fstatic = dlsym(use_static, "fstatic"));
329	pdynamic = (*fdynamic)();
330	pstatic = (*fstatic)();
331	ATF_CHECK_EQ_MSG(pdynamic, pstatic,
332	    "%p in dynamic tls user != %p in static tls user",
333	    pdynamic, pstatic);
334}
335
336ATF_TC(onlydef_static_dynamic_eager);
337ATF_TC_HEAD(onlydef_static_dynamic_eager, tc)
338{
339	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
340	    " with definition-only library,"
341	    " static load and use, then dynamic load and use");
342}
343ATF_TC_BODY(onlydef_static_dynamic_eager, tc)
344{
345	void *use_static, *use_dynamic;
346	int *(*fstatic)(void), *(*fdynamic)(void);
347	int *pstatic, *pdynamic;
348
349	ATF_REQUIRE_DL(dlopen("libh_onlydef.so", 0));
350	ATF_REQUIRE_DL(use_static = dlopen("libh_onlyuse_static.so", 0));
351	ATF_REQUIRE_DL(fstatic = dlsym(use_static, "fstatic"));
352	pstatic = (*fstatic)();
353	ATF_REQUIRE_DL(use_dynamic = dlopen("libh_onlyuse_dynamic.so", 0));
354	ATF_REQUIRE_DL(fdynamic = dlsym(use_dynamic, "fdynamic"));
355	pdynamic = (*fdynamic)();
356	ATF_CHECK_EQ_MSG(pstatic, pdynamic,
357	    "%p in static tls user != %p in dynamic tls user",
358	    pstatic, pdynamic);
359}
360
361ATF_TC(onlydef_static_dynamic_lazy);
362ATF_TC_HEAD(onlydef_static_dynamic_lazy, tc)
363{
364	atf_tc_set_md_var(tc, "descr", "extern __thread for TLS works,"
365	    " with definition-only library, static and dynamic load and use");
366}
367ATF_TC_BODY(onlydef_static_dynamic_lazy, tc)
368{
369	void *use_static, *use_dynamic;
370	int *(*fstatic)(void), *(*fdynamic)(void);
371	int *pstatic, *pdynamic;
372
373	ATF_REQUIRE_DL(dlopen("libh_onlydef.so", 0));
374	ATF_REQUIRE_DL(use_static = dlopen("libh_onlyuse_static.so", 0));
375	ATF_REQUIRE_DL(use_dynamic = dlopen("libh_onlyuse_dynamic.so", 0));
376	ATF_REQUIRE_DL(fstatic = dlsym(use_static, "fstatic"));
377	ATF_REQUIRE_DL(fdynamic = dlsym(use_dynamic, "fdynamic"));
378	pstatic = (*fstatic)();
379	pdynamic = (*fdynamic)();
380	ATF_CHECK_EQ_MSG(pstatic, pdynamic,
381	    "%p in static tls user != %p in dynamic tls user",
382	    pstatic, pdynamic);
383}
384
385ATF_TC(opencloseloop_use);
386ATF_TC_HEAD(opencloseloop_use, tc)
387{
388	atf_tc_set_md_var(tc, "descr", "Testing opening and closing in a loop,"
389	    " then opening and using dynamic TLS");
390}
391ATF_TC_BODY(opencloseloop_use, tc)
392{
393	unsigned i;
394	void *def, *use;
395	int *(*fdef)(void), *(*fuse)(void);
396	int *pdef, *puse;
397
398	/*
399	 * Open and close the definition library repeatedly.  This
400	 * should trigger allocation of many DTV offsets, which are
401	 * (currently) not recycled, so the required DTV offsets should
402	 * become very long -- pages past what is actually allocated
403	 * before we attempt to use it.
404	 *
405	 * This way, we will exercise the wrong-way-conditional fast
406	 * path of PR lib/58154.
407	 */
408	for (i = sysconf(_SC_PAGESIZE); i --> 0;) {
409		ATF_REQUIRE_DL(def = dlopen("libh_def_dynamic.so", 0));
410		ATF_REQUIRE_EQ_MSG(dlclose(def), 0,
411		    "dlclose(def): %s", dlerror());
412	}
413
414	/*
415	 * Now open the definition library and keep it open.
416	 */
417	ATF_REQUIRE_DL(def = dlopen("libh_def_dynamic.so", 0));
418	ATF_REQUIRE_DL(fdef = dlsym(def, "fdef"));
419
420	/*
421	 * Open libraries that use the definition and verify they
422	 * observe the same pointer.
423	 */
424	ATF_REQUIRE_DL(use = dlopen("libh_use_dynamic.so", 0));
425	ATF_REQUIRE_DL(fuse = dlsym(use, "fuse"));
426	pdef = (*fdef)();
427	puse = (*fuse)();
428	ATF_CHECK_EQ_MSG(pdef, puse,
429	    "%p in defining library != %p in using library",
430	    pdef, puse);
431
432	/*
433	 * Also verify the pointer can be used.
434	 */
435	*pdef = 123;
436	*puse = 456;
437	ATF_CHECK_EQ_MSG(*pdef, *puse,
438	    "%d in defining library != %d in using library",
439	    *pdef, *puse);
440}
441
442ATF_TP_ADD_TCS(tp)
443{
444
445	ATF_TP_ADD_TC(tp, dynamic_abusedef);
446	ATF_TP_ADD_TC(tp, dynamic_abusedefnoload);
447	ATF_TP_ADD_TC(tp, dynamic_defabuse_eager);
448	ATF_TP_ADD_TC(tp, dynamic_defabuse_lazy);
449	ATF_TP_ADD_TC(tp, dynamic_defuse_eager);
450	ATF_TP_ADD_TC(tp, dynamic_defuse_lazy);
451	ATF_TP_ADD_TC(tp, dynamic_usedef);
452	ATF_TP_ADD_TC(tp, dynamic_usedefnoload);
453	ATF_TP_ADD_TC(tp, onlydef_dynamic_static_ctor);
454	ATF_TP_ADD_TC(tp, onlydef_dynamic_static_eager);
455	ATF_TP_ADD_TC(tp, onlydef_dynamic_static_lazy);
456	ATF_TP_ADD_TC(tp, onlydef_static_dynamic_eager);
457	ATF_TP_ADD_TC(tp, onlydef_static_dynamic_lazy);
458	ATF_TP_ADD_TC(tp, opencloseloop_use);
459	ATF_TP_ADD_TC(tp, static_abusedef);
460	ATF_TP_ADD_TC(tp, static_abusedefnoload);
461	ATF_TP_ADD_TC(tp, static_defabuse_eager);
462	ATF_TP_ADD_TC(tp, static_defabuse_lazy);
463	ATF_TP_ADD_TC(tp, static_defuse_eager);
464	ATF_TP_ADD_TC(tp, static_defuse_lazy);
465	ATF_TP_ADD_TC(tp, static_usedef);
466	ATF_TP_ADD_TC(tp, static_usedefnoload);
467	return atf_no_error();
468}
469