diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-08-03 14:15:00 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-08-03 23:36:18 +0200 |
| commit | b604897764047229c6a931e1980cac1b2197d9d2 (patch) | |
| tree | 11c7bb5b86359a6b38fe025725b2b1dd2d9e66ce /kernel | |
| parent | e98067e72ff81b054bda8135e1c348d13f54e553 (diff) | |
binfmt_misc: correctly account pre-opened interpreters
An 'F' entry, and every interpreter a 'B' entry binds, holds a file open
from registration until the entry goes away, pinning the file, its inode,
the mount it came from and that mount's superblock. Nothing bounds how
many of those a user namespace can hold. An entry binds at most
BINFMT_MISC_INTERP_MAX interpreters, but nothing caps the entries.
Charge each binding to the user namespace and uid that makes it against a
new UCOUNT_BINFMT_MISC_INTERPRETERS. Going over budget causes -ENOSPC.
A per-instance cap would suck. Instances are keyed on the user
namespace. So any constant is multiplied by the number of namespaces the
caller creates. Creating those is virtually free. A ucount charges the
namespace and every one of its ancestors. And a namespace can raise only
its own limit. So nesting buys nothing.
The knob is /proc/sys/user/max_binfmt_misc_interpreters. Leave it at the
max_threads/2 default fork_init() gives a new type. No existing
configuration comes close to that.
binfmt_misc is tristate, which makes it the first ucount user that can be
built as a module. Export inc_ucount() and dec_ucount(); without them
CONFIG_BINFMT_MISC=m fails to link. Export them to binfmt_misc alone:
charging a ucount type is not something a module has any business doing
in general, and the list is trivial to extend if a second user shows up.
init_user_ns and init_binfmt_misc are already exported for the same
module.
Link: https://patch.msgid.link/20260803-work-binfmt_misc-interplimit-v1-1-4a2435500bd9@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/ucount.c | 6 |
1 files changed, 6 insertions, 0 deletions
diff --git a/kernel/ucount.c b/kernel/ucount.c index d6dc3e859f12..ec8b1445e287 100644 --- a/kernel/ucount.c +++ b/kernel/ucount.c @@ -4,6 +4,7 @@ #include <linux/sysctl.h> #include <linux/slab.h> #include <linux/cred.h> +#include <linux/export.h> #include <linux/hash.h> #include <linux/kmemleak.h> #include <linux/user_namespace.h> @@ -89,6 +90,9 @@ static const struct ctl_table user_table[] = { UCOUNT_ENTRY("max_fanotify_groups"), UCOUNT_ENTRY("max_fanotify_marks"), #endif +#if IS_ENABLED(CONFIG_BINFMT_MISC) + UCOUNT_ENTRY("max_binfmt_misc_interpreters"), +#endif }; #endif /* CONFIG_SYSCTL */ @@ -233,6 +237,7 @@ fail: put_ucounts(ucounts); return NULL; } +EXPORT_SYMBOL_FOR_MODULES(inc_ucount, "binfmt_misc"); void dec_ucount(struct ucounts *ucounts, enum ucount_type type) { @@ -243,6 +248,7 @@ void dec_ucount(struct ucounts *ucounts, enum ucount_type type) } put_ucounts(ucounts); } +EXPORT_SYMBOL_FOR_MODULES(dec_ucount, "binfmt_misc"); long inc_rlimit_ucounts(struct ucounts *ucounts, enum rlimit_type type, long v) { |
