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>
|
2023-12-12 02:14:41 +08:00
|
|
|
#include <uapi/linux/wait.h>
|
mm/memfd: add MFD_NOEXEC_SEAL and MFD_EXEC
The new MFD_NOEXEC_SEAL and MFD_EXEC flags allows application to set
executable bit at creation time (memfd_create).
When MFD_NOEXEC_SEAL is set, memfd is created without executable bit
(mode:0666), and sealed with F_SEAL_EXEC, so it can't be chmod to be
executable (mode: 0777) after creation.
when MFD_EXEC flag is set, memfd is created with executable bit
(mode:0777), this is the same as the old behavior of memfd_create.
The new pid namespaced sysctl vm.memfd_noexec has 3 values:
0: memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
MFD_EXEC was set.
1: memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
MFD_NOEXEC_SEAL was set.
2: memfd_create() without MFD_NOEXEC_SEAL will be rejected.
The sysctl allows finer control of memfd_create for old-software that
doesn't set the executable bit, for example, a container with
vm.memfd_noexec=1 means the old-software will create non-executable memfd
by default. Also, the value of memfd_noexec is passed to child namespace
at creation time. For example, if the init namespace has
vm.memfd_noexec=2, all its children namespaces will be created with 2.
[akpm@linux-foundation.org: add stub functions to fix build]
[akpm@linux-foundation.org: remove unneeded register_pid_ns_ctl_table_vm() stub, per Jeff]
[akpm@linux-foundation.org: s/pr_warn_ratelimited/pr_warn_once/, per review]
[akpm@linux-foundation.org: fix CONFIG_SYSCTL=n warning]
Link: https://lkml.kernel.org/r/20221215001205.51969-4-jeffxu@google.com
Signed-off-by: Jeff Xu <jeffxu@google.com>
Co-developed-by: Daniel Verkamp <dverkamp@chromium.org>
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jorge Lucangeli Obes <jorgelo@chromium.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-12-15 08:12:03 +08:00
|
|
|
#include "pid_sysctl.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);
|
2023-07-01 14:44:44 +08:00
|
|
|
len = struct_size_t(struct pid, numbers, level + 1);
|
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)
|
2021-09-03 05:54:57 +08:00
|
|
|
*pkc = kmem_cache_create(name, len, 0,
|
2022-04-30 05:38:00 +08:00
|
|
|
SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);
|
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;
|
memfd: replace ratcheting feature from vm.memfd_noexec with hierarchy
This sysctl has the very unusual behaviour of not allowing any user (even
CAP_SYS_ADMIN) to reduce the restriction setting, meaning that if you were
to set this sysctl to a more restrictive option in the host pidns you
would need to reboot your machine in order to reset it.
The justification given in [1] is that this is a security feature and thus
it should not be possible to disable. Aside from the fact that we have
plenty of security-related sysctls that can be disabled after being
enabled (fs.protected_symlinks for instance), the protection provided by
the sysctl is to stop users from being able to create a binary and then
execute it. A user with CAP_SYS_ADMIN can trivially do this without
memfd_create(2):
% cat mount-memfd.c
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/mount.h>
#define SHELLCODE "#!/bin/echo this file was executed from this totally private tmpfs:"
int main(void)
{
int fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
assert(fsfd >= 0);
assert(!fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 2));
int dfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
assert(dfd >= 0);
int execfd = openat(dfd, "exe", O_CREAT | O_RDWR | O_CLOEXEC, 0782);
assert(execfd >= 0);
assert(write(execfd, SHELLCODE, strlen(SHELLCODE)) == strlen(SHELLCODE));
assert(!close(execfd));
char *execpath = NULL;
char *argv[] = { "bad-exe", NULL }, *envp[] = { NULL };
execfd = openat(dfd, "exe", O_PATH | O_CLOEXEC);
assert(execfd >= 0);
assert(asprintf(&execpath, "/proc/self/fd/%d", execfd) > 0);
assert(!execve(execpath, argv, envp));
}
% ./mount-memfd
this file was executed from this totally private tmpfs: /proc/self/fd/5
%
Given that it is possible for CAP_SYS_ADMIN users to create executable
binaries without memfd_create(2) and without touching the host filesystem
(not to mention the many other things a CAP_SYS_ADMIN process would be
able to do that would be equivalent or worse), it seems strange to cause a
fair amount of headache to admins when there doesn't appear to be an
actual security benefit to blocking this. There appear to be concerns
about confused-deputy-esque attacks[2] but a confused deputy that can
write to arbitrary sysctls is a bigger security issue than executable
memfds.
/* New API */
The primary requirement from the original author appears to be more based
on the need to be able to restrict an entire system in a hierarchical
manner[3], such that child namespaces cannot re-enable executable memfds.
So, implement that behaviour explicitly -- the vm.memfd_noexec scope is
evaluated up the pidns tree to &init_pid_ns and you have the most
restrictive value applied to you. The new lower limit you can set
vm.memfd_noexec is whatever limit applies to your parent.
Note that a pidns will inherit a copy of the parent pidns's effective
vm.memfd_noexec setting at unshare() time. This matches the existing
behaviour, and it also ensures that a pidns will never have its
vm.memfd_noexec setting *lowered* behind its back (but it will be raised
if the parent raises theirs).
/* Backwards Compatibility */
As the previous version of the sysctl didn't allow you to lower the
setting at all, there are no backwards compatibility issues with this
aspect of the change.
However it should be noted that now that the setting is completely
hierarchical. Previously, a cloned pidns would just copy the current
pidns setting, meaning that if the parent's vm.memfd_noexec was changed it
wouldn't propoagate to existing pid namespaces. Now, the restriction
applies recursively. This is a uAPI change, however:
* The sysctl is very new, having been merged in 6.3.
* Several aspects of the sysctl were broken up until this patchset and
the other patchset by Jeff Xu last month.
And thus it seems incredibly unlikely that any real users would run into
this issue. In the worst case, if this causes userspace isues we could
make it so that modifying the setting follows the hierarchical rules but
the restriction checking uses the cached copy.
[1]: https://lore.kernel.org/CABi2SkWnAgHK1i6iqSqPMYuNEhtHBkO8jUuCvmG3RmUB5TKHJw@mail.gmail.com/
[2]: https://lore.kernel.org/CALmYWFs_dNCzw_pW1yRAo4bGCPEtykroEQaowNULp7svwMLjOg@mail.gmail.com/
[3]: https://lore.kernel.org/CALmYWFuahdUF7cT4cm7_TGLqPanuHXJ-hVSfZt7vpTnc18DPrw@mail.gmail.com/
Link: https://lkml.kernel.org/r/20230814-memfd-vm-noexec-uapi-fixes-v2-4-7ff9e3e10ba6@cyphar.com
Fixes: 105ff5339f49 ("mm/memfd: add MFD_NOEXEC_SEAL and MFD_EXEC")
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Cc: Dominique Martinet <asmadeus@codewreck.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Daniel Verkamp <dverkamp@chromium.org>
Cc: Jeff Xu <jeffxu@google.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-14 16:41:00 +08:00
|
|
|
#if defined(CONFIG_SYSCTL) && defined(CONFIG_MEMFD_CREATE)
|
|
|
|
ns->memfd_noexec_scope = pidns_memfd_noexec_scope(parent_pid_ns);
|
|
|
|
#endif
|
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;
|
2022-11-25 21:55:00 +08:00
|
|
|
/*
|
|
|
|
* Release tasks_rcu_exit_srcu to avoid following deadlock:
|
|
|
|
*
|
|
|
|
* 1) TASK A unshare(CLONE_NEWPID)
|
|
|
|
* 2) TASK A fork() twice -> TASK B (child reaper for new ns)
|
|
|
|
* and TASK C
|
|
|
|
* 3) TASK B exits, kills TASK C, waits for TASK A to reap it
|
|
|
|
* 4) TASK A calls synchronize_rcu_tasks()
|
|
|
|
* -> synchronize_srcu(tasks_rcu_exit_srcu)
|
|
|
|
* 5) *DEADLOCK*
|
|
|
|
*
|
|
|
|
* It is considered safe to release tasks_rcu_exit_srcu here
|
|
|
|
* because we assume the current task can not be concurrently
|
|
|
|
* reaped at this point.
|
|
|
|
*/
|
|
|
|
exit_tasks_rcu_stop();
|
2012-06-21 03:53:03 +08:00
|
|
|
schedule();
|
2022-11-25 21:55:00 +08:00
|
|
|
exit_tasks_rcu_start();
|
2012-06-21 03:53:03 +08:00
|
|
|
}
|
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;
|
|
|
|
|
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
|
|
|
},
|
|
|
|
};
|
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)
|
|
|
|
{
|
2021-09-03 05:55:27 +08:00
|
|
|
pid_ns_cachep = KMEM_CACHE(pid_namespace, SLAB_PANIC | SLAB_ACCOUNT);
|
2012-06-01 07:26:42 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
2023-03-03 04:28:23 +08:00
|
|
|
register_sysctl_init("kernel", pid_ns_ctl_table);
|
2012-06-01 07:26:42 +08:00
|
|
|
#endif
|
mm/memfd: add MFD_NOEXEC_SEAL and MFD_EXEC
The new MFD_NOEXEC_SEAL and MFD_EXEC flags allows application to set
executable bit at creation time (memfd_create).
When MFD_NOEXEC_SEAL is set, memfd is created without executable bit
(mode:0666), and sealed with F_SEAL_EXEC, so it can't be chmod to be
executable (mode: 0777) after creation.
when MFD_EXEC flag is set, memfd is created with executable bit
(mode:0777), this is the same as the old behavior of memfd_create.
The new pid namespaced sysctl vm.memfd_noexec has 3 values:
0: memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
MFD_EXEC was set.
1: memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL acts like
MFD_NOEXEC_SEAL was set.
2: memfd_create() without MFD_NOEXEC_SEAL will be rejected.
The sysctl allows finer control of memfd_create for old-software that
doesn't set the executable bit, for example, a container with
vm.memfd_noexec=1 means the old-software will create non-executable memfd
by default. Also, the value of memfd_noexec is passed to child namespace
at creation time. For example, if the init namespace has
vm.memfd_noexec=2, all its children namespaces will be created with 2.
[akpm@linux-foundation.org: add stub functions to fix build]
[akpm@linux-foundation.org: remove unneeded register_pid_ns_ctl_table_vm() stub, per Jeff]
[akpm@linux-foundation.org: s/pr_warn_ratelimited/pr_warn_once/, per review]
[akpm@linux-foundation.org: fix CONFIG_SYSCTL=n warning]
Link: https://lkml.kernel.org/r/20221215001205.51969-4-jeffxu@google.com
Signed-off-by: Jeff Xu <jeffxu@google.com>
Co-developed-by: Daniel Verkamp <dverkamp@chromium.org>
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: David Herrmann <dh.herrmann@gmail.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jorge Lucangeli Obes <jorgelo@chromium.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2022-12-15 08:12:03 +08:00
|
|
|
|
|
|
|
register_pid_ns_sysctl_table_vm();
|
2008-02-08 20:18:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
__initcall(pid_namespaces_init);
|