about summary refs log tree commit diff
path: root/src/unistd
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-07-29 22:59:44 -0400
committerRich Felker <dalias@aerifal.cx>2011-07-29 22:59:44 -0400
commitacb04806628990ad2430e04261dd20f23babde5e (patch)
tree90e734333eee81878a920bfd32277bc96aa46402 /src/unistd
parentaed707f679cce80afd929f0efaf080f1e8481330 (diff)
downloadmusl-acb04806628990ad2430e04261dd20f23babde5e.tar.gz
musl-acb04806628990ad2430e04261dd20f23babde5e.tar.xz
musl-acb04806628990ad2430e04261dd20f23babde5e.zip
new attempt at making set*id() safe and robust
changing credentials in a multi-threaded program is extremely
difficult on linux because it requires synchronizing the change
between all threads, which have their own thread-local credentials on
the kernel side. this is further complicated by the fact that changing
the real uid can fail due to exceeding RLIMIT_NPROC, making it
possible that the syscall will succeed in some threads but fail in
others.

the old __rsyscall approach being replaced was robust in that it would
report failure if any one thread failed, but in this case, the program
would be left in an inconsistent state where individual threads might
have different uid. (this was not as bad as glibc, which would
sometimes even fail to report the failure entirely!)

the new approach being committed refuses to change real user id when
it cannot temporarily set the rlimit to infinity. this is completely
POSIX conformant since POSIX does not require an implementation to
allow real-user-id changes for non-privileged processes whatsoever.
still, setting the real uid can fail due to memory allocation in the
kernel, but this can only happen if there is not already a cached
object for the target user. thus, we forcibly serialize the syscalls
attempts, and fail the entire operation on the first failure. this
*should* lead to an all-or-nothing success/failure result, but it's
still fragile and highly dependent on kernel developers not breaking
things worse than they're already broken.

ideally linux will eventually add a CLONE_USERCRED flag that would
give POSIX conformant credential changes without any hacks from
userspace, and all of this code would become redundant and could be
removed ~10 years down the line when everyone has abandoned the old
broken kernels. i'm not holding my breath...
Diffstat (limited to 'src/unistd')
-rw-r--r--src/unistd/setegid.c4
-rw-r--r--src/unistd/seteuid.c4
-rw-r--r--src/unistd/setgid.c2
-rw-r--r--src/unistd/setregid.c2
-rw-r--r--src/unistd/setresgid.c2
-rw-r--r--src/unistd/setresuid.c2
-rw-r--r--src/unistd/setreuid.c2
-rw-r--r--src/unistd/setuid.c2
8 files changed, 12 insertions, 8 deletions
diff --git a/src/unistd/setegid.c b/src/unistd/setegid.c
index 85348842..e6da2573 100644
--- a/src/unistd/setegid.c
+++ b/src/unistd/setegid.c
@@ -1,6 +1,8 @@
 #include <unistd.h>
+#include "libc.h"
+#include "syscall.h"
 
 int setegid(gid_t egid)
 {
-	return setregid(-1, egid);
+	return __setxid(SYS_setresgid, -1, egid, -1);
 }
diff --git a/src/unistd/seteuid.c b/src/unistd/seteuid.c
index 0aaa86e0..ef8b9df4 100644
--- a/src/unistd/seteuid.c
+++ b/src/unistd/seteuid.c
@@ -1,6 +1,8 @@
 #include <unistd.h>
+#include "syscall.h"
+#include "libc.h"
 
 int seteuid(uid_t euid)
 {
-	return setreuid(-1, euid);
+	return __setxid(SYS_setresuid, -1, euid, -1);
 }
diff --git a/src/unistd/setgid.c b/src/unistd/setgid.c
index 87b2717e..bae4616a 100644
--- a/src/unistd/setgid.c
+++ b/src/unistd/setgid.c
@@ -4,5 +4,5 @@
 
 int setgid(gid_t gid)
 {
-	return __rsyscall(SYS_setgid, gid, 0, 0, 0, 0, 0);
+	return __setxid(SYS_setgid, gid, 0, 0);
 }
diff --git a/src/unistd/setregid.c b/src/unistd/setregid.c
index 665b5556..f5a8972a 100644
--- a/src/unistd/setregid.c
+++ b/src/unistd/setregid.c
@@ -4,5 +4,5 @@
 
 int setregid(gid_t rgid, gid_t egid)
 {
-	return __rsyscall(SYS_setregid, rgid, egid, 0, 0, 0, 0);
+	return __setxid(SYS_setregid, rgid, egid, 0);
 }
diff --git a/src/unistd/setresgid.c b/src/unistd/setresgid.c
index 9b9fe50b..b9af540a 100644
--- a/src/unistd/setresgid.c
+++ b/src/unistd/setresgid.c
@@ -5,5 +5,5 @@
 
 int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 {
-	return __rsyscall(SYS_setresgid, rgid, egid, sgid, 0, 0, 0);
+	return __setxid(SYS_setresgid, rgid, egid, sgid);
 }
diff --git a/src/unistd/setresuid.c b/src/unistd/setresuid.c
index 497f7592..83692b4c 100644
--- a/src/unistd/setresuid.c
+++ b/src/unistd/setresuid.c
@@ -5,5 +5,5 @@
 
 int setresuid(uid_t ruid, uid_t euid, uid_t suid)
 {
-	return __rsyscall(SYS_setresuid, ruid, euid, suid, 0, 0, 0);
+	return __setxid(SYS_setresuid, ruid, euid, suid);
 }
diff --git a/src/unistd/setreuid.c b/src/unistd/setreuid.c
index 93d68c03..3fcc59e2 100644
--- a/src/unistd/setreuid.c
+++ b/src/unistd/setreuid.c
@@ -4,5 +4,5 @@
 
 int setreuid(uid_t ruid, uid_t euid)
 {
-	return __rsyscall(SYS_setreuid, ruid, euid, 0, 0, 0, 0);
+	return __setxid(SYS_setreuid, ruid, euid, 0);
 }
diff --git a/src/unistd/setuid.c b/src/unistd/setuid.c
index e778c7f3..602ecbbf 100644
--- a/src/unistd/setuid.c
+++ b/src/unistd/setuid.c
@@ -4,5 +4,5 @@
 
 int setuid(uid_t uid)
 {
-	return __rsyscall(SYS_setuid, uid, 0, 0, 0, 0, 0);
+	return __setxid(SYS_setuid, uid, 0, 0);
 }