GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/librthread/rthread_sem.c Lines: 100 187 53.5 %
Date: 2017-11-07 Branches: 60 130 46.2 %

Line Branch Exec Source
1
/*	$OpenBSD: rthread_sem.c,v 1.26 2017/09/05 02:40:54 guenther Exp $ */
2
/*
3
 * Copyright (c) 2004,2005,2013 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
#include <sys/types.h>
20
#include <sys/mman.h>
21
#include <sys/stat.h>
22
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <sha2.h>
26
#include <stdarg.h>
27
#include <stdlib.h>
28
#include <stdio.h>
29
#include <string.h>
30
#include <unistd.h>
31
32
#include <pthread.h>
33
34
#include "rthread.h"
35
#include "cancel.h"		/* in libc/include */
36
37
#define SHARED_IDENT ((void *)-1)
38
39
/* SHA256_DIGEST_STRING_LENGTH includes nul */
40
/* "/tmp/" + sha256 + ".sem" */
41
#define SEM_PATH_SIZE (5 + SHA256_DIGEST_STRING_LENGTH + 4)
42
43
/* long enough to be hard to guess */
44
#define SEM_RANDOM_NAME_LEN	10
45
46
/*
47
 * Size of memory to be mmap()'ed by named semaphores.
48
 * Should be >= SEM_PATH_SIZE and page-aligned.
49
 */
50
#define SEM_MMAP_SIZE	_thread_pagesize
51
52
/*
53
 * Internal implementation of semaphores
54
 */
55
int
56
_sem_wait(sem_t sem, int tryonly, const struct timespec *abstime,
57
    int *delayed_cancel)
58
{
59
264668
	void *ident = (void *)&sem->waitcount;
60
	int r;
61
62
132334
	if (sem->shared)
63
		ident = SHARED_IDENT;
64
65
132334
	_spinlock(&sem->lock);
66
132334
	if (sem->value) {
67
131185
		sem->value--;
68
		r = 0;
69
132334
	} else if (tryonly) {
70
		r = EAGAIN;
71
355
	} else {
72
794
		sem->waitcount++;
73
794
		do {
74
806
			r = __thrsleep(ident, CLOCK_REALTIME, abstime,
75
806
			    &sem->lock, delayed_cancel);
76
806
			_spinlock(&sem->lock);
77
			/* ignore interruptions other than cancelation */
78

830
			if (r == EINTR && (delayed_cancel == NULL ||
79
12
			    *delayed_cancel == 0))
80
12
				r = 0;
81

1608
		} while (r == 0 && sem->value == 0);
82
794
		sem->waitcount--;
83
794
		if (r == 0)
84
790
			sem->value--;
85
	}
86
132334
	_spinunlock(&sem->lock);
87
132334
	return (r);
88
}
89
90
/* always increment count */
91
int
92
_sem_post(sem_t sem)
93
{
94
269038
	void *ident = (void *)&sem->waitcount;
95
	int rv = 0;
96
97
134519
	if (sem->shared)
98
		ident = SHARED_IDENT;
99
100
134519
	_spinlock(&sem->lock);
101
134519
	sem->value++;
102
134519
	if (sem->waitcount) {
103
799
		__thrwakeup(ident, 1);
104
		rv = 1;
105
799
	}
106
134519
	_spinunlock(&sem->lock);
107
134519
	return (rv);
108
}
109
110
/*
111
 * exported semaphores
112
 */
113
int
114
sem_init(sem_t *semp, int pshared, unsigned int value)
115
{
116
	sem_t sem;
117
118
21898
	if (value > SEM_VALUE_MAX) {
119
		errno = EINVAL;
120
		return (-1);
121
	}
122
123
10949
	if (pshared) {
124
		errno = EPERM;
125
		return (-1);
126
#ifdef notyet
127
		char name[SEM_RANDOM_NAME_LEN];
128
		sem_t *sempshared;
129
		int i;
130
131
		for (;;) {
132
			for (i = 0; i < SEM_RANDOM_NAME_LEN - 1; i++)
133
				name[i] = arc4random_uniform(255) + 1;
134
			name[SEM_RANDOM_NAME_LEN - 1] = '\0';
135
			sempshared = sem_open(name, O_CREAT | O_EXCL, 0, value);
136
			if (sempshared != SEM_FAILED)
137
				break;
138
			if (errno == EEXIST)
139
				continue;
140
			if (errno != EPERM)
141
				errno = ENOSPC;
142
			return (-1);
143
		}
144
145
		/* unnamed semaphore should not be opened twice */
146
		if (sem_unlink(name) == -1) {
147
			sem_close(sempshared);
148
			errno = ENOSPC;
149
			return (-1);
150
		}
151
152
		*semp = *sempshared;
153
		free(sempshared);
154
		return (0);
155
#endif
156
	}
157
158
10949
	sem = calloc(1, sizeof(*sem));
159
10949
	if (!sem) {
160
		errno = ENOSPC;
161
		return (-1);
162
	}
163
10949
	sem->lock = _SPINLOCK_UNLOCKED;
164
10949
	sem->value = value;
165
10949
	*semp = sem;
166
167
10949
	return (0);
168
10949
}
169
170
int
171
sem_destroy(sem_t *semp)
172
{
173
	sem_t sem;
174
175
222
	if (!_threads_ready)		 /* for SEM_MMAP_SIZE */
176
8
		_rthread_init();
177
178

222
	if (!semp || !(sem = *semp)) {
179
		errno = EINVAL;
180
		return (-1);
181
	}
182
183
111
	if (sem->waitcount) {
184
#define MSG "sem_destroy on semaphore with waiters!\n"
185
8
		write(2, MSG, sizeof(MSG) - 1);
186
#undef MSG
187
8
		errno = EBUSY;
188
8
		return (-1);
189
	}
190
191
103
	*semp = NULL;
192
103
	if (sem->shared)
193
		munmap(sem, SEM_MMAP_SIZE);
194
	else
195
103
		free(sem);
196
197
103
	return (0);
198
111
}
199
200
int
201
sem_getvalue(sem_t *semp, int *sval)
202
{
203
	sem_t sem;
204
205

84
	if (!semp || !(sem = *semp)) {
206
		errno = EINVAL;
207
		return (-1);
208
	}
209
210
28
	_spinlock(&sem->lock);
211
28
	*sval = sem->value;
212
28
	_spinunlock(&sem->lock);
213
214
28
	return (0);
215
28
}
216
217
int
218
sem_post(sem_t *semp)
219
{
220
	sem_t sem;
221
222

371079
	if (!semp || !(sem = *semp)) {
223
		errno = EINVAL;
224
		return (-1);
225
	}
226
227
123693
	_sem_post(sem);
228
229
123693
	return (0);
230
123693
}
231
232
int
233
sem_wait(sem_t *semp)
234
{
235
85394
	struct tib *tib = TIB_GET();
236
	pthread_t self;
237
	sem_t sem;
238
	int r;
239
42697
	PREP_CANCEL_POINT(tib);
240
241
42697
	if (!_threads_ready)
242
2615
		_rthread_init();
243
42697
	self = tib->tib_thread;
244
245

85394
	if (!semp || !(sem = *semp)) {
246
4
		errno = EINVAL;
247
4
		return (-1);
248
	}
249
250

85386
	ENTER_DELAYED_CANCEL_POINT(tib, self);
251
42693
	r = _sem_wait(sem, 0, NULL, &self->delayed_cancel);
252

85386
	LEAVE_CANCEL_POINT_INNER(tib, r);
253
254
42693
	if (r) {
255
		errno = r;
256
		return (-1);
257
	}
258
259
42693
	return (0);
260
42697
}
261
262
int
263
sem_timedwait(sem_t *semp, const struct timespec *abstime)
264
{
265
32
	struct tib *tib = TIB_GET();
266
	pthread_t self;
267
	sem_t sem;
268
	int r;
269
16
	PREP_CANCEL_POINT(tib);
270
271
16
	if (!_threads_ready)
272
4
		_rthread_init();
273
16
	self = tib->tib_thread;
274
275

32
	if (!semp || !(sem = *semp)) {
276
4
		errno = EINVAL;
277
4
		return (-1);
278
	}
279
280

24
	ENTER_DELAYED_CANCEL_POINT(tib, self);
281
12
	r = _sem_wait(sem, 0, abstime, &self->delayed_cancel);
282

28
	LEAVE_CANCEL_POINT_INNER(tib, r);
283
284
12
	if (r) {
285
4
		errno = r == EWOULDBLOCK ? ETIMEDOUT : r;
286
4
		return (-1);
287
	}
288
289
8
	return (0);
290
16
}
291
292
int
293
sem_trywait(sem_t *semp)
294
{
295
	sem_t sem;
296
	int r;
297
298

244068
	if (!semp || !(sem = *semp)) {
299
4
		errno = EINVAL;
300
4
		return (-1);
301
	}
302
303
81352
	r = _sem_wait(sem, 1, NULL, NULL);
304
305
81352
	if (r) {
306
355
		errno = r;
307
355
		return (-1);
308
	}
309
310
80997
	return (0);
311
81356
}
312
313
314
static void
315
makesempath(const char *origpath, char *sempath, size_t len)
316
{
317
	char buf[SHA256_DIGEST_STRING_LENGTH];
318
319
	SHA256Data(origpath, strlen(origpath), buf);
320
	snprintf(sempath, len, "/tmp/%s.sem", buf);
321
}
322
323
sem_t *
324
sem_open(const char *name, int oflag, ...)
325
{
326
	char sempath[SEM_PATH_SIZE];
327
	struct stat sb;
328
	sem_t sem, *semp;
329
	unsigned int value = 0;
330
	int created = 0, fd;
331
332
	if (!_threads_ready)
333
		_rthread_init();
334
335
	if (oflag & ~(O_CREAT | O_EXCL)) {
336
		errno = EINVAL;
337
		return (SEM_FAILED);
338
	}
339
340
	if (oflag & O_CREAT) {
341
		va_list ap;
342
		va_start(ap, oflag);
343
		/* 3rd parameter mode is not used */
344
		va_arg(ap, mode_t);
345
		value = va_arg(ap, unsigned);
346
		va_end(ap);
347
348
		if (value > SEM_VALUE_MAX) {
349
			errno = EINVAL;
350
			return (SEM_FAILED);
351
		}
352
	}
353
354
	makesempath(name, sempath, sizeof(sempath));
355
	fd = open(sempath, O_RDWR | O_NOFOLLOW | oflag, 0600);
356
	if (fd == -1)
357
		return (SEM_FAILED);
358
	if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) {
359
		close(fd);
360
		errno = EINVAL;
361
		return (SEM_FAILED);
362
	}
363
	if (sb.st_uid != geteuid()) {
364
		close(fd);
365
		errno = EPERM;
366
		return (SEM_FAILED);
367
	}
368
	if (sb.st_size != (off_t)SEM_MMAP_SIZE) {
369
		if (!(oflag & O_CREAT)) {
370
			close(fd);
371
			errno = EINVAL;
372
			return (SEM_FAILED);
373
		}
374
		if (sb.st_size != 0) {
375
			close(fd);
376
			errno = EINVAL;
377
			return (SEM_FAILED);
378
		}
379
		if (ftruncate(fd, SEM_MMAP_SIZE) == -1) {
380
			close(fd);
381
			errno = EINVAL;
382
			return (SEM_FAILED);
383
		}
384
385
		created = 1;
386
	}
387
	sem = mmap(NULL, SEM_MMAP_SIZE, PROT_READ | PROT_WRITE,
388
	    MAP_SHARED, fd, 0);
389
	close(fd);
390
	if (sem == MAP_FAILED) {
391
		errno = EINVAL;
392
		return (SEM_FAILED);
393
	}
394
	semp = malloc(sizeof(*semp));
395
	if (!semp) {
396
		munmap(sem, SEM_MMAP_SIZE);
397
		errno = ENOSPC;
398
		return (SEM_FAILED);
399
	}
400
	if (created) {
401
		sem->lock = _SPINLOCK_UNLOCKED;
402
		sem->value = value;
403
		sem->shared = 1;
404
	}
405
	*semp = sem;
406
407
	return (semp);
408
}
409
410
int
411
sem_close(sem_t *semp)
412
{
413
	sem_t sem;
414
415
	if (!semp || !(sem = *semp) || !sem->shared) {
416
		errno = EINVAL;
417
		return (-1);
418
	}
419
420
	*semp = NULL;
421
	munmap(sem, SEM_MMAP_SIZE);
422
	free(semp);
423
424
	return (0);
425
}
426
427
int
428
sem_unlink(const char *name)
429
{
430
	char sempath[SEM_PATH_SIZE];
431
432
	makesempath(name, sempath, sizeof(sempath));
433
	return (unlink(sempath));
434
}