Kill all process by UID

Is there any secure way to kill all processes with specified UID ?
Traditional way like

setuid(WANTED_UID);
kill(-1,SIGKILL);

is not secure, because this programm will receive signals between calling setuid and calling kill (so, any programm with WANTED_UID can kill this "killer-program", because we cannot catch SIGKILL from process we try to kill).
Scripts like ps axu|sed ...|xargs kill ... are not good, because programms with WANTED_UID can call fork() after calling "ps" and before calling "kill", so child process will not be killed.

Is there any secure way to kill processes by UID which guarantees dying of all processes with specified UID?

"killall" command has an option to kill by username. For example:

killall -9 -u mysql

However, you need to do some extra works to convert the UID to username.

Yes, but i do not know is it safe. I mean killall gets process list from kernel. While getting process list from kernel process_we_want_to_kill can call fork and some processes may be stay alive (will not be killed, because new process will be created after/during getting process list in kill). I have not read source code of killall (and there are no killall in OpenBSD), but is it safe ? Will it kill really all processes without any "bugs", or there are any "features" with killing new processes which starts during killing process?

Yes, you're right. Your mentioned situation may still exist as "killall" can't block the processes from forking. :frowning:

What are you trying to accomplish or prevent?

As far as killing all processes with a given euid or ruid, for that OpenBSD provides pkill.

Yes, there's a race between the time the list of matching processes is retrieved (using the kernel virtual memory library's kvm_getprocs() interface) and the time pkill sends the processes a signal, during which a process can fork or change uid.

I suppose you can run pkill multiple times until it returns an exit status of 1 (no processes match the criteria).

Again, more info about the situation will probably help us help you.

Regards,
Alister

I'm writting programm to test students programms (user sends source code via web, server compiles it, run it with some tests). I'm trying to prevent unkillable programms when user's programm forks a lot of times (forks will be limited by OS, but user limit of processes > 1), theoreticaly programm can fork in "bad moment" and can be unkillable. Starting pkill lots of time - it is not a beautiful solution (i can write kernel patch to deny fork() - it guarantees that all processes will be killed), i'm trying to find "beautiful" solution without patching kernel and it will be better if it will be cross-platform solution. Running pkill multiple times - practicaly normal solution, but for me it looks like Achilles' heel.

You haven't provided any information regarding the ruid, euid, and suid of killer-program nor of its victims. However, it's possible that the only reason that killer-program is vulnerable is because of the setuid() call you're using.

A process p cannot send a signal to a process q unless p's real uid or effective uid matches either q's real uid or saved set uid.

Assuming that killer-program is privileged and starts with ruid==euid==suid==0, setuid(WANTED_GUID) will set them all to WANTED_GUID.

Assuming that the victims are running with ruid==euid==suid==WANTED_GUID, the victims can now kill killer-program because killer-programs ruid and/or suid matches victims' ruid and/or euid.

However, if instead you only modified killer-program's credentials so that ruid==suid==0 and euid==WANTED_GUID, the victims could not kill killer-program, since the victims' ruid and/or euid does not match killer-program's ruid and/or suid.

In short, if the assumptions are correct, all you need is to use seteuid instead of setuid.

If the uid assumptions are incorrect, then please be more specific.

Regards,
Alister

You are right, using seteuid will work, but there is a problem with sending signals: kill(-1,SIGKILL) will kill all processes, not only processes with uid=killer's_euid. Killer process has uid=0, so kill() will kill anything.