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
pthread_attr_init(pthread_attr_t * attrp)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
pthread_attr_destroy(pthread_attr_t * attrp)65 pthread_attr_destroy(pthread_attr_t *attrp)
66 {
67 free(*attrp);
68 *attrp = NULL;
69
70 return (0);
71 }
72
73 int
pthread_attr_getguardsize(const pthread_attr_t * attrp,size_t * guardsize)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
pthread_attr_setguardsize(pthread_attr_t * attrp,size_t guardsize)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
pthread_attr_getdetachstate(const pthread_attr_t * attrp,int * detachstate)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
pthread_attr_setdetachstate(pthread_attr_t * attrp,int detachstate)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
pthread_attr_getstack(const pthread_attr_t * attrp,void ** stackaddr,size_t * stacksize)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
pthread_attr_setstack(pthread_attr_t * attrp,void * stackaddr,size_t stacksize)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
pthread_attr_getstacksize(const pthread_attr_t * attrp,size_t * stacksize)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
pthread_attr_setstacksize(pthread_attr_t * attrp,size_t stacksize)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
pthread_attr_getstackaddr(const pthread_attr_t * attrp,void ** stackaddr)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
pthread_attr_setstackaddr(pthread_attr_t * attrp,void * stackaddr)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
pthread_attr_getscope(const pthread_attr_t * attrp,int * contentionscope)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
pthread_attr_setscope(pthread_attr_t * attrp,int contentionscope)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