summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-08-03 23:36:38 +0200
committerChristian Brauner <brauner@kernel.org>2026-08-03 23:36:38 +0200
commit68aabd01ddd26ced458a9e5716a640eaf8e4b7a6 (patch)
tree9b5def7de1374c3a5707f3a3631a9ab32f0f9e8d /include/linux
parente98067e72ff81b054bda8135e1c348d13f54e553 (diff)
parenta0ff406303591e714ca82a3fede627f9f7c13231 (diff)
Merge patch series "binfmt_misc: bound the interpreters an entry can pre-open"
Christian Brauner <brauner@kernel.org> says: An 'F' entry opens its interpreter at registration and every exec runs a clone of it. A 'B' entry does the same for each interpreter it binds. That file stays open for as long as the entry lives. So it pins the file, its inode, the mount it came from and that mount's superblock. An entry binds at most 100 interpreters, but nothing caps the entries. binfmt_misc is container mountable so all of this is reachable by unprivileged users. While the pins go away when the instance is unmounted, it's still weird for an unprivileged namespace to be allowed to do this. And the fix is simple. Charge each binding to the user namespace and uid that makes it against a new UCOUNT_BINFMT_MISC_INTERPRETERS and refuse with -ENOSPC when the limit is hit. A per-instance cap won't do. Instances are keyed on the user namespace, so whatever constant I pick gets multiplied by however many namespaces the caller cares to create. inc_ucount() charges the namespace and every one of its ancestors, and a namespace can only ever raise its own limit, so nesting buys nothing. The knob is /proc/sys/user/max_binfmt_misc_interpreters, per namespace like every other ucount. I left it at the max_threads/2 default that fork_init() hands a new ucount type. Nothing anyone runs today comes anywhere near that. Selftests for all of it, including that a nested namespace can't buy itself budget. * patches from https://patch.msgid.link/20260803-work-binfmt_misc-interplimit-v1-0-4a2435500bd9@kernel.org: binfmt_misc: document the pre-opened interpreter limit selftests/exec: test the pre-opened interpreter limit binfmt_misc: correctly account pre-opened interpreters Link: https://patch.msgid.link/20260803-work-binfmt_misc-interplimit-v1-0-4a2435500bd9@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/binfmt_misc.h3
-rw-r--r--include/linux/user_namespace.h3
2 files changed, 6 insertions, 0 deletions
diff --git a/include/linux/binfmt_misc.h b/include/linux/binfmt_misc.h
index 072e4b3dd78d..8045b10dd3e5 100644
--- a/include/linux/binfmt_misc.h
+++ b/include/linux/binfmt_misc.h
@@ -7,6 +7,7 @@
struct bpf_prog;
struct file;
struct linux_binprm;
+struct ucounts;
struct user_namespace;
#define BINFMT_MISC_OPS_NAME_MAX 16
@@ -21,6 +22,7 @@ struct user_namespace;
* struct binfmt_misc_interp - an interpreter an entry was registered with
* @list: link in the entry's list, in registration order
* @file: the file, opened at registration and never resolved again
+ * @ucounts: the UCOUNT_BINFMT_MISC_INTERPRETERS charge the binding took
* @path: the path it was registered under, used as the name the interpreter
* runs under; stored after @name in the same allocation
* @name: the name the load program selects it by; empty for the fixed
@@ -33,6 +35,7 @@ struct user_namespace;
struct binfmt_misc_interp {
struct list_head list;
struct file *file;
+ struct ucounts *ucounts;
const char *path;
char name[];
};
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 9c3be157397e..e38d9e60569f 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -58,6 +58,9 @@ enum ucount_type {
UCOUNT_FANOTIFY_GROUPS,
UCOUNT_FANOTIFY_MARKS,
#endif
+#if IS_ENABLED(CONFIG_BINFMT_MISC)
+ UCOUNT_BINFMT_MISC_INTERPRETERS,
+#endif
UCOUNT_COUNTS,
};