diff options
| author | Rich Felker <dalias@aerifal.cx> | 2020-10-27 00:59:54 -0400 | 
|---|---|---|
| committer | Rich Felker <dalias@aerifal.cx> | 2020-10-27 00:59:54 -0400 | 
| commit | 4209a7b1048c2601be0dd91aeb9b9ed0a7447965 (patch) | |
| tree | f80de31d9c6f4979f73078b8b862871ce3b62424 | |
| parent | 6ce91ef0e8eddd756def4e7e5c47c639f45fcf5f (diff) | |
| download | musl-4209a7b1048c2601be0dd91aeb9b9ed0a7447965.tar.gz | |
fix setgroups behavior in multithreaded process
this function is outside the scope of the standards, but logically
should behave like the set*id functions whose effects are
process-global.
| -rw-r--r-- | src/linux/setgroups.c | 30 | 
1 files changed, 29 insertions, 1 deletions
| diff --git a/src/linux/setgroups.c b/src/linux/setgroups.c index 1248fdbf..47142f14 100644 --- a/src/linux/setgroups.c +++ b/src/linux/setgroups.c @@ -1,8 +1,36 @@  #define _GNU_SOURCE  #include <unistd.h> +#include <signal.h>  #include "syscall.h" +#include "libc.h" + +struct ctx { +	size_t count; +	const gid_t *list; +	int ret; +}; + +static void do_setgroups(void *p) +{ +	struct ctx *c = p; +	if (c->ret<0) return; +	int ret = __syscall(SYS_setgroups, c->count, c->list); +	if (ret && !c->ret) { +		/* If one thread fails to set groups after another has already +		 * succeeded, forcibly killing the process is the only safe +		 * thing to do. State is inconsistent and dangerous. Use +		 * SIGKILL because it is uncatchable. */ +		__block_all_sigs(0); +		__syscall(SYS_kill, __syscall(SYS_getpid), SIGKILL); +	} +	c->ret = ret; +}  int setgroups(size_t count, const gid_t list[])  { -	return syscall(SYS_setgroups, count, list); +	/* ret is initially nonzero so that failure of the first thread does not +	 * trigger the safety kill above. */ +	struct ctx c = { .count = count, .list = list, .ret = 1 }; +	__synccall(do_setgroups, &c); +	return __syscall_ret(c.ret);  } | 
