diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2019-12-02 20:48:13 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2019-12-05 21:14:47 -0800 |
commit | e9f55d90e482f680504487be6b3afb80865691d6 (patch) | |
tree | b730e5720a0fe89bd12e4886a22b3d40a7e7640e | |
parent | f9e5c9e91be200fced2c24a62bcc401344125d2a (diff) | |
download | libcap-e9f55d90e482f680504487be6b3afb80865691d6.tar.gz |
Implement a helper library for POSIX semantics syscalls.
Since Linux kernel supported threads are not POSIX threads
and the glibc pthread library only supports POSIX semantics
for 9 system calls, to fully support the POSIX semantics for
a process sharing its security state across all of its
threads, we've created libpsx.
This commit also includes a threading test in tests/ for
this new psx_syscall() abstraction - one that transparently
mirrors calling POSIX-needing semantics syscalls over
all running threads.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r-- | Make.Rules | 3 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | libcap/.gitignore | 1 | ||||
-rw-r--r-- | libcap/Makefile | 21 | ||||
-rw-r--r-- | libcap/include/sys/psx_syscall.h | 128 | ||||
-rw-r--r-- | libcap/psx.c | 325 | ||||
-rw-r--r-- | pam_cap/Makefile | 15 | ||||
-rw-r--r-- | progs/Makefile | 3 | ||||
-rw-r--r-- | tests/.gitignore | 2 | ||||
-rw-r--r-- | tests/Makefile | 23 | ||||
-rw-r--r-- | tests/psx_test.c | 108 |
11 files changed, 614 insertions, 16 deletions
@@ -61,6 +61,9 @@ WARNINGS=-Wall -Wwrite-strings \ -Wnested-externs -Winline -Wshadow LD=$(CC) -Wl,-x -shared LDFLAGS := #-g +LIBCAPLIB := -L$(topdir)/libcap -lcap +LIBPSXLIB := -L$(topdir)/libcap -lpsx -lpthread + BUILD_GPERF := $(shell which gperf >/dev/null 2>/dev/null && echo yes) SYSTEM_HEADERS = /usr/include @@ -16,6 +16,7 @@ endif ifeq ($(GOLANG),yes) $(MAKE) -C go $@ endif + $(MAKE) -C tests $@ $(MAKE) -C progs $@ $(MAKE) -C doc $@ $(MAKE) -C kdebug $@ diff --git a/libcap/.gitignore b/libcap/.gitignore index 19a6bc9..2a53b2f 100644 --- a/libcap/.gitignore +++ b/libcap/.gitignore @@ -3,5 +3,6 @@ cap_names.list.h _caps_output.gperf libcap.a libcap.so* +libpsx.a _makenames libcap.pc diff --git a/libcap/Makefile b/libcap/Makefile index d5d36c6..8619972 100644 --- a/libcap/Makefile +++ b/libcap/Makefile @@ -9,16 +9,20 @@ include ../Make.Rules LIBNAME=$(LIBTITLE).so STALIBNAME=$(LIBTITLE).a # +STAPSXLIBNAME=libpsx.a -FILES=cap_alloc cap_proc cap_extint cap_flag cap_text cap_file +CAPFILES=cap_alloc cap_proc cap_extint cap_flag cap_text cap_file +PSXFILES=psx INCLS=libcap.h cap_names.h $(INCS) -OBJS=$(addsuffix .o, $(FILES)) +CAPOBJS=$(addsuffix .o, $(CAPFILES)) +PSXOBJS=$(addsuffix .o, $(PSXFILES)) + MAJLIBNAME=$(LIBNAME).$(VERSION) MINLIBNAME=$(MAJLIBNAME).$(MINOR) GPERF_OUTPUT = _caps_output.gperf -all: $(MINLIBNAME) $(STALIBNAME) libcap.pc +all: $(MINLIBNAME) $(STALIBNAME) libcap.pc $(STAPSXLIBNAME) ifeq ($(BUILD_GPERF),yes) USE_GPERF_OUTPUT = $(GPERF_OUTPUT) @@ -48,11 +52,15 @@ cap_names.list.h: Makefile $(KERNEL_HEADERS)/linux/capability.h @echo "=> making $@ from $(KERNEL_HEADERS)/linux/capability.h" perl -e 'while ($$l=<>) { if ($$l =~ /^\#define[ \t](CAP[_A-Z]+)[ \t]+([0-9]+)\s+$$/) { $$tok=$$1; $$val=$$2; $$tok =~ tr/A-Z/a-z/; print "{\"$$tok\",$$val},\n"; } }' $(KERNEL_HEADERS)/linux/capability.h | fgrep -v 0x > $@ -$(STALIBNAME): $(OBJS) +$(STALIBNAME): $(CAPOBJS) + $(AR) rcs $@ $^ + $(RANLIB) $@ + +$(STAPSXLIBNAME): $(PSXOBJS) $(AR) rcs $@ $^ $(RANLIB) $@ -$(MINLIBNAME): $(OBJS) +$(MINLIBNAME): $(CAPOBJS) $(LD) $(CFLAGS) $(LDFLAGS) -Wl,-soname,$(MAJLIBNAME) -o $@ $^ ln -sf $(MINLIBNAME) $(MAJLIBNAME) ln -sf $(MAJLIBNAME) $(LIBNAME) @@ -79,6 +87,7 @@ endif clean: $(LOCALCLEAN) - rm -f $(OBJS) $(LIBNAME)* $(STALIBNAME) libcap.pc + rm -f $(CAPOBJS) $(LIBNAME)* $(STALIBNAME) libcap.pc + rm -f $(PSXOBJS) $(STAPSXLIBNAME) rm -f cap_names.h cap_names.list.h _makenames $(GPERF_OUTPUT) cd include/sys && $(LOCALCLEAN) diff --git a/libcap/include/sys/psx_syscall.h b/libcap/include/sys/psx_syscall.h new file mode 100644 index 0000000..1df2659 --- /dev/null +++ b/libcap/include/sys/psx_syscall.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 Andrew G. Morgan <morgan@kernel.org> + * + * This header, and the -lpsx library, provide a number of things to + * support POSIX semantics for syscalls associated with the pthread + * library. Linking this code is tricky and is done as follows: + * + * ld ... -lpsx -lpthread --wrap=pthread_create + * or, gcc ... -lpsx -lpthread -Wl,-wrap,pthread_create + * + * glibc provides a subset of this functionality natively through the + * nptl:setxid mechanism and could implement psx_syscall() directly + * using that style of functionality but, as of 2019-11-30, the setxid + * mechanism is limited to 9 specific set*() syscalls that do not + * support the syscall6 API (needed for prctl functions and the ambient + * capabilities set for example). + * + * This psx library API also includes explicit registration of threads + * if implicit wrapping the pthread_create() function is problematic + * for your application via the psx_pthread_create() function. To use + * the library in that way, you should include this line in the file + * containing your main() function: + * + * ----------- + * #include <sys/psx_syscall.h> + * + * int main(...) { + * + * .... + * + * } + * PSX_NO_LINKER_WRAPPING + * ----------- + * + * This will ensure that your binary can link. + */ + +#ifndef _SYS_PSX_SYSCALL_H +#define _SYS_PSX_SYSCALL_H + +#include <pthread.h> + +/* + * This function is actually provided by the linker trick: + * + * gcc ... -lpsx -lpthread -Wl,-wrap,pthread_create + */ +int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + +#define PSX_NO_LINKER_WRAPPING int \ + __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr, \ + void *(*start_routine) (void *), void *arg) { \ + return -1; \ + } + +#define PSX_DEFAULT_INTERRUPT 42 + +/* + * psx_set_interrupt specifies the non-default interrupt number used + * by the psx abstraction. Return status of 0 means success, -1 + * otherwise and errno is set to the cause. + */ +int psx_set_interrupt(int interrupt_number); + +/* + * psx_syscall performs the specified syscall on all psx registered + * threads. The mecanism by which this occurs is much less efficient + * than a standard system call on Linux, so it should only be used + * when POSIX semantics are required to change process relevant + * security state. + * + * Glibc has native support for POSIX semantics on setgroups() and the + * 8 set*[gu]id() functions. So, there is no need to use psx_syscall() + * for these calls. This call exists for all the other system calls + * that need to maintain parity on all pthreads of a program. + * + * Some macrology is used to allow the caller to provide only as many + * arguments as needed, thus psx_syscall() cannot be used as a + * function pointer. For those situations, we define psx_syscall3() + * and psx_syscall6(). + */ +#define psx_syscall(syscall_nr, ...) \ + __psx_syscall(syscall_nr, __VA_ARGS__, 6, 5, 4, 3, 2, 1, 0) +long int __psx_syscall(long int syscall_nr, ...); +long int psx_syscall3(long int syscall_nr, + long int arg1, long int arg2, long int arg3); +long int psx_syscall6(long int syscall_nr, + long int arg1, long int arg2, long int arg3, + long int arg4, long int arg5, long int arg6); + +/* + * psx_register registers a pthread with the psx abstraction of system + * calls. + */ +void psx_register(pthread_t thread); + +/* + * psx_pthread_create() wraps the -lpthread pthread_create() function + * call and registers the generated thread with the psx_syscall + * infrastructure. + * + * Note, to transparently redirect all the pthread_create() calls in + * your binary to psx_pthread_create(), link with: + * + * gcc ... -lpsx -lpthread -Wl,-wrap,pthread_create + * + * [That is, libpsx contains an internal definition for the + * __wrap_pthread_create function to invoke psx_pthread_create + * functionality instead.] + */ +int psx_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + +/* + * This function is weakly defined as a no-op in libpsx. If code + * linking to it provides an alternative implementation, that one is + * called instead. For example, libcap provides one of these functions + * so psx can share its syscall functions with libcap at start up and + * thus pthreads automagically become POSIX semantics compliant. + */ +void share_psx_syscall(long int (*syscall_fn)(long int, + long int, long int, long int), + long int (*syscall6_fn)(long int, + long int, long int, long int, + long int, long int, long int)); + +#endif /* _SYS_PSX_SYSCALL_H */ diff --git a/libcap/psx.c b/libcap/psx.c new file mode 100644 index 0000000..b9bd8a5 --- /dev/null +++ b/libcap/psx.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2019 Andrew G Morgan <morgan@kernel.org> + * + * This file contains a collection of routines that perform thread + * synchronization to ensure that a whole process is running as a + * single privilege object - independent of the number of pthreads. + * + * The whole file would be unnecessary if glibc exported an explicit + * psx_syscall()-like function that leveraged the nptl:setxid + * mechanism to synchronize thread state over the whole process. + */ + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/psx_syscall.h> +#include <unistd.h> + +/* + * share_psx_syscall() is invoked to advertize the two functions + * psx_syscall3() and psx_syscall6(). The linkage is weak here so some + * code external to this library can override it transparently during + * the linkage process. + */ +__attribute__((weak)) +void share_psx_syscall(long int (*syscall_fn)(long int, + long int, long int, long int), + long int (*syscall6_fn)(long int, + long int, long int, long int, + long int, long int, long int)) +{ +} + +/* + * type to keep track of registered threads. + */ +typedef struct registered_thread_s { + struct registered_thread_s *next, *prev; + pthread_t thread; +} registered_thread_t; + +static pthread_once_t psx_tracker_initialized = PTHREAD_ONCE_INIT; + +/* + * This global structure holds the global coordination state for + * libcap's psx_posix_syscall() support. + */ +static struct psx_tracker_s { + pthread_mutex_t mu; + + int initialized; + int psx_sig; + + struct { + pthread_mutex_t mu; + pthread_cond_t cond; + long syscall_nr; + long arg1, arg2, arg3, arg4, arg5, arg6; + int six; + int active; + int todo; + } cmd; + + struct sigaction sig_action; + registered_thread_t *root; +} psx_tracker; + +/* + * psx_posix_syscall_handler performs the system call on the targeted + * thread and decreases the outstanding syscall counter. + */ +static void psx_posix_syscall_handler(int signum) { + if (!psx_tracker.cmd.active || signum != psx_tracker.psx_sig) { + return; + } + + if (!psx_tracker.cmd.six) { + (void) syscall(psx_tracker.cmd.syscall_nr, + psx_tracker.cmd.arg1, + psx_tracker.cmd.arg2, + psx_tracker.cmd.arg3); + } else { + (void) syscall(psx_tracker.cmd.syscall_nr, + psx_tracker.cmd.arg1, + psx_tracker.cmd.arg2, + psx_tracker.cmd.arg3, + psx_tracker.cmd.arg4, + psx_tracker.cmd.arg5, + psx_tracker.cmd.arg6); + } + + pthread_mutex_lock(&psx_tracker.cmd.mu); + if (! --psx_tracker.cmd.todo) { + pthread_cond_signal(&psx_tracker.cmd.cond); + } + pthread_mutex_unlock(&psx_tracker.cmd.mu); +} + +long int psx_syscall3(long int syscall_nr, + long int arg1, long int arg2, long int arg3) { + return psx_syscall(syscall_nr, arg1, arg2, arg3); +} + +long int psx_syscall6(long int syscall_nr, + long int arg1, long int arg2, long int arg3, + long int arg4, long int arg5, long int arg6) { + return psx_syscall(syscall_nr, arg1, arg2, arg3, arg4, arg5, arg6); +} + +/* + * psx_syscall_start initializes the subsystem. + */ +static void psx_syscall_start(void) { + psx_tracker.initialized = 1; + + psx_tracker.psx_sig = 42; /* default signal number for syscall syncing */ + psx_tracker.sig_action.sa_handler = psx_posix_syscall_handler; + sigemptyset(&psx_tracker.sig_action.sa_mask); + psx_tracker.sig_action.sa_flags = 0; + + sigaction(psx_tracker.psx_sig, &psx_tracker.sig_action, NULL); + + share_psx_syscall(psx_syscall3, psx_syscall6); +} + +static void psx_do_registration(pthread_t thread) { + int first_time = !psx_tracker.initialized; + (void) pthread_once(&psx_tracker_initialized, psx_syscall_start); + + if (first_time) { + // First invocation, use recursion to register main() thread. + psx_do_registration(pthread_self()); + } + + registered_thread_t *node = calloc(1, sizeof(registered_thread_t)); + node->next = psx_tracker.root; + if (node->next) { + node->next->prev = node; + } + node->thread = thread; + psx_tracker.root = node; +} + +/* + * psx_register registers the a thread as pariticipating in the single + * (POSIX) pool of privilege used by the library. + * + * In normal, expected, use you should never need to call this because + * the linker magic wrapping will arrange for this function to be + * called implicitly every time a pthread is created with + * pthread_create() or psx_pthread_create(). If, however, you are + * unable to use the linker trick to wrap pthread_create(), you should + * include this line in one and only one place in your code. Just + * after the end of the main() function would be a good place, for + * example: + * + * int main(int argc, char **argv) { + + * ... + * } + * + * PSX_NO_LINKER_WRAPPING + * + * This is required for libpsx to link. It also requires the coder to + * explicitly use psx_register() for all threads not started with + * psx_pthread_create(). + * + * Note, there is no need to ever register the main() process thread. + */ +void psx_register(pthread_t thread) { + pthread_mutex_lock(&psx_tracker.mu); + psx_do_registration(thread); + pthread_mutex_unlock(&psx_tracker.mu); +} + +/* provide a prototype */ +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + +/* + * psx_pthread_create is a wrapper for pthread_create() that registers + * the newly created thread. If your threads are created already, they + * can be individually registered with psx_register(). + */ +int psx_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) { + if (pthread_create == __wrap_pthread_create) { + return __wrap_pthread_create(thread, attr, start_routine, arg); + } + + pthread_mutex_lock(&psx_tracker.mu); + int ret = pthread_create(thread, attr, start_routine, arg); + if (ret != -1) { + psx_do_registration(*thread); + } + pthread_mutex_unlock(&psx_tracker.mu); + return ret; +} + +/* + * __wrap_pthread_create is the wrapped destination of all regular + * pthread_create calls. + */ +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) { + pthread_mutex_lock(&psx_tracker.mu); + int ret = __real_pthread_create(thread, attr, start_routine, arg); + if (ret != -1) { + psx_do_registration(*thread); + } + pthread_mutex_unlock(&psx_tracker.mu); + return ret; +} + +/* + * __psx_syscall performs the syscall on the current thread and if no + * error is detected it ensures that the syscall is also performed on + * all (other) registered threads. The return code is the value for + * the first invocation. It uses a trick to figure out how many + * arguments the user has supplied. The other half of the trick is + * provided by the macro psx_syscall() in the <sys/psx_syscall.h> + * file. The trick is the 7th optional argument (8th over all) to + * __psx_syscall is the count of arguments supplied to psx_syscall. + * + * User: + * psx_syscall(nr, a, b); + * Expanded by macro to: + * __psx_syscall(nr, a, b, 6, 5, 4, 3, 2, 1, 0); + * The eighth arg is now ------------------------------------^ + */ +long int __psx_syscall(long int syscall_nr, ...) { + long int arg[7]; + + va_list aptr; + va_start(aptr, syscall_nr); + for (int i = 0; i < 7; i++) { + arg[i] = va_arg(aptr, long int); + } + va_end(aptr); + + int count = arg[6]; + if (count < 0 || count > 6) { + errno = EINVAL; + return -1; + } + + pthread_mutex_lock(&psx_tracker.mu); + long int ret; + + psx_tracker.cmd.syscall_nr = syscall_nr; + psx_tracker.cmd.arg1 = count > 0 ? arg[0] : 0; + psx_tracker.cmd.arg2 = count > 1 ? arg[1] : 0; + psx_tracker.cmd.arg3 = count > 2 ? arg[2] : 0; + if (count > 3) { + psx_tracker.cmd.six = 1; + psx_tracker.cmd.arg1 = arg[3]; + psx_tracker.cmd.arg2 = count > 4 ? arg[4] : 0; + psx_tracker.cmd.arg3 = count > 5 ? arg[5] : 0; + + ret = syscall(syscall_nr, + psx_tracker.cmd.arg1, + psx_tracker.cmd.arg2, + psx_tracker.cmd.arg3, + psx_tracker.cmd.arg4, + psx_tracker.cmd.arg5, + psx_tracker.cmd.arg6); + } else { + psx_tracker.cmd.six = 0; + + ret = syscall(syscall_nr, + psx_tracker.cmd.arg1, + psx_tracker.cmd.arg2, + psx_tracker.cmd.arg3); + } + + if (ret == -1 || !psx_tracker.initialized) { + goto defer; + } + + int restore_errno = errno; + psx_tracker.cmd.active = 1; + + pthread_t self = pthread_self(); + registered_thread_t *next = NULL; + for (registered_thread_t *ref = psx_tracker.root; ref; ref = next) { + next = ref->next; + if (ref->thread == self) { + continue; + } + if (pthread_kill(ref->thread, psx_tracker.psx_sig) == 0) { + pthread_mutex_lock(&psx_tracker.cmd.mu); + psx_tracker.cmd.todo++; + pthread_mutex_unlock(&psx_tracker.cmd.mu); + continue; + } + + /* need to remove now invalid thread id from linked list */ + if (ref == psx_tracker.root) { + psx_tracker.root = next; + } else if (ref->prev) { + ref->prev->next = next; + } + if (next) { + next->prev = ref->prev; + } + free(ref); + } + + pthread_mutex_lock(&psx_tracker.cmd.mu); + while (psx_tracker.cmd.todo) { + pthread_cond_wait(&psx_tracker.cmd.cond, &psx_tracker.cmd.mu); + } + pthread_mutex_unlock(&psx_tracker.cmd.mu); + + errno = restore_errno; +defer: + + psx_tracker.cmd.active = 0; + pthread_mutex_unlock(&psx_tracker.mu); + + return ret; +} diff --git a/pam_cap/Makefile b/pam_cap/Makefile index cc32fb6..22f0f81 100644 --- a/pam_cap/Makefile +++ b/pam_cap/Makefile @@ -3,12 +3,6 @@ topdir=$(shell pwd)/.. include ../Make.Rules -# Note (as the author of much of the Linux-PAM library, I am confident -# that this next line does *not* require -lpam on it.) If you think it -# does, *verify that it does*, and if you observe that it fails as -# written (and you know why it fails), email me and explain why. Thanks! -LDLIBS += -L../libcap -lcap - all: pam_cap.so $(MAKE) testcompile @@ -16,14 +10,19 @@ install: all mkdir -p -m 0755 $(FAKEROOT)$(LIBDIR)/security install -m 0755 pam_cap.so $(FAKEROOT)$(LIBDIR)/security +# Note (as the author of much of the Linux-PAM library, I am confident +# that this next line does *not* require -lpam on it.) If you think it +# does, *verify that it does*, and if you observe that it fails as +# written (and you know why it fails), email me and explain why. Thanks! + pam_cap.so: pam_cap.o - $(LD) $(LDFLAGS) -o pam_cap.so $< $(LDLIBS) + $(LD) -o pam_cap.so $< $(LIBCAPLIB) $(LDFLAGS) pam_cap.o: pam_cap.c $(CC) $(CFLAGS) $(IPATH) -c $< -o $@ testcompile: test.c pam_cap.o - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lpam -ldl $(LDLIBS) + $(CC) $(CFLAGS) -o $@ $+ -lpam -ldl $(LIBCAPLIB) $(LDFLAGS) clean: rm -f *.o *.so testcompile *~ diff --git a/progs/Makefile b/progs/Makefile index 148b6af..0786ad3 100644 --- a/progs/Makefile +++ b/progs/Makefile @@ -11,12 +11,11 @@ BUILD=$(PROGS) ifneq ($(DYNAMIC),yes) LDFLAGS += --static endif -LDLIBS += -L../libcap -lcap all: $(BUILD) $(BUILD): %: %.o - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) + $(CC) $(CFLAGS) -o $@ $< $(LIBCAPLIB) $(LDFLAGS) %.o: %.c $(INCS) $(CC) $(IPATH) $(CFLAGS) -c $< -o $@ diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..42afee3 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +psx_test +psx_test_wrap diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..739e864 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,23 @@ +# +# defines +# +topdir=$(shell pwd)/.. +include ../Make.Rules +# + +all: run_psx_test + +install: all + +run_psx_test: psx_test psx_test_wrap + ./psx_test + ./psx_test_wrap + +psx_test: psx_test.c + $(CC) $(CFLAGS) $(IPATH) -DNOWRAP $< -o $@ $(LIBPSXLIB) + +psx_test_wrap: psx_test.c + $(CC) $(CFLAGS) $(IPATH) $< -o $@ $(LIBPSXLIB) -Wl,-wrap,pthread_create + +clean: + rm -f psx_test psx_test_wrap diff --git a/tests/psx_test.c b/tests/psx_test.c new file mode 100644 index 0000000..92b99a0 --- /dev/null +++ b/tests/psx_test.c @@ -0,0 +1,108 @@ +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/prctl.h> +#include <sys/psx_syscall.h> +#include <sys/syscall.h> + +static void say_hello_expecting(const char *title, int n, int kept) { + int keeper = prctl(PR_GET_KEEPCAPS); + printf("hello, %s<%d> %lx (keepcaps=%d vs. want=%d)\n", + title, n, pthread_self(), keeper, kept); + if (keeper != kept) { + printf("--> FAILURE %s thread=%lx has wrong keepcaps: got=%d want=%d\n", + title, pthread_self(), keeper, kept); + exit(1); + } +} + +pthread_mutex_t mu; +pthread_cond_t cond; + +int global_kept = 0; +int step = 0; +int replies = 0; +int launched = 0; +int started = 0; + +static void *say_hello(void *args) { + int count = 0; + + pthread_mutex_lock(&mu); + started++; + pthread_cond_broadcast(&cond); + + int this_step = step+1; + do { + while (this_step > step) { + pthread_cond_wait(&cond, &mu); + } + this_step++; + + say_hello_expecting("thread", count, global_kept); + + replies++; + pthread_cond_broadcast(&cond); + } while (++count != 3); + + pthread_mutex_unlock(&mu); + + return NULL; +} + +int main(int argc, char **argv) { + pthread_t tid[3]; + + for (int i = 0; i<10; i++) { + printf("iteration: %d\n", i); + + pthread_mutex_lock(&mu); + global_kept = !global_kept; + replies = 0; + step = i; + pthread_mutex_unlock(&mu); + + psx_syscall(SYS_prctl, PR_SET_KEEPCAPS, global_kept); + step++; + pthread_cond_broadcast(&cond); + + say_hello_expecting("main", i, global_kept); + + pthread_mutex_lock(&mu); + while (replies < launched) { + pthread_cond_wait(&cond, &mu); + } + pthread_mutex_unlock(&mu); + + if (i < 3) { + launched++; + if (i == 1) { + // Confirm this works whether or not we are WRAPPING. + psx_pthread_create(&tid[i], NULL, say_hello, NULL); + } else if (i < 3) { +#ifdef NOWRAP + psx_pthread_create(&tid[i], NULL, say_hello, NULL); +#else + pthread_create(&tid[i], NULL, say_hello, NULL); +#endif + } + // Confirm that the thread is started. + pthread_mutex_lock(&mu); + while (started < launched) { + pthread_cond_wait(&cond, &mu); + } + pthread_mutex_unlock(&mu); + } else if (i < 6) { + // Confirm one thread has finished. + pthread_join(tid[i-3], NULL); + launched--; + } + } + + printf("%s PASSED\n", argv[0]); + exit(0); +} + +#ifdef NOWRAP +PSX_NO_LINKER_WRAPPING +#endif |