xref: /openbsd/lib/librthread/rthread_attr.c (revision 73471bf0)
1 /*	$OpenBSD: rthread_attr.c,v 1.25 2018/05/02 14:06:00 bluhm Exp $ */
2 /*
3  * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * generic attribute support
20  */
21 
22 #include <sys/mman.h>
23 
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <syslog.h>
27 #include <unistd.h>
28 #include <errno.h>
29 
30 #include <pthread.h>
31 #include <pthread_np.h>
32 
33 #include "rthread.h"
34 
35 /*
36  * Note: stack_size + guard_size == total stack used
37  *
38  * pthread_attr_init MUST be called before any other attribute function
39  * for proper operation.
40  *
41  * Every call to pthread_attr_init MUST be matched with a call to
42  * pthread_attr_destroy to avoid leaking memory.   This is an implementation
43  * requirement, not a POSIX requirement.
44  */
45 
46 int
47 pthread_attr_init(pthread_attr_t *attrp)
48 {
49 	pthread_attr_t attr;
50 
51 	/* make sure _rthread_attr_default has been initialized */
52 	if (!_threads_ready)
53 		_rthread_init();
54 
55 	attr = calloc(1, sizeof(*attr));
56 	if (!attr)
57 		return (errno);
58 	*attr = _rthread_attr_default;
59 	*attrp = attr;
60 
61 	return (0);
62 }
63 
64 int
65 pthread_attr_destroy(pthread_attr_t *attrp)
66 {
67 	free(*attrp);
68 	*attrp = NULL;
69 
70 	return (0);
71 }
72 
73 int
74 pthread_attr_getguardsize(const pthread_attr_t *attrp, size_t *guardsize)
75 {
76 	*guardsize = (*attrp)->guard_size;
77 
78 	return (0);
79 }
80 
81 int
82 pthread_attr_setguardsize(pthread_attr_t *attrp, size_t guardsize)
83 {
84 	(*attrp)->guard_size = guardsize;
85 
86 	return (0);
87 }
88 
89 int
90 pthread_attr_getdetachstate(const pthread_attr_t *attrp, int *detachstate)
91 {
92 	*detachstate = (*attrp)->detach_state;
93 
94 	return (0);
95 }
96 
97 int
98 pthread_attr_setdetachstate(pthread_attr_t *attrp, int detachstate)
99 {
100 	int error;
101 
102 	error = (detachstate == PTHREAD_CREATE_DETACHED ||
103 		  detachstate == PTHREAD_CREATE_JOINABLE) ? 0 : EINVAL;
104 	if (error == 0)
105 		(*attrp)->detach_state = detachstate;
106 
107 	return (error);
108 }
109 
110 int
111 pthread_attr_getstack(const pthread_attr_t *attrp, void **stackaddr,
112     size_t *stacksize)
113 {
114 	*stackaddr = (*attrp)->stack_addr;
115 	*stacksize = (*attrp)->stack_size;
116 
117 	return (0);
118 }
119 
120 int
121 pthread_attr_setstack(pthread_attr_t *attrp, void *stackaddr, size_t stacksize)
122 {
123 	int error;
124 	volatile char *p = stackaddr;
125 	size_t i;
126 	struct syslog_data data = SYSLOG_DATA_INIT;
127 
128 	if (stacksize < PTHREAD_STACK_MIN) {
129 		syslog_r(LOG_ERR, &data,
130 		    "pthread_attr_setstack(%p, %zu): "
131 		    "stack size below min size %d",
132 		    stackaddr, stacksize, PTHREAD_STACK_MIN);
133 		return (EINVAL);
134 	}
135 
136 	/*
137 	 * Make sure that the stack is page-aligned and a multiple
138 	 * of the page size
139 	 */
140 	if (((uintptr_t)stackaddr % PTHREAD_STACK_MIN) != 0
141 	    || (stacksize % PTHREAD_STACK_MIN) != 0) {
142 		syslog_r(LOG_ERR, &data,
143 		    "pthread_attr_setstack(%p, 0x%zx): "
144 		    "unaligned thread stack start and/or size",
145 		    stackaddr, stacksize);
146 		return (EINVAL);
147 	}
148 
149 	/*
150 	 * We are going to re-mmap() stackaddr to MAP_STACK, but only
151 	 * if the entire range [stackaddr, stackaddr+stacksize) consists
152 	 * of valid address that are mapped PROT_READ|PROT_WRITE.
153 	 * Test this by reading and writing every page.
154 	 *
155 	 * XXX: What if the caller has SIGSEGV blocked or ignored?
156 	 * Then we won't crash here when entering an invalid mapping.
157 	 */
158 	for (i = 0; i < stacksize; i += PTHREAD_STACK_MIN) {
159 		char val = p[i];
160 
161 		p[i] = val;
162 	}
163 
164 	if (mmap(stackaddr, stacksize, PROT_READ|PROT_WRITE,
165 	    MAP_FIXED|MAP_STACK|MAP_ANON|MAP_PRIVATE, -1, 0) == MAP_FAILED) {
166 		syslog_r(LOG_ERR, &data,
167 		    "pthread_attr_setstack(%p, %zu): mmap error %m",
168 		    stackaddr, stacksize);
169 		return (errno);
170 	}
171 
172 	if ((error = pthread_attr_setstackaddr(attrp, stackaddr)))
173 		return (error);
174 	(*attrp)->stack_size = stacksize;
175 
176 	return (0);
177 }
178 
179 int
180 pthread_attr_getstacksize(const pthread_attr_t *attrp, size_t *stacksize)
181 {
182 	*stacksize = (*attrp)->stack_size;
183 
184 	return (0);
185 }
186 
187 int
188 pthread_attr_setstacksize(pthread_attr_t *attrp, size_t stacksize)
189 {
190 	if (!_threads_ready)		/* for ROUND_TO_PAGE */
191 		_rthread_init();
192 
193 	if (stacksize < PTHREAD_STACK_MIN ||
194 	    stacksize > ROUND_TO_PAGE(stacksize))
195 		return (EINVAL);
196 	(*attrp)->stack_size = stacksize;
197 
198 	return (0);
199 }
200 
201 int
202 pthread_attr_getstackaddr(const pthread_attr_t *attrp, void **stackaddr)
203 {
204 	*stackaddr = (*attrp)->stack_addr;
205 
206 	return (0);
207 }
208 
209 int
210 pthread_attr_setstackaddr(pthread_attr_t *attrp, void *stackaddr)
211 {
212 	if (!_threads_ready)
213 		_rthread_init();		/* for _thread_pagesize */
214 
215 	if (stackaddr == NULL || (uintptr_t)stackaddr & (_thread_pagesize - 1))
216 		return (EINVAL);
217 	(*attrp)->stack_addr = stackaddr;
218 
219 	return (0);
220 }
221 DEF_NONSTD(pthread_attr_setstackaddr);
222 
223 int
224 pthread_attr_getscope(const pthread_attr_t *attrp, int *contentionscope)
225 {
226 	*contentionscope = (*attrp)->contention_scope;
227 
228 	return (0);
229 }
230 
231 int
232 pthread_attr_setscope(pthread_attr_t *attrp, int contentionscope)
233 {
234 	if (contentionscope != PTHREAD_SCOPE_SYSTEM &&
235 	    contentionscope != PTHREAD_SCOPE_PROCESS)
236 		return (EINVAL);
237 	(*attrp)->contention_scope = contentionscope;
238 
239 	return (0);
240 }
241 
242