overhaul __synccall and fix AS-safety and other issues in set*id
multi-threaded set*id and setrlimit use the internal __synccall
function to work around the kernel's wrongful treatment of these
process properties as thread-local. the old implementation of
__synccall failed to be AS-safe, despite POSIX requiring setuid and
setgid to be AS-safe, and was not rigorous in assuring that all
threads were caught. in a worst case, threads late in the process of
exiting could retain permissions after setuid reported success, in
which case attacks to regain dropped permissions may have been
possible under the right conditions.
the new implementation of __synccall depends on the presence of
/proc/self/task and will fail if it can't be opened, but is able to
determine that it has caught all threads, and does not use any locks
except its own. it thereby achieves AS-safety simply by blocking
signals to preclude re-entry in the same thread.
with this commit, all known conformance and safety issues in set*id
functions should be fixed.
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index 1a47ed1..64971d5 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -139,6 +139,8 @@
static void *dummy_tsd[1] = { 0 };
weak_alias(dummy_tsd, __pthread_tsd_main);
+volatile int __block_new_threads = 0;
+
static FILE *volatile dummy_file = 0;
weak_alias(dummy_file, __stdin_used);
weak_alias(dummy_file, __stdout_used);
@@ -178,6 +180,7 @@
if (attrp && !c11) attr = *attrp;
__acquire_ptc();
+ if (__block_new_threads) __wait(&__block_new_threads, 0, 1, 1);
if (attr._a_stackaddr) {
size_t need = libc.tls_size + __pthread_tsd_size;