]> git.proxmox.com Git - mirror_lxc.git/commitdiff
init: don't kill(-1) if we aren't in a pid ns
authorTycho Andersen <tycho@tycho.ws>
Fri, 8 Dec 2017 23:23:26 +0000 (23:23 +0000)
committerTycho Andersen <tycho@tycho.ws>
Thu, 14 Dec 2017 00:26:02 +0000 (00:26 +0000)
...otherwise we'll kill everyone on the machine. Instead, let's explicitly
try to kill our children. Let's do a best effort against fork bombs by
disabling forking via the pids cgroup if it exists. This is best effort for
a number of reasons:

* the pids cgroup may not be available
* the container may have bind mounted /dev/null over pids.max, so the write
  doesn't do anything

Signed-off-by: Tycho Andersen <tycho@tycho.ws>
src/lxc/lxc_init.c

index f2011f9fbfe7c281cb4802460b4f0dd77c1c2aef..bcdfcc38924a45b3351c617beaa7bce5fd7fe061 100644 (file)
@@ -92,6 +92,86 @@ static struct arguments my_args = {
        .shortopts = short_options
 };
 
+static void prevent_forking(void)
+{
+       FILE *f;
+       char name[PATH_MAX], path[PATH_MAX];
+       int ret;
+
+       f = fopen("/proc/self/cgroup", "r");
+       if (!f) {
+               SYSERROR("opening /proc/self/cgroup");
+               return;
+       }
+
+       while (!feof(f)) {
+               int fd;
+
+               if (2 != fscanf(f, "%*d:%[^:]:%s", name, path)) {
+                       ERROR("didn't scan the right number of things");
+                       goto out;
+               }
+
+               if (strcmp(name, "pids"))
+                       continue;
+
+               ret = snprintf(name, sizeof(name), "/sys/fs/cgroup/pids/%s/pids.max", path);
+               if (ret < 0 || ret >= sizeof(path)) {
+                       ERROR("failed snprintf");
+                       goto out;
+               }
+
+               fd = open(name, O_WRONLY);
+               if (fd < 0) {
+                       SYSERROR("open");
+                       goto out;
+               }
+
+               if (write(fd, "1", 1) != 1)
+                       SYSERROR("write");
+
+               close(fd);
+               break;
+       }
+
+out:
+       fclose(f);
+}
+
+static void kill_children(pid_t pid)
+{
+       FILE *f;
+       char path[PATH_MAX];
+       int ret;
+
+       ret = snprintf(path, sizeof(path), "/proc/%d/task/%d/children", pid, pid);
+       if (ret < 0 || ret >= sizeof(path)) {
+               ERROR("failed snprintf");
+               return;
+       }
+
+       f = fopen(path, "r");
+       if (!f) {
+               SYSERROR("couldn't open %s", path);
+               return;
+       }
+
+       while (!feof(f)) {
+               pid_t pid;
+
+               if (fscanf(f, "%d ", &pid) != 1) {
+                       ERROR("couldn't scan pid");
+                       fclose(f);
+                       return;
+               }
+
+               kill_children(pid);
+               kill(pid, SIGKILL);
+       }
+
+       fclose(f);
+}
+
 int main(int argc, char *argv[])
 {
        int i, ret;
@@ -258,19 +338,29 @@ int main(int argc, char *argv[])
                case SIGTERM:
                        if (!shutdown) {
                                shutdown = 1;
+                               prevent_forking();
+                               if (getpid() != 1) {
+                                       kill_children(getpid());
+                               } else {
+                                       ret = kill(-1, SIGTERM);
+                                       if (ret < 0)
+                                               DEBUG("%s - Failed to send SIGTERM to "
+                                                     "all children", strerror(errno));
+                               }
+                               alarm(1);
+                       }
+                       break;
+               case SIGALRM:
+                       prevent_forking();
+                       if (getpid() != 1) {
+                               kill_children(getpid());
+                       } else {
                                ret = kill(-1, SIGTERM);
                                if (ret < 0)
                                        DEBUG("%s - Failed to send SIGTERM to "
                                              "all children", strerror(errno));
-                               alarm(1);
                        }
                        break;
-               case SIGALRM:
-                       ret = kill(-1, SIGKILL);
-                       if (ret < 0)
-                               DEBUG("%s - Failed to send SIGKILL to all "
-                                     "children", strerror(errno));
-                       break;
                default:
                        ret = kill(pid, was_interrupted);
                        if (ret < 0)