linuxsetuid

How is bash able to kill children processes with CTRL+C


I wrote a simple program as follows -

int main(int argc, char* argv[]) {
    setuid(0); 
    setgid(0);
    printf("Current uid and euid are %d, %d\n", getuid(), geteuid());
    while(1);
}

I compiled this as root and set the setuid bit using sudo chmod +s test.

When this program is run as a non-privileged user from bash, the program prints -

Current uid and euid are 0, 0

and then gets stuck in an infinite loop.

However I can still kill this process by pressing Crl+C. If I understand correctly, bash(running as a non-privileged user) should not be able to send SIGINT to a root process.

I also tried the same with kill <pid of test> and that fails as excepted.

How is bash able to kill the process? Is there a special relationship between the parent process and the child process?

I also tried this other wrapper program -

int main(int argc, char* argv[]) {
        pid_t p = fork();
        if (p == 0) {
                char * args[] = {"./test", NULL};
                execv("./test", args);
        } else {
                sleep(4);
                int ret = kill(p, 9);
                printf("Kill returned = %d\n", ret);
                return 0;
        }
}

And ran it as an unprivileged user (where test has setuid bit set by root). In this case the parent is not able to kill the child. the kill call returns -1 and the test process gets orphaned.

What is happening here? What does bash do special that it can kill the children processes it spawns?


Solution

  • Bash doesn't need any permissions because bash isn't doing anything. When you hit ^C, SIGINT is sent to all processes in the foreground process group by the tty driver. The signal comes from the system, not from another process, so the permission checks relevant to one process sending a signal to another don't apply.