1

From man page of setrlimit, when RLIMIT_CPU limit is set, process should receive SIGXCPU every second once the soft limit is reached, and SIGKILL when the hard limit is reached.

I have the following code where the child process is created using clone() call. Then RLIMIT_CPU is set followed by long loop, which should be terminated via SIGKILL. This seems to work with SIGCHLD flag set. However, when I use SIGCHLD | CLONE_NEWPID flags, the loop doesn't terminate.

EDIT:

What happens on my machine: program writes pid 1 interrupted two times, and then execution freezes (loop is running and isn't killed) until loop is finished. This lasts about 10 seconds. When I remove CLONE_NEWPID program terminates after 3 seconds.

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sched.h>

int inside(void *arg)
{
    struct rlimit r = {1, 3};
    setrlimit(RLIMIT_CPU, &r);
    for (long long i = 0; i < 3e9; i++);
    return 0;
}

void handler(int signal)
{
    printf("pid %d interrupted\n", getpid());
    fflush(stdout);
}

void setup_signals(void)
{
    struct sigaction s;
    bzero(&s, sizeof s);
    s.sa_handler = handler;
    sigaction(SIGXCPU, &s, NULL);
}

int main(void)
{
    const int stack_size = 1 << 16;
    char stack[stack_size];
    setup_signals();
    int pid = clone(inside, stack + stack_size, SIGCHLD | CLONE_NEWPID, NULL);
    if (pid < 0) return 1;
    wait(NULL);
    return 0;
}

Why the child process is not killed (it gets killed if you remove CLONE_NEWPID)?

  • Are you sure this "long loop" is not just optimized away? – Eugene Sh. Feb 21 '18 at 17:47
  • You can compile with gcc and see output. Program is receiving `SIGXCPU` signals while the loop is running. – nikola12345 Feb 21 '18 at 17:49
  • @EugeneSh. Even if that's true, why would it work when he removes the `CLONE_NEWPID` flag? – Barmar Feb 21 '18 at 17:53
  • Works for me on Ubuntu 16.04, gcc 5.4.0, linux 4.4.0, both with and without CLONE_NEWPID. What distro/compiler/kernel are you using? Can you run `strace -f` and see if any signals at all are received by the child? – Mark Plotnick Feb 21 '18 at 17:54
  • @Barmar Dunno.. some different timings... It was just a guess to sanitize the input. – Eugene Sh. Feb 21 '18 at 17:57
  • @MarkPlotnick are you sure the process gets killed after 3 seconds of hard limit? – nikola12345 Feb 21 '18 at 18:06
  • Yes. `[pid 5647] clone(strace: Process 5648 attached child_stack=0x7ffcca542770, flags=CLONE_NEWPID|SIGCHLD) = 5648 [pid 5648] setrlimit(RLIMIT_CPU, {rlim_cur=1, rlim_max=3}) = 0 [pid 5648] --- SIGXCPU {si_signo=SIGXCPU, si_code=SI_KERNEL} --- [pid 5648] --- SIGXCPU {si_signo=SIGXCPU, si_code=SI_KERNEL} --- [pid 5648] +++ killed by SIGKILL +++ [pid 5647] <... wait4 resumed> NULL, 0, NULL) = 5648 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=5648, si_uid=0, si_status=SIGKILL, si_utime=299, si_stime=0} ---` – Mark Plotnick Feb 21 '18 at 18:14
  • @MarkPlotnick This means child is receiving `SIGXCPU`. Please see the edit. – nikola12345 Feb 21 '18 at 18:26
  • @MarkPlotnick Also when I run `strace` without `-f` flag it runs about 10 seconds, but when I run `strace -f` it runs 3 seconds. – nikola12345 Feb 21 '18 at 18:35
  • I can reproduce that. Without strace -f, mine runs for 4 seconds. I'll look into it some more. – Mark Plotnick Feb 21 '18 at 18:56
  • Minor nitpick: you cannot use printf() inside a signal handler, it is not async-safe. (it uses malloc, internally) – wildplasser Feb 21 '18 at 20:59
  • 1
    I tried this: have `inside` call clone again, `clone(..., SIGCHLD)`, and do the setrlimit and loop in that second task. And things work! So I looked for anything that might explain if init, even in a pid namespace, is treated specially. I found this stackoverflow question, [SIGKILL init process (PID 1)](https://stackoverflow.com/questions/21031537/sigkill-init-process-pid-1), which explains all we've seen. "the kernel will not deliver any fatal signal to init, and SIGKILL is not excepted. Applying ptrace(2) (the system call that strace uses) to process 1 apparently disables this protection." – Mark Plotnick Feb 22 '18 at 18:20
  • @MarkPlotnick Ok, this pretty much resolves the problem, although we do not know if this is intended behaviour with ptrace. Thanks! – nikola12345 Feb 22 '18 at 20:55
  • How is the program becoming PID 1? – Barmar Feb 23 '18 at 20:05
  • @Barmar CLONE_NEWPID – Mark Plotnick Feb 23 '18 at 23:44
  • @MarkPlotnick Ahh, that's a new feature to me, didn't know about process namespaces. – Barmar Feb 23 '18 at 23:46
  • loop is too short, incrementing integer 3e9 times is a very fast computation... – Jean-Baptiste Yunès Feb 24 '18 at 13:08

0 Answers0