summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2023-09-15 11:00:33 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2023-09-15 11:00:33 +1000
commit6c2024d65172a79512e4d6f9a2e52d1d247b8587 (patch)
tree20e65e8d4f3d9f0625d8d58f62ce5c4b76c06ca4
parent5131835778cfb2ab15b1a0731ebd668b55a501f0 (diff)
parent05bd6e0242b4c059ec6797e3d721ecca6d15ff8d (diff)
downloadlinux-6c2024d65172a79512e4d6f9a2e52d1d247b8587.tar.gz
linux-6c2024d65172a79512e4d6f9a2e52d1d247b8587.tar.xz
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git
-rw-r--r--Documentation/admin-guide/sysctl/kernel.rst14
-rw-r--r--ipc/ipc_sysctl.c37
-rw-r--r--ipc/mq_sysctl.c36
-rw-r--r--kernel/ucount.c16
4 files changed, 89 insertions, 14 deletions
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index cf33de56da27..fddd71aeebcb 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -597,6 +597,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)
========================================================
@@ -1276,15 +1279,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
======
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 &current->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)
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 &current->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)
diff --git a/kernel/ucount.c b/kernel/ucount.c
index 4aa6166cb856..e188c25ed2b3 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -213,18 +213,16 @@ void put_ucounts(struct ucounts *ucounts)
}
}
-static inline bool atomic_long_inc_below(atomic_long_t *v, int u)
+static inline bool atomic_long_inc_below(atomic_long_t *v, long u)
{
- long c, old;
- c = atomic_long_read(v);
- for (;;) {
+ long c = atomic_long_read(v);
+
+ do {
if (unlikely(c >= u))
return false;
- old = atomic_long_cmpxchg(v, c, c+1);
- if (likely(old == c))
- return true;
- c = old;
- }
+ } while (!atomic_long_try_cmpxchg(v, &c, c+1));
+
+ return true;
}
struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,