list_for_each_entry_safe(interp, tmp, &e->interps, list) {
list_del(&interp->list);
close_interp_file(interp->file);
+ dec_ucount(interp->ucounts, UCOUNT_BINFMT_MISC_INTERPRETERS);
kfree(interp);
}
}
* The caller has to have validated @name and @path, established that @e
* cannot be matched yet, and owns @f until this succeeds.
*
- * Return: 0 on success, a negative errno on failure
+ * Return: 0 on success, -ENOSPC if the entry is full or the binder is out of
+ * UCOUNT_BINFMT_MISC_INTERPRETERS budget, a negative errno on failure
*/
static int entry_attach_interpreter(struct binfmt_misc_entry *e,
const char *name, const char *path,
{
size_t nlen = strlen(name), plen = strlen(path);
struct binfmt_misc_interp *interp;
+ struct ucounts *ucounts;
if (binfmt_misc_find_interp(&e->interps, name))
return -EEXIST;
if (list_count_nodes(&e->interps) >= BINFMT_MISC_INTERP_MAX)
return -ENOSPC;
+ /* The binding keeps a file open, so charge it to whoever binds it. */
+ ucounts = inc_ucount(current_user_ns(), current_euid(),
+ UCOUNT_BINFMT_MISC_INTERPRETERS);
+ if (!ucounts)
+ return -ENOSPC;
+
/* One allocation, both strings in it, like the entry's own buffer. */
interp = kmalloc(struct_size(interp, name, nlen + plen + 2),
GFP_KERNEL_ACCOUNT);
- if (!interp)
+ if (!interp) {
+ dec_ucount(ucounts, UCOUNT_BINFMT_MISC_INTERPRETERS);
return -ENOMEM;
+ }
interp->path = interp->name + nlen + 1;
strscpy(interp->name, name, nlen + 1);
strscpy(interp->name + nlen + 1, path, plen + 1);
interp->file = f;
+ interp->ucounts = ucounts;
/* Publish the node: a lockless cat may be walking the list. */
list_add_tail_rcu(&interp->list, &e->interps);
pr_debug("register: interpreter: %s {%s}\n", name, path);
struct bpf_prog;
struct file;
struct linux_binprm;
+struct ucounts;
struct user_namespace;
#define BINFMT_MISC_OPS_NAME_MAX 16
* 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
struct binfmt_misc_interp {
struct list_head list;
struct file *file;
+ struct ucounts *ucounts;
const char *path;
char name[];
};
#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>
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 */
put_ucounts(ucounts);
return NULL;
}
+EXPORT_SYMBOL_FOR_MODULES(inc_ucount, "binfmt_misc");
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)
{