2019-05-19 20:08:55 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2008-02-08 20:18:24 +08:00
|
|
|
/*
|
|
|
|
* Pid namespaces
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* (C) 2007 Pavel Emelyanov <xemul@openvz.org>, OpenVZ, SWsoft Inc.
|
|
|
|
* (C) 2007 Sukadev Bhattiprolu <sukadev@us.ibm.com>, IBM
|
|
|
|
* Many thanks to Oleg Nesterov for comments and help
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/pid.h>
|
|
|
|
#include <linux/pid_namespace.h>
|
2012-08-02 19:25:10 +08:00
|
|
|
#include <linux/user_namespace.h>
|
2008-02-08 20:18:24 +08:00
|
|
|
#include <linux/syscalls.h>
|
2017-02-03 00:54:15 +08:00
|
|
|
#include <linux/cred.h>
|
2008-02-08 20:18:24 +08:00
|
|
|
#include <linux/err.h>
|
2008-07-25 16:48:47 +08:00
|
|
|
#include <linux/acct.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2013-04-12 08:50:06 +08:00
|
|
|
#include <linux/proc_ns.h>
|
pidns: add reboot_pid_ns() to handle the reboot syscall
In the case of a child pid namespace, rebooting the system does not really
makes sense. When the pid namespace is used in conjunction with the other
namespaces in order to create a linux container, the reboot syscall leads
to some problems.
A container can reboot the host. That can be fixed by dropping the
sys_reboot capability but we are unable to correctly to poweroff/
halt/reboot a container and the container stays stuck at the shutdown time
with the container's init process waiting indefinitively.
After several attempts, no solution from userspace was found to reliabily
handle the shutdown from a container.
This patch propose to make the init process of the child pid namespace to
exit with a signal status set to : SIGINT if the child pid namespace
called "halt/poweroff" and SIGHUP if the child pid namespace called
"reboot". When the reboot syscall is called and we are not in the initial
pid namespace, we kill the pid namespace for "HALT", "POWEROFF",
"RESTART", and "RESTART2". Otherwise we return EINVAL.
Returning EINVAL is also an easy way to check if this feature is supported
by the kernel when invoking another 'reboot' option like CAD.
By this way the parent process of the child pid namespace knows if it
rebooted or not and can take the right decision.
Test case:
==========
#include <alloca.h>
#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <signal.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/reboot.h>
static int do_reboot(void *arg)
{
int *cmd = arg;
if (reboot(*cmd))
printf("failed to reboot(%d): %m\n", *cmd);
}
int test_reboot(int cmd, int sig)
{
long stack_size = 4096;
void *stack = alloca(stack_size) + stack_size;
int status;
pid_t ret;
ret = clone(do_reboot, stack, CLONE_NEWPID | SIGCHLD, &cmd);
if (ret < 0) {
printf("failed to clone: %m\n");
return -1;
}
if (wait(&status) < 0) {
printf("unexpected wait error: %m\n");
return -1;
}
if (!WIFSIGNALED(status)) {
printf("child process exited but was not signaled\n");
return -1;
}
if (WTERMSIG(status) != sig) {
printf("signal termination is not the one expected\n");
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int status;
status = test_reboot(LINUX_REBOOT_CMD_RESTART, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_RESTART2, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART2) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_HALT, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_HALT) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_POWER_OFF, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_POWERR_OFF) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_CAD_ON, -1);
if (status >= 0) {
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) should have failed\n");
return 1;
}
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) has failed as expected\n");
return 0;
}
[akpm@linux-foundation.org: tweak and add comments]
[akpm@linux-foundation.org: checkpatch fixes]
Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Tested-by: Serge Hallyn <serge.hallyn@canonical.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-29 05:42:51 +08:00
|
|
|
#include <linux/reboot.h>
|
2012-08-04 10:11:22 +08:00
|
|
|
#include <linux/export.h>
|
2017-02-09 01:51:36 +08:00
|
|
|
#include <linux/sched/task.h>
|
2017-02-04 06:47:37 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2017-11-18 07:30:30 +08:00
|
|
|
#include <linux/idr.h>
|
2008-02-08 20:18:24 +08:00
|
|
|
|
|
|
|
static DEFINE_MUTEX(pid_caches_mutex);
|
|
|
|
static struct kmem_cache *pid_ns_cachep;
|
2018-03-21 02:51:06 +08:00
|
|
|
/* Write once array, filled from the beginning. */
|
|
|
|
static struct kmem_cache *pid_cache[MAX_PID_NS_LEVEL];
|
2008-02-08 20:18:24 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* creates the kmem cache to allocate pids from.
|
2018-03-21 02:51:06 +08:00
|
|
|
* @level: pid namespace level
|
2008-02-08 20:18:24 +08:00
|
|
|
*/
|
|
|
|
|
2018-03-21 02:51:06 +08:00
|
|
|
static struct kmem_cache *create_pid_cachep(unsigned int level)
|
2008-02-08 20:18:24 +08:00
|
|
|
{
|
2018-03-21 02:51:06 +08:00
|
|
|
/* Level 0 is init_pid_ns.pid_cachep */
|
|
|
|
struct kmem_cache **pkc = &pid_cache[level - 1];
|
|
|
|
struct kmem_cache *kc;
|
|
|
|
char name[4 + 10 + 1];
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
kc = READ_ONCE(*pkc);
|
|
|
|
if (kc)
|
|
|
|
return kc;
|
|
|
|
|
|
|
|
snprintf(name, sizeof(name), "pid_%u", level + 1);
|
|
|
|
len = sizeof(struct pid) + level * sizeof(struct upid);
|
2008-02-08 20:18:24 +08:00
|
|
|
mutex_lock(&pid_caches_mutex);
|
2018-03-21 02:51:06 +08:00
|
|
|
/* Name collision forces to do allocation under mutex. */
|
|
|
|
if (!*pkc)
|
|
|
|
*pkc = kmem_cache_create(name, len, 0, SLAB_HWCACHE_ALIGN, 0);
|
2008-02-08 20:18:24 +08:00
|
|
|
mutex_unlock(&pid_caches_mutex);
|
2018-03-21 02:51:06 +08:00
|
|
|
/* current can fail, but someone else can succeed. */
|
|
|
|
return READ_ONCE(*pkc);
|
2008-02-08 20:18:24 +08:00
|
|
|
}
|
|
|
|
|
2016-08-09 03:08:36 +08:00
|
|
|
static struct ucounts *inc_pid_namespaces(struct user_namespace *ns)
|
|
|
|
{
|
|
|
|
return inc_ucount(ns, current_euid(), UCOUNT_PID_NAMESPACES);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dec_pid_namespaces(struct ucounts *ucounts)
|
|
|
|
{
|
|
|
|
dec_ucount(ucounts, UCOUNT_PID_NAMESPACES);
|
|
|
|
}
|
|
|
|
|
2012-08-02 19:25:10 +08:00
|
|
|
static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns,
|
|
|
|
struct pid_namespace *parent_pid_ns)
|
2008-02-08 20:18:24 +08:00
|
|
|
{
|
|
|
|
struct pid_namespace *ns;
|
2009-06-18 07:27:52 +08:00
|
|
|
unsigned int level = parent_pid_ns->level + 1;
|
2016-08-09 03:08:36 +08:00
|
|
|
struct ucounts *ucounts;
|
pidns: limit the nesting depth of pid namespaces
'struct pid' is a "variable sized struct" - a header with an array of
upids at the end.
The size of the array depends on a level (depth) of pid namespaces. Now a
level of pidns is not limited, so 'struct pid' can be more than one page.
Looks reasonable, that it should be less than a page. MAX_PIS_NS_LEVEL is
not calculated from PAGE_SIZE, because in this case it depends on
architectures, config options and it will be reduced, if someone adds a
new fields in struct pid or struct upid.
I suggest to set MAX_PIS_NS_LEVEL = 32, because it saves ability to expand
"struct pid" and it's more than enough for all known for me use-cases.
When someone finds a reasonable use case, we can add a config option or a
sysctl parameter.
In addition it will reduce the effect of another problem, when we have
many nested namespaces and the oldest one starts dying.
zap_pid_ns_processe will be called for each namespace and find_vpid will
be called for each process in a namespace. find_vpid will be called
minimum max_level^2 / 2 times. The reason of that is that when we found a
bit in pidmap, we can't determine this pidns is top for this process or it
isn't.
vpid is a heavy operation, so a fork bomb, which create many nested
namespace, can make a system inaccessible for a long time. For example my
system becomes inaccessible for a few minutes with 4000 processes.
[akpm@linux-foundation.org: return -EINVAL in response to excessive nesting, not -ENOMEM]
Signed-off-by: Andrew Vagin <avagin@openvz.org>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-26 04:38:07 +08:00
|
|
|
int err;
|
|
|
|
|
2017-04-30 03:12:15 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
if (!in_userns(parent_pid_ns->user_ns, user_ns))
|
|
|
|
goto out;
|
|
|
|
|
2016-09-23 02:08:36 +08:00
|
|
|
err = -ENOSPC;
|
2016-08-09 03:08:36 +08:00
|
|
|
if (level > MAX_PID_NS_LEVEL)
|
|
|
|
goto out;
|
|
|
|
ucounts = inc_pid_namespaces(user_ns);
|
|
|
|
if (!ucounts)
|
pidns: limit the nesting depth of pid namespaces
'struct pid' is a "variable sized struct" - a header with an array of
upids at the end.
The size of the array depends on a level (depth) of pid namespaces. Now a
level of pidns is not limited, so 'struct pid' can be more than one page.
Looks reasonable, that it should be less than a page. MAX_PIS_NS_LEVEL is
not calculated from PAGE_SIZE, because in this case it depends on
architectures, config options and it will be reduced, if someone adds a
new fields in struct pid or struct upid.
I suggest to set MAX_PIS_NS_LEVEL = 32, because it saves ability to expand
"struct pid" and it's more than enough for all known for me use-cases.
When someone finds a reasonable use case, we can add a config option or a
sysctl parameter.
In addition it will reduce the effect of another problem, when we have
many nested namespaces and the oldest one starts dying.
zap_pid_ns_processe will be called for each namespace and find_vpid will
be called for each process in a namespace. find_vpid will be called
minimum max_level^2 / 2 times. The reason of that is that when we found a
bit in pidmap, we can't determine this pidns is top for this process or it
isn't.
vpid is a heavy operation, so a fork bomb, which create many nested
namespace, can make a system inaccessible for a long time. For example my
system becomes inaccessible for a few minutes with 4000 processes.
[akpm@linux-foundation.org: return -EINVAL in response to excessive nesting, not -ENOMEM]
Signed-off-by: Andrew Vagin <avagin@openvz.org>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-26 04:38:07 +08:00
|
|
|
goto out;
|
2008-02-08 20:18:24 +08:00
|
|
|
|
pidns: limit the nesting depth of pid namespaces
'struct pid' is a "variable sized struct" - a header with an array of
upids at the end.
The size of the array depends on a level (depth) of pid namespaces. Now a
level of pidns is not limited, so 'struct pid' can be more than one page.
Looks reasonable, that it should be less than a page. MAX_PIS_NS_LEVEL is
not calculated from PAGE_SIZE, because in this case it depends on
architectures, config options and it will be reduced, if someone adds a
new fields in struct pid or struct upid.
I suggest to set MAX_PIS_NS_LEVEL = 32, because it saves ability to expand
"struct pid" and it's more than enough for all known for me use-cases.
When someone finds a reasonable use case, we can add a config option or a
sysctl parameter.
In addition it will reduce the effect of another problem, when we have
many nested namespaces and the oldest one starts dying.
zap_pid_ns_processe will be called for each namespace and find_vpid will
be called for each process in a namespace. find_vpid will be called
minimum max_level^2 / 2 times. The reason of that is that when we found a
bit in pidmap, we can't determine this pidns is top for this process or it
isn't.
vpid is a heavy operation, so a fork bomb, which create many nested
namespace, can make a system inaccessible for a long time. For example my
system becomes inaccessible for a few minutes with 4000 processes.
[akpm@linux-foundation.org: return -EINVAL in response to excessive nesting, not -ENOMEM]
Signed-off-by: Andrew Vagin <avagin@openvz.org>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-10-26 04:38:07 +08:00
|
|
|
err = -ENOMEM;
|
2008-07-25 16:48:42 +08:00
|
|
|
ns = kmem_cache_zalloc(pid_ns_cachep, GFP_KERNEL);
|
2008-02-08 20:18:24 +08:00
|
|
|
if (ns == NULL)
|
2016-08-09 03:08:36 +08:00
|
|
|
goto out_dec;
|
2008-02-08 20:18:24 +08:00
|
|
|
|
2017-11-18 07:30:30 +08:00
|
|
|
idr_init(&ns->idr);
|
2008-02-08 20:18:24 +08:00
|
|
|
|
2018-03-21 02:51:06 +08:00
|
|
|
ns->pid_cachep = create_pid_cachep(level);
|
2008-02-08 20:18:24 +08:00
|
|
|
if (ns->pid_cachep == NULL)
|
2017-11-18 07:30:30 +08:00
|
|
|
goto out_free_idr;
|
2008-02-08 20:18:24 +08:00
|
|
|
|
2014-11-01 12:45:45 +08:00
|
|
|
err = ns_alloc_inum(&ns->ns);
|
2011-06-16 01:21:48 +08:00
|
|
|
if (err)
|
2017-11-18 07:30:30 +08:00
|
|
|
goto out_free_idr;
|
2014-11-01 14:32:53 +08:00
|
|
|
ns->ns.ops = &pidns_operations;
|
2011-06-16 01:21:48 +08:00
|
|
|
|
2020-08-03 18:16:32 +08:00
|
|
|
refcount_set(&ns->ns.count, 1);
|
2008-02-08 20:18:24 +08:00
|
|
|
ns->level = level;
|
2009-06-18 07:27:52 +08:00
|
|
|
ns->parent = get_pid_ns(parent_pid_ns);
|
2012-08-02 19:25:10 +08:00
|
|
|
ns->user_ns = get_user_ns(user_ns);
|
2016-08-09 03:08:36 +08:00
|
|
|
ns->ucounts = ucounts;
|
2017-11-18 07:30:34 +08:00
|
|
|
ns->pid_allocated = PIDNS_ADDING;
|
2008-02-08 20:18:24 +08:00
|
|
|
|
|
|
|
return ns;
|
|
|
|
|
2017-11-18 07:30:30 +08:00
|
|
|
out_free_idr:
|
|
|
|
idr_destroy(&ns->idr);
|
2008-02-08 20:18:24 +08:00
|
|
|
kmem_cache_free(pid_ns_cachep, ns);
|
2016-08-09 03:08:36 +08:00
|
|
|
out_dec:
|
|
|
|
dec_pid_namespaces(ucounts);
|
2008-02-08 20:18:24 +08:00
|
|
|
out:
|
2011-03-24 07:43:13 +08:00
|
|
|
return ERR_PTR(err);
|
2008-02-08 20:18:24 +08:00
|
|
|
}
|
|
|
|
|
2013-10-04 01:28:06 +08:00
|
|
|
static void delayed_free_pidns(struct rcu_head *p)
|
|
|
|
{
|
2017-01-05 11:28:14 +08:00
|
|
|
struct pid_namespace *ns = container_of(p, struct pid_namespace, rcu);
|
|
|
|
|
|
|
|
dec_pid_namespaces(ns->ucounts);
|
|
|
|
put_user_ns(ns->user_ns);
|
|
|
|
|
|
|
|
kmem_cache_free(pid_ns_cachep, ns);
|
2013-10-04 01:28:06 +08:00
|
|
|
}
|
|
|
|
|
2008-02-08 20:18:24 +08:00
|
|
|
static void destroy_pid_namespace(struct pid_namespace *ns)
|
|
|
|
{
|
2014-11-01 12:45:45 +08:00
|
|
|
ns_free_inum(&ns->ns);
|
2017-11-18 07:30:30 +08:00
|
|
|
|
|
|
|
idr_destroy(&ns->idr);
|
2013-10-04 01:28:06 +08:00
|
|
|
call_rcu(&ns->rcu, delayed_free_pidns);
|
2008-02-08 20:18:24 +08:00
|
|
|
}
|
|
|
|
|
2012-08-02 19:25:10 +08:00
|
|
|
struct pid_namespace *copy_pid_ns(unsigned long flags,
|
|
|
|
struct user_namespace *user_ns, struct pid_namespace *old_ns)
|
2008-02-08 20:18:24 +08:00
|
|
|
{
|
|
|
|
if (!(flags & CLONE_NEWPID))
|
2009-06-18 07:27:53 +08:00
|
|
|
return get_pid_ns(old_ns);
|
2012-08-02 23:35:35 +08:00
|
|
|
if (task_active_pid_ns(current) != old_ns)
|
|
|
|
return ERR_PTR(-EINVAL);
|
2012-08-02 19:25:10 +08:00
|
|
|
return create_pid_namespace(user_ns, old_ns);
|
2008-02-08 20:18:24 +08:00
|
|
|
}
|
|
|
|
|
2012-10-20 04:56:53 +08:00
|
|
|
void put_pid_ns(struct pid_namespace *ns)
|
|
|
|
{
|
|
|
|
struct pid_namespace *parent;
|
|
|
|
|
|
|
|
while (ns != &init_pid_ns) {
|
|
|
|
parent = ns->parent;
|
2020-08-03 18:16:32 +08:00
|
|
|
if (!refcount_dec_and_test(&ns->ns.count))
|
2012-10-20 04:56:53 +08:00
|
|
|
break;
|
2020-08-03 18:16:32 +08:00
|
|
|
destroy_pid_namespace(ns);
|
2012-10-20 04:56:53 +08:00
|
|
|
ns = parent;
|
|
|
|
}
|
2008-02-08 20:18:24 +08:00
|
|
|
}
|
2012-10-20 04:56:53 +08:00
|
|
|
EXPORT_SYMBOL_GPL(put_pid_ns);
|
2008-02-08 20:18:24 +08:00
|
|
|
|
|
|
|
void zap_pid_ns_processes(struct pid_namespace *pid_ns)
|
|
|
|
{
|
|
|
|
int nr;
|
|
|
|
int rc;
|
2012-06-01 07:26:40 +08:00
|
|
|
struct task_struct *task, *me = current;
|
2013-03-26 17:27:11 +08:00
|
|
|
int init_pids = thread_group_leader(me) ? 1 : 2;
|
2017-11-18 07:30:30 +08:00
|
|
|
struct pid *pid;
|
2012-06-01 07:26:40 +08:00
|
|
|
|
2012-12-22 12:27:12 +08:00
|
|
|
/* Don't allow any more processes into the pid namespace */
|
|
|
|
disable_pid_allocation(pid_ns);
|
|
|
|
|
2014-12-11 07:55:28 +08:00
|
|
|
/*
|
|
|
|
* Ignore SIGCHLD causing any terminated children to autoreap.
|
|
|
|
* This speeds up the namespace shutdown, plus see the comment
|
|
|
|
* below.
|
|
|
|
*/
|
2012-06-01 07:26:40 +08:00
|
|
|
spin_lock_irq(&me->sighand->siglock);
|
|
|
|
me->sighand->action[SIGCHLD - 1].sa.sa_handler = SIG_IGN;
|
|
|
|
spin_unlock_irq(&me->sighand->siglock);
|
2008-02-08 20:18:24 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The last thread in the cgroup-init thread group is terminating.
|
|
|
|
* Find remaining pid_ts in the namespace, signal and wait for them
|
|
|
|
* to exit.
|
|
|
|
*
|
|
|
|
* Note: This signals each threads in the namespace - even those that
|
|
|
|
* belong to the same thread group, To avoid this, we would have
|
|
|
|
* to walk the entire tasklist looking a processes in this
|
|
|
|
* namespace, but that could be unnecessarily expensive if the
|
|
|
|
* pid namespace has just a few processes. Or we need to
|
|
|
|
* maintain a tasklist for each pid namespace.
|
|
|
|
*
|
|
|
|
*/
|
2017-11-18 07:30:30 +08:00
|
|
|
rcu_read_lock();
|
2008-02-08 20:18:24 +08:00
|
|
|
read_lock(&tasklist_lock);
|
2017-11-18 07:30:30 +08:00
|
|
|
nr = 2;
|
|
|
|
idr_for_each_entry_continue(&pid_ns->idr, pid, nr) {
|
|
|
|
task = pid_task(pid, PIDTYPE_PID);
|
2012-03-24 06:02:46 +08:00
|
|
|
if (task && !__fatal_signal_pending(task))
|
2018-07-21 05:35:14 +08:00
|
|
|
group_send_sig_info(SIGKILL, SEND_SIG_PRIV, task, PIDTYPE_MAX);
|
2008-02-08 20:18:24 +08:00
|
|
|
}
|
|
|
|
read_unlock(&tasklist_lock);
|
2017-11-18 07:30:30 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-08 20:18:24 +08:00
|
|
|
|
2014-12-11 07:55:28 +08:00
|
|
|
/*
|
|
|
|
* Reap the EXIT_ZOMBIE children we had before we ignored SIGCHLD.
|
2018-03-11 18:34:26 +08:00
|
|
|
* kernel_wait4() will also block until our children traced from the
|
2014-12-11 07:55:28 +08:00
|
|
|
* parent namespace are detached and become EXIT_DEAD.
|
|
|
|
*/
|
2008-02-08 20:18:24 +08:00
|
|
|
do {
|
|
|
|
clear_thread_flag(TIF_SIGPENDING);
|
2018-03-11 18:34:26 +08:00
|
|
|
rc = kernel_wait4(-1, NULL, __WALL, NULL);
|
2008-02-08 20:18:24 +08:00
|
|
|
} while (rc != -ECHILD);
|
|
|
|
|
2012-06-21 03:53:03 +08:00
|
|
|
/*
|
2020-02-29 06:29:12 +08:00
|
|
|
* kernel_wait4() misses EXIT_DEAD children, and EXIT_ZOMBIE
|
|
|
|
* process whose parents processes are outside of the pid
|
|
|
|
* namespace. Such processes are created with setns()+fork().
|
2014-12-11 07:55:28 +08:00
|
|
|
*
|
2020-02-29 06:29:12 +08:00
|
|
|
* If those EXIT_ZOMBIE processes are not reaped by their
|
|
|
|
* parents before their parents exit, they will be reparented
|
|
|
|
* to pid_ns->child_reaper. Thus pidns->child_reaper needs to
|
|
|
|
* stay valid until they all go away.
|
2014-12-11 07:55:28 +08:00
|
|
|
*
|
2020-10-16 11:10:28 +08:00
|
|
|
* The code relies on the pid_ns->child_reaper ignoring
|
2020-02-29 06:29:12 +08:00
|
|
|
* SIGCHILD to cause those EXIT_ZOMBIE processes to be
|
|
|
|
* autoreaped if reparented.
|
|
|
|
*
|
|
|
|
* Semantically it is also desirable to wait for EXIT_ZOMBIE
|
|
|
|
* processes before allowing the child_reaper to be reaped, as
|
|
|
|
* that gives the invariant that when the init process of a
|
|
|
|
* pid namespace is reaped all of the processes in the pid
|
|
|
|
* namespace are gone.
|
|
|
|
*
|
|
|
|
* Once all of the other tasks are gone from the pid_namespace
|
|
|
|
* free_pid() will awaken this task.
|
2012-06-21 03:53:03 +08:00
|
|
|
*/
|
|
|
|
for (;;) {
|
2017-05-12 07:21:01 +08:00
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2017-11-18 07:30:34 +08:00
|
|
|
if (pid_ns->pid_allocated == init_pids)
|
2012-06-21 03:53:03 +08:00
|
|
|
break;
|
|
|
|
schedule();
|
|
|
|
}
|
2012-08-02 06:03:42 +08:00
|
|
|
__set_current_state(TASK_RUNNING);
|
2012-06-21 03:53:03 +08:00
|
|
|
|
pidns: add reboot_pid_ns() to handle the reboot syscall
In the case of a child pid namespace, rebooting the system does not really
makes sense. When the pid namespace is used in conjunction with the other
namespaces in order to create a linux container, the reboot syscall leads
to some problems.
A container can reboot the host. That can be fixed by dropping the
sys_reboot capability but we are unable to correctly to poweroff/
halt/reboot a container and the container stays stuck at the shutdown time
with the container's init process waiting indefinitively.
After several attempts, no solution from userspace was found to reliabily
handle the shutdown from a container.
This patch propose to make the init process of the child pid namespace to
exit with a signal status set to : SIGINT if the child pid namespace
called "halt/poweroff" and SIGHUP if the child pid namespace called
"reboot". When the reboot syscall is called and we are not in the initial
pid namespace, we kill the pid namespace for "HALT", "POWEROFF",
"RESTART", and "RESTART2". Otherwise we return EINVAL.
Returning EINVAL is also an easy way to check if this feature is supported
by the kernel when invoking another 'reboot' option like CAD.
By this way the parent process of the child pid namespace knows if it
rebooted or not and can take the right decision.
Test case:
==========
#include <alloca.h>
#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <signal.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/reboot.h>
static int do_reboot(void *arg)
{
int *cmd = arg;
if (reboot(*cmd))
printf("failed to reboot(%d): %m\n", *cmd);
}
int test_reboot(int cmd, int sig)
{
long stack_size = 4096;
void *stack = alloca(stack_size) + stack_size;
int status;
pid_t ret;
ret = clone(do_reboot, stack, CLONE_NEWPID | SIGCHLD, &cmd);
if (ret < 0) {
printf("failed to clone: %m\n");
return -1;
}
if (wait(&status) < 0) {
printf("unexpected wait error: %m\n");
return -1;
}
if (!WIFSIGNALED(status)) {
printf("child process exited but was not signaled\n");
return -1;
}
if (WTERMSIG(status) != sig) {
printf("signal termination is not the one expected\n");
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int status;
status = test_reboot(LINUX_REBOOT_CMD_RESTART, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_RESTART2, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART2) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_HALT, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_HALT) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_POWER_OFF, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_POWERR_OFF) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_CAD_ON, -1);
if (status >= 0) {
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) should have failed\n");
return 1;
}
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) has failed as expected\n");
return 0;
}
[akpm@linux-foundation.org: tweak and add comments]
[akpm@linux-foundation.org: checkpatch fixes]
Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Tested-by: Serge Hallyn <serge.hallyn@canonical.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-29 05:42:51 +08:00
|
|
|
if (pid_ns->reboot)
|
|
|
|
current->signal->group_exit_code = pid_ns->reboot;
|
|
|
|
|
2008-07-25 16:48:47 +08:00
|
|
|
acct_exit_ns(pid_ns);
|
2008-02-08 20:18:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-01 07:26:42 +08:00
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
2012-01-13 09:20:27 +08:00
|
|
|
static int pid_ns_ctl_handler(struct ctl_table *table, int write,
|
2020-04-24 14:43:38 +08:00
|
|
|
void *buffer, size_t *lenp, loff_t *ppos)
|
2012-01-13 09:20:27 +08:00
|
|
|
{
|
2012-08-02 19:25:10 +08:00
|
|
|
struct pid_namespace *pid_ns = task_active_pid_ns(current);
|
2012-01-13 09:20:27 +08:00
|
|
|
struct ctl_table tmp = *table;
|
2017-11-18 07:30:30 +08:00
|
|
|
int ret, next;
|
2012-01-13 09:20:27 +08:00
|
|
|
|
2020-07-19 18:04:13 +08:00
|
|
|
if (write && !checkpoint_restore_ns_capable(pid_ns->user_ns))
|
2012-01-13 09:20:27 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Writing directly to ns' last_pid field is OK, since this field
|
|
|
|
* is volatile in a living namespace anyway and a code writing to
|
|
|
|
* it should synchronize its usage with external means.
|
|
|
|
*/
|
|
|
|
|
2017-11-18 07:30:30 +08:00
|
|
|
next = idr_get_cursor(&pid_ns->idr) - 1;
|
|
|
|
|
|
|
|
tmp.data = &next;
|
|
|
|
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
|
|
|
if (!ret && write)
|
|
|
|
idr_set_cursor(&pid_ns->idr, next + 1);
|
|
|
|
|
|
|
|
return ret;
|
2012-01-13 09:20:27 +08:00
|
|
|
}
|
|
|
|
|
2012-09-18 05:09:12 +08:00
|
|
|
extern int pid_max;
|
2012-01-13 09:20:27 +08:00
|
|
|
static struct ctl_table pid_ns_ctl_table[] = {
|
|
|
|
{
|
|
|
|
.procname = "ns_last_pid",
|
|
|
|
.maxlen = sizeof(int),
|
|
|
|
.mode = 0666, /* permissions are checked in the handler */
|
|
|
|
.proc_handler = pid_ns_ctl_handler,
|
proc/sysctl: add shared variables for range check
In the sysctl code the proc_dointvec_minmax() function is often used to
validate the user supplied value between an allowed range. This
function uses the extra1 and extra2 members from struct ctl_table as
minimum and maximum allowed value.
On sysctl handler declaration, in every source file there are some
readonly variables containing just an integer which address is assigned
to the extra1 and extra2 members, so the sysctl range is enforced.
The special values 0, 1 and INT_MAX are very often used as range
boundary, leading duplication of variables like zero=0, one=1,
int_max=INT_MAX in different source files:
$ git grep -E '\.extra[12].*&(zero|one|int_max)' |wc -l
248
Add a const int array containing the most commonly used values, some
macros to refer more easily to the correct array member, and use them
instead of creating a local one for every object file.
This is the bloat-o-meter output comparing the old and new binary
compiled with the default Fedora config:
# scripts/bloat-o-meter -d vmlinux.o.old vmlinux.o
add/remove: 2/2 grow/shrink: 0/2 up/down: 24/-188 (-164)
Data old new delta
sysctl_vals - 12 +12
__kstrtab_sysctl_vals - 12 +12
max 14 10 -4
int_max 16 - -16
one 68 - -68
zero 128 28 -100
Total: Before=20583249, After=20583085, chg -0.00%
[mcroce@redhat.com: tipc: remove two unused variables]
Link: http://lkml.kernel.org/r/20190530091952.4108-1-mcroce@redhat.com
[akpm@linux-foundation.org: fix net/ipv6/sysctl_net_ipv6.c]
[arnd@arndb.de: proc/sysctl: make firmware loader table conditional]
Link: http://lkml.kernel.org/r/20190617130014.1713870-1-arnd@arndb.de
[akpm@linux-foundation.org: fix fs/eventpoll.c]
Link: http://lkml.kernel.org/r/20190430180111.10688-1-mcroce@redhat.com
Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Aaron Tomlin <atomlin@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2019-07-19 06:58:50 +08:00
|
|
|
.extra1 = SYSCTL_ZERO,
|
2012-09-18 05:09:12 +08:00
|
|
|
.extra2 = &pid_max,
|
2012-01-13 09:20:27 +08:00
|
|
|
},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
static struct ctl_path kern_path[] = { { .procname = "kernel", }, { } };
|
2012-06-01 07:26:42 +08:00
|
|
|
#endif /* CONFIG_CHECKPOINT_RESTORE */
|
2012-01-13 09:20:27 +08:00
|
|
|
|
pidns: add reboot_pid_ns() to handle the reboot syscall
In the case of a child pid namespace, rebooting the system does not really
makes sense. When the pid namespace is used in conjunction with the other
namespaces in order to create a linux container, the reboot syscall leads
to some problems.
A container can reboot the host. That can be fixed by dropping the
sys_reboot capability but we are unable to correctly to poweroff/
halt/reboot a container and the container stays stuck at the shutdown time
with the container's init process waiting indefinitively.
After several attempts, no solution from userspace was found to reliabily
handle the shutdown from a container.
This patch propose to make the init process of the child pid namespace to
exit with a signal status set to : SIGINT if the child pid namespace
called "halt/poweroff" and SIGHUP if the child pid namespace called
"reboot". When the reboot syscall is called and we are not in the initial
pid namespace, we kill the pid namespace for "HALT", "POWEROFF",
"RESTART", and "RESTART2". Otherwise we return EINVAL.
Returning EINVAL is also an easy way to check if this feature is supported
by the kernel when invoking another 'reboot' option like CAD.
By this way the parent process of the child pid namespace knows if it
rebooted or not and can take the right decision.
Test case:
==========
#include <alloca.h>
#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <signal.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/reboot.h>
static int do_reboot(void *arg)
{
int *cmd = arg;
if (reboot(*cmd))
printf("failed to reboot(%d): %m\n", *cmd);
}
int test_reboot(int cmd, int sig)
{
long stack_size = 4096;
void *stack = alloca(stack_size) + stack_size;
int status;
pid_t ret;
ret = clone(do_reboot, stack, CLONE_NEWPID | SIGCHLD, &cmd);
if (ret < 0) {
printf("failed to clone: %m\n");
return -1;
}
if (wait(&status) < 0) {
printf("unexpected wait error: %m\n");
return -1;
}
if (!WIFSIGNALED(status)) {
printf("child process exited but was not signaled\n");
return -1;
}
if (WTERMSIG(status) != sig) {
printf("signal termination is not the one expected\n");
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int status;
status = test_reboot(LINUX_REBOOT_CMD_RESTART, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_RESTART2, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART2) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_HALT, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_HALT) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_POWER_OFF, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_POWERR_OFF) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_CAD_ON, -1);
if (status >= 0) {
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) should have failed\n");
return 1;
}
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) has failed as expected\n");
return 0;
}
[akpm@linux-foundation.org: tweak and add comments]
[akpm@linux-foundation.org: checkpatch fixes]
Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Tested-by: Serge Hallyn <serge.hallyn@canonical.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-29 05:42:51 +08:00
|
|
|
int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
|
|
|
|
{
|
|
|
|
if (pid_ns == &init_pid_ns)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case LINUX_REBOOT_CMD_RESTART2:
|
|
|
|
case LINUX_REBOOT_CMD_RESTART:
|
|
|
|
pid_ns->reboot = SIGHUP;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LINUX_REBOOT_CMD_POWER_OFF:
|
|
|
|
case LINUX_REBOOT_CMD_HALT:
|
|
|
|
pid_ns->reboot = SIGINT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
read_lock(&tasklist_lock);
|
2019-05-16 01:29:52 +08:00
|
|
|
send_sig(SIGKILL, pid_ns->child_reaper, 1);
|
pidns: add reboot_pid_ns() to handle the reboot syscall
In the case of a child pid namespace, rebooting the system does not really
makes sense. When the pid namespace is used in conjunction with the other
namespaces in order to create a linux container, the reboot syscall leads
to some problems.
A container can reboot the host. That can be fixed by dropping the
sys_reboot capability but we are unable to correctly to poweroff/
halt/reboot a container and the container stays stuck at the shutdown time
with the container's init process waiting indefinitively.
After several attempts, no solution from userspace was found to reliabily
handle the shutdown from a container.
This patch propose to make the init process of the child pid namespace to
exit with a signal status set to : SIGINT if the child pid namespace
called "halt/poweroff" and SIGHUP if the child pid namespace called
"reboot". When the reboot syscall is called and we are not in the initial
pid namespace, we kill the pid namespace for "HALT", "POWEROFF",
"RESTART", and "RESTART2". Otherwise we return EINVAL.
Returning EINVAL is also an easy way to check if this feature is supported
by the kernel when invoking another 'reboot' option like CAD.
By this way the parent process of the child pid namespace knows if it
rebooted or not and can take the right decision.
Test case:
==========
#include <alloca.h>
#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <signal.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/reboot.h>
static int do_reboot(void *arg)
{
int *cmd = arg;
if (reboot(*cmd))
printf("failed to reboot(%d): %m\n", *cmd);
}
int test_reboot(int cmd, int sig)
{
long stack_size = 4096;
void *stack = alloca(stack_size) + stack_size;
int status;
pid_t ret;
ret = clone(do_reboot, stack, CLONE_NEWPID | SIGCHLD, &cmd);
if (ret < 0) {
printf("failed to clone: %m\n");
return -1;
}
if (wait(&status) < 0) {
printf("unexpected wait error: %m\n");
return -1;
}
if (!WIFSIGNALED(status)) {
printf("child process exited but was not signaled\n");
return -1;
}
if (WTERMSIG(status) != sig) {
printf("signal termination is not the one expected\n");
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int status;
status = test_reboot(LINUX_REBOOT_CMD_RESTART, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_RESTART2, SIGHUP);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_RESTART2) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_HALT, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_HALT) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_POWER_OFF, SIGINT);
if (status < 0)
return 1;
printf("reboot(LINUX_REBOOT_CMD_POWERR_OFF) succeed\n");
status = test_reboot(LINUX_REBOOT_CMD_CAD_ON, -1);
if (status >= 0) {
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) should have failed\n");
return 1;
}
printf("reboot(LINUX_REBOOT_CMD_CAD_ON) has failed as expected\n");
return 0;
}
[akpm@linux-foundation.org: tweak and add comments]
[akpm@linux-foundation.org: checkpatch fixes]
Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Tested-by: Serge Hallyn <serge.hallyn@canonical.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-29 05:42:51 +08:00
|
|
|
read_unlock(&tasklist_lock);
|
|
|
|
|
|
|
|
do_exit(0);
|
|
|
|
|
|
|
|
/* Not reached */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-01 12:25:30 +08:00
|
|
|
static inline struct pid_namespace *to_pid_ns(struct ns_common *ns)
|
|
|
|
{
|
|
|
|
return container_of(ns, struct pid_namespace, ns);
|
|
|
|
}
|
|
|
|
|
2014-11-01 12:37:32 +08:00
|
|
|
static struct ns_common *pidns_get(struct task_struct *task)
|
2010-03-08 10:17:03 +08:00
|
|
|
{
|
|
|
|
struct pid_namespace *ns;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2014-04-02 23:45:05 +08:00
|
|
|
ns = task_active_pid_ns(task);
|
|
|
|
if (ns)
|
|
|
|
get_pid_ns(ns);
|
2010-03-08 10:17:03 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2014-11-01 12:25:30 +08:00
|
|
|
return ns ? &ns->ns : NULL;
|
2010-03-08 10:17:03 +08:00
|
|
|
}
|
|
|
|
|
2017-05-09 06:56:41 +08:00
|
|
|
static struct ns_common *pidns_for_children_get(struct task_struct *task)
|
|
|
|
{
|
|
|
|
struct pid_namespace *ns = NULL;
|
|
|
|
|
|
|
|
task_lock(task);
|
|
|
|
if (task->nsproxy) {
|
|
|
|
ns = task->nsproxy->pid_ns_for_children;
|
|
|
|
get_pid_ns(ns);
|
|
|
|
}
|
|
|
|
task_unlock(task);
|
|
|
|
|
|
|
|
if (ns) {
|
|
|
|
read_lock(&tasklist_lock);
|
|
|
|
if (!ns->child_reaper) {
|
|
|
|
put_pid_ns(ns);
|
|
|
|
ns = NULL;
|
|
|
|
}
|
|
|
|
read_unlock(&tasklist_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns ? &ns->ns : NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-01 12:37:32 +08:00
|
|
|
static void pidns_put(struct ns_common *ns)
|
2010-03-08 10:17:03 +08:00
|
|
|
{
|
2014-11-01 12:25:30 +08:00
|
|
|
put_pid_ns(to_pid_ns(ns));
|
2010-03-08 10:17:03 +08:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:04:30 +08:00
|
|
|
static int pidns_install(struct nsset *nsset, struct ns_common *ns)
|
2010-03-08 10:17:03 +08:00
|
|
|
{
|
2020-05-05 22:04:30 +08:00
|
|
|
struct nsproxy *nsproxy = nsset->nsproxy;
|
2010-03-08 10:17:03 +08:00
|
|
|
struct pid_namespace *active = task_active_pid_ns(current);
|
2014-11-01 12:25:30 +08:00
|
|
|
struct pid_namespace *ancestor, *new = to_pid_ns(ns);
|
2010-03-08 10:17:03 +08:00
|
|
|
|
2012-12-14 23:55:36 +08:00
|
|
|
if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) ||
|
2020-05-05 22:04:30 +08:00
|
|
|
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
|
2010-03-08 10:17:03 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only allow entering the current active pid namespace
|
|
|
|
* or a child of the current active pid namespace.
|
|
|
|
*
|
|
|
|
* This is required for fork to return a usable pid value and
|
|
|
|
* this maintains the property that processes and their
|
|
|
|
* children can not escape their current pid namespace.
|
|
|
|
*/
|
|
|
|
if (new->level < active->level)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ancestor = new;
|
|
|
|
while (ancestor->level > active->level)
|
|
|
|
ancestor = ancestor->parent;
|
|
|
|
if (ancestor != active)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-08-23 02:39:16 +08:00
|
|
|
put_pid_ns(nsproxy->pid_ns_for_children);
|
|
|
|
nsproxy->pid_ns_for_children = get_pid_ns(new);
|
2010-03-08 10:17:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-06 15:47:15 +08:00
|
|
|
static struct ns_common *pidns_get_parent(struct ns_common *ns)
|
|
|
|
{
|
|
|
|
struct pid_namespace *active = task_active_pid_ns(current);
|
|
|
|
struct pid_namespace *pid_ns, *p;
|
|
|
|
|
|
|
|
/* See if the parent is in the current namespace */
|
|
|
|
pid_ns = p = to_pid_ns(ns)->parent;
|
|
|
|
for (;;) {
|
|
|
|
if (!p)
|
|
|
|
return ERR_PTR(-EPERM);
|
|
|
|
if (p == active)
|
|
|
|
break;
|
|
|
|
p = p->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &get_pid_ns(pid_ns)->ns;
|
|
|
|
}
|
|
|
|
|
2016-09-06 15:47:13 +08:00
|
|
|
static struct user_namespace *pidns_owner(struct ns_common *ns)
|
|
|
|
{
|
|
|
|
return to_pid_ns(ns)->user_ns;
|
|
|
|
}
|
|
|
|
|
2010-03-08 10:17:03 +08:00
|
|
|
const struct proc_ns_operations pidns_operations = {
|
|
|
|
.name = "pid",
|
|
|
|
.type = CLONE_NEWPID,
|
|
|
|
.get = pidns_get,
|
|
|
|
.put = pidns_put,
|
|
|
|
.install = pidns_install,
|
2016-09-06 15:47:13 +08:00
|
|
|
.owner = pidns_owner,
|
2016-09-06 15:47:15 +08:00
|
|
|
.get_parent = pidns_get_parent,
|
2010-03-08 10:17:03 +08:00
|
|
|
};
|
|
|
|
|
2017-05-09 06:56:41 +08:00
|
|
|
const struct proc_ns_operations pidns_for_children_operations = {
|
|
|
|
.name = "pid_for_children",
|
|
|
|
.real_ns_name = "pid",
|
|
|
|
.type = CLONE_NEWPID,
|
|
|
|
.get = pidns_for_children_get,
|
|
|
|
.put = pidns_put,
|
|
|
|
.install = pidns_install,
|
|
|
|
.owner = pidns_owner,
|
|
|
|
.get_parent = pidns_get_parent,
|
|
|
|
};
|
|
|
|
|
2008-02-08 20:18:24 +08:00
|
|
|
static __init int pid_namespaces_init(void)
|
|
|
|
{
|
|
|
|
pid_ns_cachep = KMEM_CACHE(pid_namespace, SLAB_PANIC);
|
2012-06-01 07:26:42 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
2012-01-13 09:20:27 +08:00
|
|
|
register_sysctl_paths(kern_path, pid_ns_ctl_table);
|
2012-06-01 07:26:42 +08:00
|
|
|
#endif
|
2008-02-08 20:18:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
__initcall(pid_namespaces_init);
|