[PATCH RFC v3 29/90] cells: add the "ids" slot, for a single id

From: Josef Bacik

Date: Thu Nov 05 2026 - 10:17:14 EST


CELL_SET_BINARY with the key "ids" sets how many ids the cell has. Start
with the value 1. Such a cell runs with the uid and gid of the user who
created it, and its tasks see those ids under the same numbers.

This is refused for root, because too many checks in the kernel look at
the bare value of kuid 0, with or without capabilities. It is also refused
for an id of the window, as map_write() refuses to map one into an
ordinary user namespace.

Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
kernel/cell/Makefile | 1 +
kernel/cell/ids.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/cell/internal.h | 3 +++
kernel/cell/slots.c | 14 ++++++++++++
4 files changed, 80 insertions(+)

diff --git a/kernel/cell/Makefile b/kernel/cell/Makefile
index ac48bfb289d0..61ca8e9cb216 100644
--- a/kernel/cell/Makefile
+++ b/kernel/cell/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += core.o
obj-y += slots.o
+obj-y += ids.o
diff --git a/kernel/cell/ids.c b/kernel/cell/ids.c
new file mode 100644
index 000000000000..aaf7e7ed9826
--- /dev/null
+++ b/kernel/cell/ids.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cells: the ids of a cell.
+ */
+#include <linux/user_namespace.h>
+
+#include "internal.h"
+
+/* The id of whoever made the cell, under its own number. */
+static int cell_set_one_id(struct cell *cell)
+{
+ struct user_namespace *ns = cell->user_ns;
+ struct uid_gid_extent uids = {
+ .first = from_kuid(ns->parent, ns->owner),
+ .lower_first = __kuid_val(ns->owner),
+ .count = 1,
+ };
+ struct uid_gid_extent gids = {
+ .first = from_kgid(ns->parent, ns->group),
+ .lower_first = __kgid_val(ns->group),
+ .count = 1,
+ };
+
+ /* Too much goes by the bare value of kuid 0, capabilities or none. */
+ if (uid_eq(ns->owner, GLOBAL_ROOT_UID))
+ return -EPERM;
+ /* Nor an id of the window, as for a legacy user namespace. */
+ if (cell_id_in_window(__kuid_val(ns->owner)) ||
+ cell_id_in_window(__kgid_val(ns->group)))
+ return -EPERM;
+ return userns_set_cell_map(ns, &uids, &gids, 0, NULL);
+}
+
+/**
+ * cell_set_ids - fill the "ids" slot
+ * @cell: a cell that is being built
+ * @count: how many ids it is to have, 1 or CELL_ID_BLOCK_SIZE
+ *
+ * Context: The caller holds the lock of the cell.
+ * Return: 0, -EBUSY if the slot is taken, -EINVAL for any other @count, -EPERM
+ * if whoever made the cell cannot have a cell of one id, -ENOSPC if there is no
+ * block to be had.
+ */
+int cell_set_ids(struct cell *cell, u32 count)
+{
+ int ret;
+
+ if (cell->ids_count)
+ return -EBUSY;
+
+ switch (count) {
+ case 1:
+ ret = cell_set_one_id(cell);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!ret)
+ cell->ids_count = count;
+ return ret;
+}
diff --git a/kernel/cell/internal.h b/kernel/cell/internal.h
index 01a7a8581ff7..c7a1895eadd4 100644
--- a/kernel/cell/internal.h
+++ b/kernel/cell/internal.h
@@ -59,4 +59,7 @@ static inline struct cell *cell_from_fd(const struct fd *f)
return cell_from_file(fd_file(*f)) ?: ERR_PTR(-EINVAL);
}

+/* ids.c */
+int cell_set_ids(struct cell *cell, u32 count);
+
#endif /* _KERNEL_CELL_INTERNAL_H */
diff --git a/kernel/cell/slots.c b/kernel/cell/slots.c
index b3dfb32b1345..cc732c4e09f9 100644
--- a/kernel/cell/slots.c
+++ b/kernel/cell/slots.c
@@ -73,6 +73,18 @@ static int cell_set_root(struct cell *cell, struct file *file)
return 0;
}

+static int cell_set_ids_binary(struct cell *cell, const void __user *uvalue,
+ size_t len)
+{
+ u32 count;
+
+ if (len != sizeof(count))
+ return -EINVAL;
+ if (copy_from_user(&count, uvalue, sizeof(count)))
+ return -EFAULT;
+ return cell_set_ids(cell, count);
+}
+
static int cell_set_string(struct cell *cell, const char *key, const char *val)
{
if (!strcmp(key, "hostname"))
@@ -83,6 +95,8 @@ static int cell_set_string(struct cell *cell, const char *key, const char *val)
static int cell_set_binary(struct cell *cell, const char *key,
const void __user *uvalue, size_t len)
{
+ if (!strcmp(key, "ids"))
+ return cell_set_ids_binary(cell, uvalue, len);
return -EINVAL;
}


--
2.55.0