init: don't kill(-1) if we aren't in a pid ns

...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>
This commit is contained in:
Tycho Andersen 2017-12-08 23:23:26 +00:00
parent 4be4832738
commit d76e3e1a5b

View 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,18 +338,28 @@ int main(int argc, char *argv[])
case SIGTERM:
if (!shutdown) {
shutdown = 1;
ret = kill(-1, SIGTERM);
if (ret < 0)
DEBUG("%s - Failed to send SIGTERM to "
"all children", strerror(errno));
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));
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));
}
break;
default:
ret = kill(pid, was_interrupted);