RFC: Enforcing process hierarchies (`prctl` related)

3 messages, 2 authors, 2012-08-25 · open the first message on its own page

RFC: Enforcing process hierarchies (`prctl` related)

From: Ciprian Dorin Craciun <hidden>
Date: 2012-08-24 16:20:57

    Hello all! I'm about to write quite a long-ish email, but I
provide a small summary first for the "busy". :)


== Quick summary

    The context is simple:
    * the developer wants to write a C application (call it a
"controller") that spawns other child processes, which in their turn
spawn other processes and so on;
    * some of these spawned processes might double fork (i.e. they daemonize);
    * these child processes are not written by the developer in
question; (thus we can't "fix" them...)
    * the controller will run with no special privileges (i.e.
non-root, no capabilities, etc.);
    * (for purely practical reasons, thus let's not start a flame war)
we can assume a Linux-only environment, thus portability is not an
issue; (the developer is also not scared in applying patches to the
kernel...)

    The problem I try to find a solution to is again quite simple, how
can the developer write the "controller" application such that the
following happens:
    (A) any orphaned child of a sub-process (i.e. double forking ones,
ones whose parent has exited, etc.) are reparented to the controller;
(this is already solved in 3.4;)
    (B) the controller should be able to kill all its subprocesses
(and their subprocesses, recursively);
    (C) if the controller dies (segfault, killed, etc.) all its
subprocesses are terminated (and recursively so);

    Thus, what "magic" :) should one invoke to obtain the above?


== Use cases

    To better put the problem into a context, the previous features
would allow one to implement something like:
    * "personal" service control systems (like `systemd`, `runit`,
`daemon-tools`, etc.);
    * constrain badly written `sh` scripts that if terminated leak
processes; or build systems (like `make`), or SSH sessions, or cron
jobs, etc.;
    * a building block in a PaaS underlaying;

    But certainly NOT for "security" purposes...


== Existing primitives

    I've really tried thinking about this for the last year or so, and
I've not managed to get a "good" enough solution... But I've found the
following partial solutions, but all of them have issues...

    (1) `clone (CLONE_NEWPID)` -> solves all the problems, but has
major drawbacks:
    * it requires special rights (root or capability);
    * it creates an alternative process namespace thus will confuse
the hell out of scripts assuming PID equivalence...

    (2) `prctl (PR_SET_CHILD_SUBREAPER)` -> solves the issue (A)
(reparenting), but that's it.

    (3) `prctl (PR_SET_PDEATHSIG)` -> solves the issue (C) (forced
termination), but it has the following issues:
    * it works only for the first level of sub-processes, as it must
be explicitly set after each `fork`;
    * thus it is not "inheritable" by new children;
    * it doesn't work for `setuid` processes (due to possible security issue);

    (4) `killpg (group)` -> solves the issue (B), but:
    * any child could choose to change its process group;
    * if the controller uses its own group it kills itself;

    Have I missed something?


== Brainstorming

    It would have been nice if either of the following would happen
(in order of preference):

    (a) maybe an additional `PR_SET_CHILD_SUBREAPER`-like flag that
would make the controller process really behave like a sub-init, that
is on its termination terminate all its children recursively;

    (b) `PR_SET_PDEATHSIG` would be inheritable at least for non
`setuid` processes; (maybe even better if the signal is `SIGKILL` keep
it even for `setuid` processes as I don't see too much harm there...)

    (c) have some way to send a signal to all a process children;

    (d) (last resort solution we have today) iterate through
`/proc/$PID` and find all the controller children, SIGKILL them, and
continue until no process is found, then die; but it doesn't solve
(C)...


    Something else?

    Hope I find some solution to this... (If this is not the mailing
list to send it to, please point me in the right direction.)

    Thanks in advance for feedback,
    Ciprian.

RFC: Enforcing process hierarchies (`prctl` related)

From: Mulyadi Santosa <hidden>
Date: 2012-08-25 17:48:50

Hi........

On Fri, Aug 24, 2012 at 11:20 PM, Ciprian Dorin Craciun
[off-list ref] wrote:
    (d) (last resort solution we have today) iterate through
`/proc/$PID` and find all the controller children, SIGKILL them, and
continue until no process is found, then die; but it doesn't solve
(C)...
IMHO this is the way to do it....but, to tackle C, maybe a signal
handler to SIGTERM could do it. But wait, I forget whether SIGTERM
could be intercepted.....




-- 
regards,

Mulyadi Santosa
Freelance Linux trainer and consultant

blog: the-hydra.blogspot.com
training: mulyaditraining.blogspot.com

RFC: Enforcing process hierarchies (`prctl` related)

From: Ciprian Dorin Craciun <hidden>
Date: 2012-08-25 18:08:14

On Sat, Aug 25, 2012 at 8:48 PM, Mulyadi Santosa
[off-list ref] wrote:
Hi........

On Fri, Aug 24, 2012 at 11:20 PM, Ciprian Dorin Craciun
[off-list ref] wrote:
quoted
    (d) (last resort solution we have today) iterate through
`/proc/$PID` and find all the controller children, SIGKILL them, and
continue until no process is found, then die; but it doesn't solve
(C)...
IMHO this is the way to do it....but, to tackle C, maybe a signal
handler to SIGTERM could do it. But wait, I forget whether SIGTERM
could be intercepted.....
    Solving (C) (that is terminating all children if the controller
suddenly dies) is impossible to achieve from the controller process
itself as if it dies it can't proceed to killing the children
processes, thus they are leaking to the next process reaper (most
probably `init` itself). (The only way to solve this is to use the
`clone (CLONE_NEWPID)`, but as said you need special privileges to
call it...)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help