Rootless containers are not allowed to modify kernel IPC parameters.
All default limits are set to such high values that in fact there are no
limits at all. All limits are not inherited and are initialized to
default values when a new ipc_namespace is created.
For new ipc_namespace:
size_t ipc_ns.shm_ctlmax = SHMMAX; // (ULONG_MAX - (1UL << 24))
size_t ipc_ns.shm_ctlall = SHMALL; // (ULONG_MAX - (1UL << 24))
int ipc_ns.shm_ctlmni = IPCMNI; // (1 << 15)
int ipc_ns.shm_rmid_forced = 0;
unsigned int ipc_ns.msg_ctlmax = MSGMAX; // 8192
unsigned int ipc_ns.msg_ctlmni = MSGMNI; // 32000
unsigned int ipc_ns.msg_ctlmnb = MSGMNB; // 16384
The shm_tot (total amount of shared pages) has also ceased to be
global, it is located in ipc_namespace and is not inherited from
anywhere.
In such conditions, it cannot be said that these limits limit anything.
The real limiter for them is cgroups.
If we allow rootless containers to change these parameters, then it can
only be reduced.
Signed-off-by: Alexey Gladkov <[email protected]>
---
ipc/ipc_sysctl.c | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index ef313ecfb53a..a6a9d7f680dd 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -190,25 +190,53 @@ static int set_is_seen(struct ctl_table_set *set)
return ¤t->nsproxy->ipc_ns->ipc_set == set;
}
+static void ipc_set_ownership(struct ctl_table_header *head,
+ struct ctl_table *table,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct ipc_namespace *ns =
+ container_of(head->set, struct ipc_namespace, ipc_set);
+
+ kuid_t ns_root_uid = make_kuid(ns->user_ns, 0);
+ kgid_t ns_root_gid = make_kgid(ns->user_ns, 0);
+
+ *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID;
+ *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID;
+}
+
static int ipc_permissions(struct ctl_table_header *head, struct ctl_table *table)
{
+ struct ipc_namespace *ns =
+ container_of(head->set, struct ipc_namespace, ipc_set);
int mode = table->mode;
+ kuid_t ns_root_uid;
+ kgid_t ns_root_gid;
-#ifdef CONFIG_CHECKPOINT_RESTORE
- struct ipc_namespace *ns = current->nsproxy->ipc_ns;
+ ipc_set_ownership(head, table, &ns_root_uid, ns_root_gid);
+#ifdef CONFIG_CHECKPOINT_RESTORE
if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) ||
(table->data == &ns->ids[IPC_MSG_IDS].next_id) ||
(table->data == &ns->ids[IPC_SHM_IDS].next_id)) &&
checkpoint_restore_ns_capable(ns->user_ns))
mode = 0666;
+ else
#endif
- return mode;
+ if (uid_eq(current_euid(), ns_root_uid))
+ mode >>= 6;
+
+ else if (in_egroup_p(ns_root_gid))
+ mode >>= 3;
+
+ mode &= 7;
+
+ return (mode << 6) | (mode << 3) | mode;
}
static struct ctl_table_root set_root = {
.lookup = set_lookup,
.permissions = ipc_permissions,
+ .set_ownership = ipc_set_ownership,
};
bool setup_ipc_sysctls(struct ipc_namespace *ns)
--
2.33.4
Hi Alexey,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on akpm-mm/mm-everything]
[also build test ERROR on kees/for-next/pstore linus/master v6.0-rc6 next-20220920]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Alexey-Gladkov/sysctl-Allow-change-system-v-ipc-sysctls-inside-ipc-namespace/20220921-030939
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
config: hexagon-randconfig-r041-20220921 (https://download.01.org/0day-ci/archive/20220921/[email protected]/config)
compiler: clang version 16.0.0 (https://github.com/llvm/llvm-project 791a7ae1ba3efd6bca96338e10ffde557ba83920)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/eb972fb9aad60123519d8dd32df26cb58985ce4a
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Alexey-Gladkov/sysctl-Allow-change-system-v-ipc-sysctls-inside-ipc-namespace/20220921-030939
git checkout eb972fb9aad60123519d8dd32df26cb58985ce4a
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
>> ipc/ipc_sysctl.c:215:47: error: passing 'kgid_t' to parameter of incompatible type 'kgid_t *'; take the address with &
ipc_set_ownership(head, table, &ns_root_uid, ns_root_gid);
^~~~~~~~~~~
&
ipc/ipc_sysctl.c:195:31: note: passing argument to parameter 'gid' here
kuid_t *uid, kgid_t *gid)
^
>> ipc/ipc_sysctl.c:225:13: error: call to undeclared function 'current_euid'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
if (uid_eq(current_euid(), ns_root_uid))
^
ipc/ipc_sysctl.c:225:13: note: did you mean 'current_work'?
include/linux/workqueue.h:467:28: note: 'current_work' declared here
extern struct work_struct *current_work(void);
^
>> ipc/ipc_sysctl.c:225:13: error: passing 'int' to parameter of incompatible type 'kuid_t'
if (uid_eq(current_euid(), ns_root_uid))
^~~~~~~~~~~~~~
include/linux/uidgid.h:61:34: note: passing argument to parameter 'left' here
static inline bool uid_eq(kuid_t left, kuid_t right)
^
>> ipc/ipc_sysctl.c:228:11: error: call to undeclared function 'in_egroup_p'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
else if (in_egroup_p(ns_root_gid))
^
4 errors generated.
vim +215 ipc/ipc_sysctl.c
206
207 static int ipc_permissions(struct ctl_table_header *head, struct ctl_table *table)
208 {
209 struct ipc_namespace *ns =
210 container_of(head->set, struct ipc_namespace, ipc_set);
211 int mode = table->mode;
212 kuid_t ns_root_uid;
213 kgid_t ns_root_gid;
214
> 215 ipc_set_ownership(head, table, &ns_root_uid, ns_root_gid);
216
217 #ifdef CONFIG_CHECKPOINT_RESTORE
218 if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) ||
219 (table->data == &ns->ids[IPC_MSG_IDS].next_id) ||
220 (table->data == &ns->ids[IPC_SHM_IDS].next_id)) &&
221 checkpoint_restore_ns_capable(ns->user_ns))
222 mode = 0666;
223 else
224 #endif
> 225 if (uid_eq(current_euid(), ns_root_uid))
226 mode >>= 6;
227
> 228 else if (in_egroup_p(ns_root_gid))
229 mode >>= 3;
230
231 mode &= 7;
232
233 return (mode << 6) | (mode << 3) | mode;
234 }
235
--
0-DAY CI Kernel Test Service
https://01.org/lkp
Right now ipc and mq limits count as per ipc namespace, but only real
root can change them. By default, the current values of these limits are
such that it can only be reduced. Since only root can change the values,
it is impossible to reduce these limits in the rootless container.
We can allow limit changes within ipc namespace because mq parameters
are limited by RLIMIT_MSGQUEUE and ipc parameters are not limited to
anything other than cgroups.
--
Alexey Gladkov (3):
sysctl: Allow change system v ipc sysctls inside ipc namespace
sysctl: Allow to change limits for posix messages queues
docs: Add information about ipc sysctls limitations
Documentation/admin-guide/sysctl/kernel.rst | 14 ++++++--
ipc/ipc_sysctl.c | 36 +++++++++++++++++++--
ipc/mq_sysctl.c | 36 +++++++++++++++++++++
3 files changed, 81 insertions(+), 5 deletions(-)
--
2.33.4
Right now ipc and mq limits count as per ipc namespace, but only real
root can change them. By default, the current values of these limits are
such that it can only be reduced. Since only root can change the values,
it is impossible to reduce these limits in the rootless container.
We can allow limit changes within ipc namespace because mq parameters
are limited by RLIMIT_MSGQUEUE and ipc parameters are not limited to
anything other than cgroups.
This is just a rebase of patches on v6.7-6264-g70d201a40823.
---
Alexey Gladkov (3):
sysctl: Allow change system v ipc sysctls inside ipc namespace
docs: Add information about ipc sysctls limitations
sysctl: Allow to change limits for posix messages queues
Documentation/admin-guide/sysctl/kernel.rst | 14 ++++++--
ipc/ipc_sysctl.c | 37 +++++++++++++++++++--
ipc/mq_sysctl.c | 36 ++++++++++++++++++++
3 files changed, 82 insertions(+), 5 deletions(-)
--
2.43.0
All parameters of posix messages queues (queues_max/msg_max/msgsize_max)
end up being limited by RLIMIT_MSGQUEUE. The code in mqueue_get_inode is
where that limiting happens.
The RLIMIT_MSGQUEUE is bound to the user namespace and is counted
hierarchically.
We can allow root in the user namespace to modify the posix messages
queues parameters.
Signed-off-by: Alexey Gladkov <[email protected]>
Link: https://lkml.kernel.org/r/7eb21211c8622e91d226e63416b1b93c079f60ee.1663756794.git.legion@kernel.org
Signed-off-by: Eric W. Biederman <[email protected]>
---
ipc/mq_sysctl.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/ipc/mq_sysctl.c b/ipc/mq_sysctl.c
index ebb5ed81c151..21fba3a6edaf 100644
--- a/ipc/mq_sysctl.c
+++ b/ipc/mq_sysctl.c
@@ -12,6 +12,7 @@
#include <linux/stat.h>
#include <linux/capability.h>
#include <linux/slab.h>
+#include <linux/cred.h>
static int msg_max_limit_min = MIN_MSGMAX;
static int msg_max_limit_max = HARD_MSGMAX;
@@ -76,8 +77,43 @@ static int set_is_seen(struct ctl_table_set *set)
return ¤t->nsproxy->ipc_ns->mq_set == set;
}
+static void mq_set_ownership(struct ctl_table_header *head,
+ struct ctl_table *table,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct ipc_namespace *ns =
+ container_of(head->set, struct ipc_namespace, mq_set);
+
+ kuid_t ns_root_uid = make_kuid(ns->user_ns, 0);
+ kgid_t ns_root_gid = make_kgid(ns->user_ns, 0);
+
+ *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID;
+ *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID;
+}
+
+static int mq_permissions(struct ctl_table_header *head, struct ctl_table *table)
+{
+ int mode = table->mode;
+ kuid_t ns_root_uid;
+ kgid_t ns_root_gid;
+
+ mq_set_ownership(head, table, &ns_root_uid, &ns_root_gid);
+
+ if (uid_eq(current_euid(), ns_root_uid))
+ mode >>= 6;
+
+ else if (in_egroup_p(ns_root_gid))
+ mode >>= 3;
+
+ mode &= 7;
+
+ return (mode << 6) | (mode << 3) | mode;
+}
+
static struct ctl_table_root set_root = {
.lookup = set_lookup,
+ .permissions = mq_permissions,
+ .set_ownership = mq_set_ownership,
};
bool setup_mq_sysctls(struct ipc_namespace *ns)
--
2.43.0
After 25b21cb2f6d6 ("[PATCH] IPC namespace core") and 4e9823111bdc
("[PATCH] IPC namespace - shm") the shared memory page count stopped
being global and started counting per ipc namespace. The documentation
and shmget(2) still says that shmall is a global option.
shmget(2):
SHMALL System-wide limit on the total amount of shared memory, measured
in units of the system page size. On Linux, this limit can be read and
modified via /proc/sys/kernel/shmall.
I think the changes made in 2006 should be documented.
Signed-off-by: Alexey Gladkov <[email protected]>
Acked-by: "Eric W. Biederman" <[email protected]>
Link: https://lkml.kernel.org/r/ede20ddf7be48b93e8084c3be2e920841ee1a641.1663756794.git.legion@kernel.org
Signed-off-by: Eric W. Biederman <[email protected]>
---
Documentation/admin-guide/sysctl/kernel.rst | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index 6584a1f9bfe3..bc578663619d 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -594,6 +594,9 @@ default (``MSGMNB``).
``msgmni`` is the maximum number of IPC queues. 32000 by default
(``MSGMNI``).
+All of these parameters are set per ipc namespace. The maximum number of bytes
+in POSIX message queues is limited by ``RLIMIT_MSGQUEUE``. This limit is
+respected hierarchically in the each user namespace.
msg_next_id, sem_next_id, and shm_next_id (System V IPC)
========================================================
@@ -1274,15 +1277,20 @@ are doing anyway :)
shmall
======
-This parameter sets the total amount of shared memory pages that
-can be used system wide. Hence, ``shmall`` should always be at least
-``ceil(shmmax/PAGE_SIZE)``.
+This parameter sets the total amount of shared memory pages that can be used
+inside ipc namespace. The shared memory pages counting occurs for each ipc
+namespace separately and is not inherited. Hence, ``shmall`` should always be at
+least ``ceil(shmmax/PAGE_SIZE)``.
If you are not sure what the default ``PAGE_SIZE`` is on your Linux
system, you can run the following command::
# getconf PAGE_SIZE
+To reduce or disable the ability to allocate shared memory, you must create a
+new ipc namespace, set this parameter to the required value and prohibit the
+creation of a new ipc namespace in the current user namespace or cgroups can
+be used.
shmmax
======
--
2.43.0
Rootless containers are not allowed to modify kernel IPC parameters.
All default limits are set to such high values that in fact there are no
limits at all. All limits are not inherited and are initialized to
default values when a new ipc_namespace is created.
For new ipc_namespace:
size_t ipc_ns.shm_ctlmax = SHMMAX; // (ULONG_MAX - (1UL << 24))
size_t ipc_ns.shm_ctlall = SHMALL; // (ULONG_MAX - (1UL << 24))
int ipc_ns.shm_ctlmni = IPCMNI; // (1 << 15)
int ipc_ns.shm_rmid_forced = 0;
unsigned int ipc_ns.msg_ctlmax = MSGMAX; // 8192
unsigned int ipc_ns.msg_ctlmni = MSGMNI; // 32000
unsigned int ipc_ns.msg_ctlmnb = MSGMNB; // 16384
The shm_tot (total amount of shared pages) has also ceased to be
global, it is located in ipc_namespace and is not inherited from
anywhere.
In such conditions, it cannot be said that these limits limit anything.
The real limiter for them is cgroups.
If we allow rootless containers to change these parameters, then it can
only be reduced.
Signed-off-by: Alexey Gladkov <[email protected]>
Link: https://lkml.kernel.org/r/e2d84d3ec0172cfff759e6065da84ce0cc2736f8.1663756794.git.legion@kernel.org
Signed-off-by: Eric W. Biederman <[email protected]>
---
ipc/ipc_sysctl.c | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index 8c62e443f78b..01c4a50d22b2 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -14,6 +14,7 @@
#include <linux/ipc_namespace.h>
#include <linux/msg.h>
#include <linux/slab.h>
+#include <linux/cred.h>
#include "util.h"
static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
@@ -190,25 +191,57 @@ static int set_is_seen(struct ctl_table_set *set)
return ¤t->nsproxy->ipc_ns->ipc_set == set;
}
+static void ipc_set_ownership(struct ctl_table_header *head,
+ struct ctl_table *table,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct ipc_namespace *ns =
+ container_of(head->set, struct ipc_namespace, ipc_set);
+
+ kuid_t ns_root_uid = make_kuid(ns->user_ns, 0);
+ kgid_t ns_root_gid = make_kgid(ns->user_ns, 0);
+
+ *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID;
+ *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID;
+}
+
static int ipc_permissions(struct ctl_table_header *head, struct ctl_table *table)
{
int mode = table->mode;
#ifdef CONFIG_CHECKPOINT_RESTORE
- struct ipc_namespace *ns = current->nsproxy->ipc_ns;
+ struct ipc_namespace *ns =
+ container_of(head->set, struct ipc_namespace, ipc_set);
if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) ||
(table->data == &ns->ids[IPC_MSG_IDS].next_id) ||
(table->data == &ns->ids[IPC_SHM_IDS].next_id)) &&
checkpoint_restore_ns_capable(ns->user_ns))
mode = 0666;
+ else
#endif
- return mode;
+ {
+ kuid_t ns_root_uid;
+ kgid_t ns_root_gid;
+
+ ipc_set_ownership(head, table, &ns_root_uid, &ns_root_gid);
+
+ if (uid_eq(current_euid(), ns_root_uid))
+ mode >>= 6;
+
+ else if (in_egroup_p(ns_root_gid))
+ mode >>= 3;
+ }
+
+ mode &= 7;
+
+ return (mode << 6) | (mode << 3) | mode;
}
static struct ctl_table_root set_root = {
.lookup = set_lookup,
.permissions = ipc_permissions,
+ .set_ownership = ipc_set_ownership,
};
bool setup_ipc_sysctls(struct ipc_namespace *ns)
--
2.43.0