Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701// SPDX-License-Identifier: GPL-2.0-only/* * binfmt_misc.c * * Copyright (C) 1997 Richard Günther * * binfmt_misc detects binaries via a magic or filename extension and invokes * a specified wrapper. See Documentation/admin-guide/binfmt-misc.rst for more details. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/array_size.h>#include <linux/binfmt_misc.h>#include <linux/binfmts.h>#include <linux/bitops.h>#include <linux/bits.h>#include <linux/bug.h>#include <linux/cleanup.h>#include <linux/cred.h>#include <linux/ctype.h>#include <linux/file.h>#include <linux/fs.h>#include <linux/fs_context.h>#include <linux/init.h>#include <linux/kstrtox.h>#include <linux/limits.h>#include <linux/list.h>#include <linux/magic.h>#include <linux/module.h>#include <linux/printk.h>#include <linux/rculist.h>#include <linux/refcount.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/srcu.h>#include <linux/string.h>#include <linux/string_helpers.h>#include <linux/uaccess.h>#include <linux/user_namespace.h>
#include "internal.h"
/* Entry status and match type bit numbers. */enum binfmt_misc_entry_bits { MISC_FMT_ENABLED_BIT = 0, MISC_FMT_MAGIC_BIT = 1, MISC_FMT_BPF_BIT = 2,};
/* Entry behavior flags, fixed at registration time. */enum binfmt_misc_entry_flags { MISC_FMT_PRESERVE_ARGV0 = (1U << 31), MISC_FMT_OPEN_BINARY = (1U << 30), MISC_FMT_CREDENTIALS = (1U << 29), MISC_FMT_OPEN_FILE = (1U << 28), MISC_FMT_TRANSPARENT = (1U << 27), MISC_FMT_LOADER = (1U << 26), MISC_FMT_DISABLED = (1U << 25),};
/* The flags that shape the invocation; a 'B' handler picks those per exec. */#define MISC_FMT_INVOCATION_FLAGS (MISC_FMT_PRESERVE_ARGV0 | \ MISC_FMT_OPEN_BINARY | \ MISC_FMT_CREDENTIALS | \ MISC_FMT_OPEN_FILE | \ MISC_FMT_TRANSPARENT | \ MISC_FMT_LOADER)
/** * struct binfmt_misc_flag - a flag character of the register string * @c: the character userspace writes and reads back * @flag: the entry flag it sets * @implies: entry flags it turns on in addition * @desc: what it does, for the registration debug output */struct binfmt_misc_flag { char c; unsigned long flag; unsigned long implies; const char *desc;};
static const struct binfmt_misc_flag misc_flags[] = { { 'P', MISC_FMT_PRESERVE_ARGV0, 0, "preserve argv0" }, { 'O', MISC_FMT_OPEN_BINARY, 0, "open binary" }, { 'C', MISC_FMT_CREDENTIALS, MISC_FMT_OPEN_BINARY, "credentials from the binary" }, { 'F', MISC_FMT_OPEN_FILE, 0, "open interpreter file now" }, { 'T', MISC_FMT_TRANSPARENT, MISC_FMT_OPEN_BINARY, "transparent" }, { 'L', MISC_FMT_LOADER, 0, "loader substitution" }, { 'D', MISC_FMT_DISABLED, 0, "register disabled" },};
/* Look up a flag character, NULL if @c is not one. */static const struct binfmt_misc_flag *misc_flag_by_char(const char c){ for (int i = 0; i < ARRAY_SIZE(misc_flags); i++) if (misc_flags[i].c == c) return &misc_flags[i]; return NULL;}
static bool misc_valid_delim(const char c){ if (!isascii(c) || !ispunct(c)) return false; return c != '\\';}
struct binfmt_misc_entry { struct hlist_node node; unsigned long flags; /* type, status, etc. */ int offset; /* offset of magic */ int size; /* size of magic/mask */ char *magic; /* magic or filename extension */ char *mask; /* mask, NULL for exact match */ const char *interpreter; /* filename of interpreter */ char *name; struct dentry *dentry; const struct binfmt_misc_ops *bpf_ops; /* bpf-backed handler ('B') */ const char *bpf_ops_name; struct list_head interps; /* the interpreters it bound */ refcount_t users; /* sync removal with load_misc_binary() */ struct rcu_head rcu; char buf[]; /* register string, fields point in here */};
/* * Max length of the register string. Determined by: * - 7 delimiters * - name: ~50 bytes * - type: 1 byte * - offset: 3 bytes (has to be smaller than BINPRM_BUF_SIZE) * - magic: 128 bytes (512 in escaped form) * - mask: 128 bytes (512 in escaped form) * - interp: ~50 bytes * - flags: 5 bytes * Round that up a bit, and then back off to hold the internal data * (like struct binfmt_misc_entry). */#define MAX_REGISTER_LENGTH 1920
/* Trailing delimiter pad so field parsing always terminates at a delimiter. */#define MISC_DELIM_PAD 8
/* Protects the entry walk in load_misc_binary(), which may sleep in it. */DEFINE_STATIC_SRCU_FAST(bm_entries_srcu);
/* Check if @e's magic matches @bprm's buffer, applying the mask if set. */static bool entry_matches_magic(const struct binfmt_misc_entry *e, const struct linux_binprm *bprm){ const char *s = bprm->buf + e->offset; int i;
if (!e->mask) return !memcmp(s, e->magic, e->size);
for (i = 0; i < e->size; i++) if ((s[i] ^ e->magic[i]) & e->mask[i]) return false; return true;}
/* Check if @e's registered extension matches @ext, NULL if there is none. */static bool entry_matches_extension(const struct binfmt_misc_entry *e, const char *ext){ return ext && !strcmp(e->magic, ext);}
/** * search_binfmt_handler - search for a binary handler for @bprm * @misc: handle to binfmt_misc instance * @bprm: binary for which we are looking for a handler * * Search for a binary type handler for @bprm in the list of registered binary * type handlers. A 'B' entry's match program decides whether the handler * applies; it may sleep to read the binary. The matched entry is returned * with a reference taken while the walk still held it; a dying entry - * unlinked with its last reference gone - cannot be matched and the walk * moves on. * * The caller must hold the bm_entries_srcu read lock, which allows an * entry's evaluation to sleep. * * Return: referenced binary type list entry on success, NULL on failure */static struct binfmt_misc_entry *search_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm){ char *dot = strrchr(bprm->interp, '.'); const char *ext = dot ? dot + 1 : NULL; struct binfmt_misc_entry *e;
/* Walk all the registered handlers. */ hlist_for_each_entry_rcu(e, &misc->entries, node, srcu_read_lock_held(&bm_entries_srcu)) { /* * Make sure this one is currently enabled. An entry enters * the list at most once and only whole: its configuration is * ordered before the rcu insertion that makes it visible * here. */ if (!test_bit(MISC_FMT_ENABLED_BIT, &e->flags)) continue;
if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) { if (!e->bpf_ops->match(bprm)) continue; } else if (test_bit(MISC_FMT_MAGIC_BIT, &e->flags)) { if (!entry_matches_magic(e, bprm)) continue; } else { if (!entry_matches_extension(e, ext)) continue; }
/* A dying entry cannot be matched, walk on. */ if (refcount_inc_not_zero(&e->users)) return e; }
return NULL;}
/** * get_binfmt_handler - try to find a binary type handler * @misc: handle to binfmt_misc instance * @bprm: binary for which we are looking for a handler * * Try to find a binfmt handler for the binary type. If one is found it is * returned with a reference protecting it against removal via * bm_{entry,status}_write(). * * Return: binary type list entry on success, NULL on failure */static struct binfmt_misc_entry *get_binfmt_handler(struct binfmt_misc *misc, struct linux_binprm *bprm){ guard(srcu_fast)(&bm_entries_srcu); return search_binfmt_handler(misc, bprm);}
/** * binfmt_misc_find_interp - find a bound interpreter by name * @interps: the interpreters the matched entry was registered with * @name: the name to look for * * Return: the interpreter on success, NULL if @interps has none by that name */const struct binfmt_misc_interp *binfmt_misc_find_interp(const struct list_head *interps, const char *name){ struct binfmt_misc_interp *interp;
list_for_each_entry(interp, interps, list) if (!strcmp(interp->name, name)) return interp; return NULL;}
/* Undo the open_exec() a pre-opened interpreter file came from. */static void close_interp_file(struct file *f){ if (IS_ERR_OR_NULL(f)) return; exe_file_allow_write_access(f); filp_close(f, NULL);}
DEFINE_FREE(close_interp_file, struct file *, close_interp_file(_T))
/* * Open an interpreter @path for execution: now, in the writer's context, * and - since binfmt_misc mounts can be unprivileged - with @cred, the * credentials the control file being written was opened with, not the * writer's own. */static struct file *open_interp_file(const struct cred *cred, const char *path){ struct file *f;
scoped_with_creds(cred) f = open_exec(path); if (IS_ERR(f)) pr_notice("register: failed to install interpreter %s\n", path); return f;}
/* Release the interpreters an entry was registered with. */static void entry_put_interpreters(struct binfmt_misc_entry *e){ struct binfmt_misc_interp *interp, *tmp;
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); }}
/** * entry_attach_interpreter - bind an opened interpreter to @e * @e: entry being configured * @name: name the load program will select it by; empty for the fixed * interpreter of a static entry * @path: the path @f was opened from * @f: the interpreter, opened for execution * * Every exec runs a clone of @f, so the path decided which file is bound * and nothing else: it is not resolved again, in any namespace. * * 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, -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, struct file *f){ 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_flex(*interp, name, nlen + plen + 2, GFP_KERNEL_ACCOUNT); 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); return 0;}
static void bm_entry_free_rcu(struct rcu_head *rcu){ struct binfmt_misc_entry *e = container_of(rcu, struct binfmt_misc_entry, rcu);
/* No walker that could sleep in the handler's programs is left. */ if (e->bpf_ops) binfmt_misc_put_ops(e->bpf_ops); kfree(e);}
/** * put_binfmt_handler - put binary handler entry * @e: entry to put * * Free entry syncing with load_misc_binary() and defer final free to * load_misc_binary() in case it is using the binary type handler we were * requested to remove. Also the teardown for a registration that fails * before add_entry() publishes the entry. */static void put_binfmt_handler(struct binfmt_misc_entry *e){ if (IS_ERR_OR_NULL(e)) return;
if (refcount_dec_and_test(&e->users)) { entry_put_interpreters(e); /* Walkers may still dereference this entry, even sleeping. */ call_srcu(&bm_entries_srcu, &e->rcu, bm_entry_free_rcu); }}
DEFINE_FREE(put_binfmt_handler, struct binfmt_misc_entry *, put_binfmt_handler(_T))
/* Drop everything a load program staged for this exec. */static void drop_staged_selection(struct linux_binprm *bprm){ kfree(bprm->bpf_interp); bprm->bpf_interp = NULL; kfree(bprm->bpf_interp_arg); bprm->bpf_interp_arg = NULL; if (bprm->bpf_interp_file) { fput(bprm->bpf_interp_file); bprm->bpf_interp_file = NULL; } bprm->bpf_flags = 0;}
/** * current_binfmt_misc - get the binfmt_misc instance of the caller's user namespace * * If a user namespace doesn't have its own binfmt_misc mount it uses the * handlers of its closest ancestor with one. This mimics the behavior of * pre-namespaced binfmt_misc where all registered handlers were available * to all users and user namespaces on the system. The init user namespace * instance is statically set up so the fallback is never reached in * practice. * * Return: the binfmt_misc instance of the caller's user namespace */static struct binfmt_misc *current_binfmt_misc(void){ const struct user_namespace *user_ns; struct binfmt_misc *misc;
for (user_ns = current_user_ns(); user_ns; user_ns = user_ns->parent) { /* Pairs with smp_store_release() in bm_fill_super(). */ misc = smp_load_acquire(&user_ns->binfmt_misc); if (misc) return misc; }
return &init_binfmt_misc;}
/** * entry_select_interpreter - get the interpreter for the matched @e * @e: matched binary type handler * @bprm: binary that is being executed * * A static entry carries its interpreter path, for a 'B' entry the * handler's load program selects it, either by path or by the name of one * of the interpreters the entry bound. The match is committed, so a failing * program fails the exec. * * Return: the interpreter on success, an ERR_PTR on failure */static const char *entry_select_interpreter(const struct binfmt_misc_entry *e, struct linux_binprm *bprm){ int retval;
/* * Drop what a previous chain level staged before anything can pick it * up. A static entry stages nothing but consumes a staged file just * like a 'B' entry does. */ drop_staged_selection(bprm);
if (!test_bit(MISC_FMT_BPF_BIT, &e->flags)) return e->interpreter;
/* The interpreters this entry lets the program choose from. */ bprm->bpf_interps = &e->interps; retval = e->bpf_ops->load(bprm); bprm->bpf_interps = NULL; if (retval) { /* Keep a program-supplied error within errno range. */ if (retval > 0 || retval < -MAX_ERRNO) retval = -ENOEXEC; goto drop_staged; }
/* Selecting an interpreter is part of the contract. */ if (!bprm->bpf_interp) { retval = -ENOEXEC; goto drop_staged; }
return bprm->bpf_interp;
drop_staged: /* A failing load leaves nothing behind for later entries. */ drop_staged_selection(bprm); return ERR_PTR(retval);}
/** * entry_invocation_flags - the invocation flags in effect for this exec * @e: matched binary type handler * @bprm: binary that is being executed * * A static entry fixes its flags at registration, a 'B' entry's load program * picks them per exec with bpf_binprm_set_flags(). Translate the latter into * the former, implications included, so the dispatch has one set to act on. * * Return: the invocation flags for this exec */static unsigned long entry_invocation_flags(const struct binfmt_misc_entry *e, struct linux_binprm *bprm){ unsigned long flags = 0; u64 bpf_flags;
if (!test_bit(MISC_FMT_BPF_BIT, &e->flags)) return e->flags;
bpf_flags = bprm->bpf_flags; /* Clear so they can't accumulate into a nested interpreter level. */ bprm->bpf_flags = 0;
if (bpf_flags & BPF_BINPRM_PRESERVE_ARGV0) flags |= MISC_FMT_PRESERVE_ARGV0; if (bpf_flags & BPF_BINPRM_EXECFD) flags |= MISC_FMT_OPEN_BINARY; if (bpf_flags & BPF_BINPRM_CREDENTIALS) flags |= MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_BINARY; if (bpf_flags & BPF_BINPRM_TRANSPARENT) flags |= MISC_FMT_TRANSPARENT | MISC_FMT_OPEN_BINARY; if (bpf_flags & BPF_BINPRM_LOADER) flags |= MISC_FMT_LOADER;
return flags;}
/** * entry_open_interpreter - open the entry's interpreter for execution * @e: matched binary type handler * @bprm: binary that is being executed * @interpreter: the interpreter selected for this exec * * An 'F' entry hands out a clone of the file it pre-opened at registration, * and so does a 'B' entry whose load program selected one of the * interpreters it bound. Any other entry opens the selected path. * * Return: the opened interpreter on success, an ERR_PTR on failure */static struct file *entry_open_interpreter(const struct binfmt_misc_entry *e, struct linux_binprm *bprm, const char *interpreter){ struct file *interp_file __free(fput) = NULL; struct binfmt_misc_interp *interp; struct file *bound; int retval;
if (bprm->bpf_interp_file) { bound = bprm->bpf_interp_file; } else if (e->flags & MISC_FMT_OPEN_FILE) { /* An 'F' entry pre-opened exactly one interpreter. */ interp = list_first_entry(&e->interps, struct binfmt_misc_interp, list); bound = interp->file; } else { return open_exec(interpreter); }
interp_file = file_clone_open(bound); if (IS_ERR(interp_file)) return interp_file;
retval = exe_file_deny_write_access(interp_file); if (retval) return ERR_PTR(retval);
return no_free_ptr(interp_file);}
/** * build_interp_argv - splice the interpreter invocation into the argv * @bprm: binary that is being executed * @interpreter: the interpreter selected for this exec * @flags: invocation flags in effect for this exec * * The interpreter becomes argv[0] and the binary its last argument, with an * optional staged argument in between. The caller's argv[0] is dropped * unless 'P' keeps it. * * Return: 0 on success, a negative error code on failure */static int build_interp_argv(struct linux_binprm *bprm, const char *interpreter, unsigned long flags){ int retval;
/* The interpreter has to be able to load the binary by path. */ if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE) return -ENOENT;
/* The entry's own choice - not one accumulated from an earlier level. */ if (flags & MISC_FMT_PRESERVE_ARGV0) { bprm->interp_flags |= BINPRM_FLAGS_PRESERVE_ARGV0; } else { retval = remove_arg_zero(bprm); if (retval) return retval; }
/* make the binary the last argument to the interpreter */ retval = copy_string_kernel(bprm->interp, bprm); if (retval < 0) return retval; bprm->argc++;
/* * A single optional argument to the interpreter, inserted between it * and the binary just like the argument of a #! interpreter line. */ if (bprm->bpf_interp_arg) { retval = copy_string_kernel(bprm->bpf_interp_arg, bprm); if (retval < 0) return retval; bprm->argc++; /* Consumed - don't let it leak into a nested interpreter's argv. */ kfree(bprm->bpf_interp_arg); bprm->bpf_interp_arg = NULL; }
/* add the interp as argv[0] */ retval = copy_string_kernel(interpreter, bprm); if (retval < 0) return retval; bprm->argc++;
return 0;}
/* * the loader itself */static int load_misc_binary(struct linux_binprm *bprm){ struct binfmt_misc_entry *fmt __free(put_binfmt_handler) = NULL; const char *interpreter; struct file *interp_file; struct binfmt_misc *misc; unsigned long flags; int retval;
/* Only binfmt_misc stages one and exec_binprm() clears it per round. */ WARN_ON_ONCE(bprm->loader);
misc = current_binfmt_misc(); if (!READ_ONCE(misc->enabled)) return -ENOEXEC;
fmt = get_binfmt_handler(misc, bprm); if (!fmt) return -ENOEXEC;
interpreter = entry_select_interpreter(fmt, bprm); if (IS_ERR(interpreter)) return PTR_ERR(interpreter);
flags = entry_invocation_flags(fmt, bprm);
/* No argv is built for a staged argument to land in. */ if ((flags & (MISC_FMT_LOADER | MISC_FMT_TRANSPARENT)) && bprm->bpf_interp_arg) return -EINVAL;
/* * Stash the interpreter for binfmt_elf to consume in place of the * binary's PT_INTERP and decline the match, so the search continues * to the real format in the same round. */ if (flags & MISC_FMT_LOADER) { interp_file = entry_open_interpreter(fmt, bprm, interpreter); if (IS_ERR(interp_file)) { retval = PTR_ERR(interp_file); /* Declining here would run the binary's own PT_INTERP. */ return retval == -ENOEXEC ? -EACCES : retval; }
bprm->loader = interp_file; return -ENOEXEC; }
if (!(flags & MISC_FMT_TRANSPARENT)) { retval = build_interp_argv(bprm, interpreter, flags); if (retval) return retval; }
/* Update interp for the next round; sched_prepare_exec reports it. */ retval = bprm_change_interp(interpreter, bprm); if (retval < 0) return retval;
interp_file = entry_open_interpreter(fmt, bprm, interpreter); if (IS_ERR(interp_file)) return PTR_ERR(interp_file);
/* Raise only past the last failure, or an -ENOEXEC decline leaks it. */ if (flags & MISC_FMT_TRANSPARENT) bprm->interp_flags |= BINPRM_FLAGS_TRANSPARENT_INTERP;
bprm->interpreter = interp_file; if (flags & MISC_FMT_OPEN_BINARY) bprm->have_execfd = 1; if (flags & MISC_FMT_CREDENTIALS) bprm->execfd_creds = 1; return 0;}
/* Command parsers */
/* * Scan the argument starting at @s up to the delimiter @del, recognising * the \x escape. Terminates the argument with a NUL and returns a pointer * past it or NULL on a malformed escape. */static char *scanarg(char *s, char del){ char c;
while ((c = *s++) != del) { if (c == '\\' && *s == 'x') { s++; if (!isxdigit(*s++)) return NULL; if (!isxdigit(*s++)) return NULL; } } s[-1] = '\0'; return s;}
/* Parse the 'flags' field, stopping at the first character that is not one. */static char *check_special_flags(char *p, struct binfmt_misc_entry *e){ for (;; p++) { const struct binfmt_misc_flag *f = misc_flag_by_char(*p);
if (!f) return p; pr_debug("register: flag: %c (%s)\n", f->c, f->desc); e->flags |= f->flag | f->implies; }}
/* Parse the 'offset', 'magic' and 'mask' fields of an 'M' entry. */static char *parse_magic_fields(struct binfmt_misc_entry *e, char *p, char del){ char *s;
/* Parse the 'offset' field. */ s = strchr(p, del); if (!s) return NULL; *s = '\0'; if (p != s) { if (kstrtoint(p, 10, &e->offset) || e->offset < 0) return NULL; } p = s + 1; pr_debug("register: offset: %#x\n", e->offset);
/* Parse the 'magic' field. */ e->magic = p; p = scanarg(p, del); if (!p || !e->magic[0]) return NULL; print_hex_dump_debug( KBUILD_MODNAME ": register: magic[raw]: ", DUMP_PREFIX_NONE, 16, 1, e->magic, p - e->magic, true);
/* Parse the 'mask' field. */ e->mask = p; p = scanarg(p, del); if (!p) return NULL; if (!e->mask[0]) { e->mask = NULL; pr_debug("register: mask[raw]: none\n"); } else { print_hex_dump_debug( KBUILD_MODNAME ": register: mask[raw]: ", DUMP_PREFIX_NONE, 16, 1, e->mask, p - e->mask, true); }
/* * Decode the magic & mask fields. Note: while we might have accepted * embedded NUL bytes from above, the unescape helpers will stop at * the first one they encounter. */ e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX); if (e->mask && string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size) return NULL; if (e->size > BINPRM_BUF_SIZE || BINPRM_BUF_SIZE - e->size < e->offset) return NULL; pr_debug("register: magic/mask length: %i\n", e->size); print_hex_dump_debug( KBUILD_MODNAME ": register: magic[decoded]: ", DUMP_PREFIX_NONE, 16, 1, e->magic, e->size, true); if (e->mask) print_hex_dump_debug( KBUILD_MODNAME ": register: mask[decoded]: ", DUMP_PREFIX_NONE, 16, 1, e->mask, e->size, true); return p;}
/* Parse the 'magic' field of an 'E' entry: the filename extension. */static char *parse_extension_fields(struct binfmt_misc_entry *e, char *p, char del){ /* Skip the 'offset' field. */ p = strchr(p, del); if (!p) return NULL; *p++ = '\0';
/* Parse the 'magic' field. */ e->magic = p; p = strchr(p, del); if (!p) return NULL; *p++ = '\0'; if (!e->magic[0] || strchr(e->magic, '/')) return NULL; pr_debug("register: extension: {%s}\n", e->magic);
/* Skip the 'mask' field. */ p = strchr(p, del); if (!p) return NULL; *p++ = '\0'; return p;}
/* * Parse the fields of a 'B' entry: the 'offset', 'magic' and 'mask' fields * must be empty. The handler name is carried in the 'interpreter' field. */static char *parse_bpf_fields(struct binfmt_misc_entry *e, char *p, char del){ /* The 'offset' field must be empty. */ if (*p++ != del) return NULL;
/* The 'magic' field must be empty. */ if (*p++ != del) return NULL;
/* The 'mask' field must be empty. */ if (*p++ != del) return NULL;
return p;}
/* * This registers a new binary format, it recognises the syntax * ':name:type:offset:magic:mask:interpreter:flags' * where the ':' is the IFS, that can be chosen with the first char */static struct binfmt_misc_entry *create_entry(const char __user *buffer, size_t count){ struct binfmt_misc_entry *e __free(kfree) = NULL; char *buf, *p; char del;
pr_debug("register: received %zu bytes\n", count);
/* some sanity checks */ if ((count < 11) || (count > MAX_REGISTER_LENGTH)) return ERR_PTR(-EINVAL);
e = kmalloc_flex(*e, buf, count + MISC_DELIM_PAD, GFP_KERNEL_ACCOUNT); if (!e) return ERR_PTR(-ENOMEM);
p = buf = e->buf;
memset(e, 0, sizeof(*e)); INIT_LIST_HEAD(&e->interps); if (copy_from_user(buf, buffer, count)) return ERR_PTR(-EFAULT);
del = *p++; /* delimiter */
pr_debug("register: delim: %#x\n", del);
if (!misc_valid_delim(del)) return ERR_PTR(-EINVAL);
/* Pad the buffer with the delim to simplify parsing below. */ memset(buf + count, del, MISC_DELIM_PAD);
/* Parse the 'name' field. */ e->name = p; p = strchr(p, del); if (!p) return ERR_PTR(-EINVAL); *p++ = '\0'; if (!e->name[0] || !strcmp(e->name, ".") || !strcmp(e->name, "..") || strchr(e->name, '/')) return ERR_PTR(-EINVAL);
pr_debug("register: name: {%s}\n", e->name);
/* Parse the 'type' field. */ switch (*p++) { case 'E': pr_debug("register: type: E (extension)\n"); e->flags = BIT(MISC_FMT_ENABLED_BIT); break; case 'M': pr_debug("register: type: M (magic)\n"); e->flags = BIT(MISC_FMT_ENABLED_BIT) | BIT(MISC_FMT_MAGIC_BIT); break; case 'B': pr_debug("register: type: B (bpf)\n"); if (!IS_ENABLED(CONFIG_BINFMT_MISC_BPF)) return ERR_PTR(-EINVAL); e->flags = BIT(MISC_FMT_ENABLED_BIT) | BIT(MISC_FMT_BPF_BIT); break; default: return ERR_PTR(-EINVAL); } if (*p++ != del) return ERR_PTR(-EINVAL);
if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) p = parse_bpf_fields(e, p, del); else if (test_bit(MISC_FMT_MAGIC_BIT, &e->flags)) p = parse_magic_fields(e, p, del); else p = parse_extension_fields(e, p, del); if (!p) return ERR_PTR(-EINVAL);
/* Parse the 'interpreter' field. */ e->interpreter = p; p = strchr(p, del); if (!p) return ERR_PTR(-EINVAL); *p++ = '\0'; if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) { /* The 'interpreter' field carries the handler name. */ e->bpf_ops_name = e->interpreter; e->interpreter = NULL; if (!e->bpf_ops_name[0]) return ERR_PTR(-EINVAL); pr_debug("register: bpf handler: {%s}\n", e->bpf_ops_name); } else if (!e->interpreter[0]) { return ERR_PTR(-EINVAL); } else { pr_debug("register: interpreter: {%s}\n", e->interpreter); }
/* Parse the 'flags' field. */ p = check_special_flags(p, e);
/* * A bpf handler decides the invocation flags per exec with * bpf_binprm_set_flags() rather than fixing them at registration, and * the interpreters it binds pre-open what 'F' would have, so a 'B' * entry carries no invocation flags. */ if (test_bit(MISC_FMT_BPF_BIT, &e->flags) && (e->flags & MISC_FMT_INVOCATION_FLAGS)) return ERR_PTR(-EINVAL);
/* * 'D' is a directive for this registration rather than a lasting * property, so consume it: the entry is created disabled and stays * out of the search list until '1' is written to its entry file. * Staying out is what leaves it open to being given interpreters; * the first enable publishes it, for good. */ if (e->flags & MISC_FMT_DISABLED) { e->flags &= ~MISC_FMT_DISABLED; clear_bit(MISC_FMT_ENABLED_BIT, &e->flags); }
/* Transparency preserves the whole argv, argv[0] included. */ if ((e->flags & MISC_FMT_TRANSPARENT) && (e->flags & MISC_FMT_PRESERVE_ARGV0)) return ERR_PTR(-EINVAL);
/* A native exec splices no argv, passes no execfd and needs no creds. */ if ((e->flags & MISC_FMT_LOADER) && (e->flags & (MISC_FMT_TRANSPARENT | MISC_FMT_PRESERVE_ARGV0 | MISC_FMT_CREDENTIALS | MISC_FMT_OPEN_BINARY))) return ERR_PTR(-EINVAL);
if (*p == '\n') p++; if (p != buf + count) return ERR_PTR(-EINVAL);
/* Non-F opens the interp at exec against the caller's cwd; require absolute. */ if ((e->flags & (MISC_FMT_LOADER | MISC_FMT_CREDENTIALS)) && !(e->flags & MISC_FMT_OPEN_FILE) && e->interpreter[0] != '/') return ERR_PTR(-EINVAL);
/* Born holding one reference; put_binfmt_handler() is the teardown. */ refcount_set(&e->users, 1); return no_free_ptr(e);}
/* Commands accepted by the /status and /<entry> files. */enum bm_command { BM_CMD_IGNORE, /* empty write */ BM_CMD_DISABLE, /* "0" */ BM_CMD_ENABLE, /* "1" */ BM_CMD_REMOVE, /* "-1" */};
/* Longest of the commands above, "-1\n". */#define MAX_COMMAND_LENGTH 3
/* * Parse what userspace wrote to /status or an entry file: '1' enables, * '0' disables and '-1' removes the entry or all entries. */static int parse_command(const char *s, size_t count){ if (count > MAX_COMMAND_LENGTH) return -EINVAL; if (!count) return BM_CMD_IGNORE; if (s[count - 1] == '\n') count--; if (count == 1 && s[0] == '0') return BM_CMD_DISABLE; if (count == 1 && s[0] == '1') return BM_CMD_ENABLE; if (count == 2 && s[0] == '-' && s[1] == '1') return BM_CMD_REMOVE; return -EINVAL;}
/* Copy in a command from a file that takes nothing else, and parse it. */static int read_command(const char __user *buffer, size_t count){ char s[MAX_COMMAND_LENGTH + 1];
if (count > sizeof(s) - 1) return -EINVAL; if (copy_from_user(s, buffer, count)) return -EFAULT; return parse_command(s, count);}
/* generic stuff */
/* The root directory's inode; its lock serializes configuring an instance. */static struct inode *bm_root_inode(struct super_block *sb){ return d_inode(sb->s_root);}
static void bm_seq_hex(struct seq_file *m, const u8 *data, int size){ for (int i = 0; i < size; i++) seq_printf(m, "%02x", data[i]);}
static int bm_entry_show(struct seq_file *m, void *unused){ struct binfmt_misc_entry *e = m->private;
if (test_bit(MISC_FMT_ENABLED_BIT, &e->flags)) seq_puts(m, "enabled\n"); else seq_puts(m, "disabled\n");
if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) { struct binfmt_misc_interp *interp;
seq_printf(m, "bpf %s\n", e->bpf_ops->name); /* * A staged entry's set can still grow, so every binding is * rcu-published. The open file pins the entry and with it * every node, so rcu is for the tearing, not the lifetime. */ rcu_read_lock(); list_for_each_entry_rcu(interp, &e->interps, list) seq_printf(m, "bpf-interpreter %s %s\n", interp->name, interp->path); rcu_read_unlock(); } else { seq_printf(m, "interpreter %s\n", e->interpreter); }
/* print the special flags */ seq_puts(m, "flags: "); for (int i = 0; i < ARRAY_SIZE(misc_flags); i++) if (e->flags & misc_flags[i].flag) seq_putc(m, misc_flags[i].c); seq_putc(m, '\n');
if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) { /* The program does the matching. */ } else if (!test_bit(MISC_FMT_MAGIC_BIT, &e->flags)) { seq_printf(m, "extension .%s\n", e->magic); } else { seq_printf(m, "offset %i\nmagic ", e->offset); bm_seq_hex(m, e->magic, e->size); if (e->mask) { seq_puts(m, "\nmask "); bm_seq_hex(m, e->mask, e->size); } seq_putc(m, '\n'); } return 0;}
static struct inode *bm_get_inode(struct super_block *sb, umode_t mode){ struct inode *inode = new_inode(sb);
if (inode) { inode->i_ino = get_next_ino(); inode->i_mode = mode; simple_inode_init_ts(inode); } return inode;}
/** * i_binfmt_misc - retrieve struct binfmt_misc from a binfmt_misc inode * @inode: inode of the relevant binfmt_misc instance * * This helper retrieves struct binfmt_misc from a binfmt_misc inode. This can * be done without any memory barriers because we are guaranteed that * user_ns->binfmt_misc is fully initialized. It was fully initialized when the * binfmt_misc mount was first created. * * Return: struct binfmt_misc of the relevant binfmt_misc instance */static struct binfmt_misc *i_binfmt_misc(struct inode *inode){ return inode->i_sb->s_user_ns->binfmt_misc;}
/** * bm_evict_inode - cleanup data associated with @inode * @inode: inode to which the data is attached * * Cleanup the binary type handler data associated with @inode if a binary type * entry is removed or the filesystem is unmounted and the super block is * shutdown. * * If the ->evict call was not caused by a super block shutdown but by * removing the entry via bm_{entry,status}_write() or unlink(2) the entry * will have already been removed from the list. We keep the hlist_unhashed() * check to make that explicit.*/static void bm_evict_inode(struct inode *inode){ struct binfmt_misc_entry *e = inode->i_private;
clear_inode(inode);
if (e) { struct binfmt_misc *misc;
misc = i_binfmt_misc(inode); spin_lock(&misc->entries_lock); if (!hlist_unhashed(&e->node)) hlist_del_init_rcu(&e->node); spin_unlock(&misc->entries_lock); put_binfmt_handler(e); }}
/** * unlink_binfmt_handler - unhash a binary type handler * @misc: handle to binfmt_misc instance * @e: binary type handler to unhash * * Adding and removing entries via bm_{entry,register,status}_write() and * unlink(2) happens under the exclusively held inode lock of the root * dentry keeping the list stable for writers. load_misc_binary() walks it * concurrently under SRCU. The entries_lock is only held around the actual * unlink to serialize against bm_evict_inode() which unlinks entries * during umount without holding the root inode lock. */static void unlink_binfmt_handler(struct binfmt_misc *misc, struct binfmt_misc_entry *e){ spin_lock(&misc->entries_lock); hlist_del_init_rcu(&e->node); spin_unlock(&misc->entries_lock);}
/** * remove_binfmt_handler - remove a binary type handler * @misc: handle to binfmt_misc instance * @e: binary type handler to remove * * Remove a binary type handler from the list of binary type handlers and * remove its associated dentry. */static void remove_binfmt_handler(struct binfmt_misc *misc, struct binfmt_misc_entry *e){ unlink_binfmt_handler(misc, e); locked_recursive_removal(e->dentry, NULL);}
/* Remove @e unless it was already removed. */static void bm_remove_entry(struct binfmt_misc_entry *e, struct super_block *sb){ struct inode *root = bm_root_inode(sb);
inode_lock_nested(root, I_MUTEX_PARENT); /* A staged entry is not hashed; the dentry says if it was removed. */ if (!d_unhashed(e->dentry)) remove_binfmt_handler(i_binfmt_misc(root), e); inode_unlock(root);}
/* Remove all entries of the binfmt_misc instance @misc belonging to @sb. */static void bm_remove_all_entries(struct binfmt_misc *misc, struct super_block *sb){ struct inode *root = bm_root_inode(sb); struct dentry *child = NULL;
inode_lock_nested(root, I_MUTEX_PARENT); /* * Walk the directory rather than the search list: a staged entry * is in the former but not yet in the latter. The control files * carry no entry and stay. */ while ((child = find_next_child(sb->s_root, child))) { struct binfmt_misc_entry *e = d_inode(child)->i_private;
if (e) remove_binfmt_handler(misc, e); } inode_unlock(root);}
/** * bm_unlink - remove a binary type handler via unlink(2) * @dir: inode of the root directory * @dentry: entry file to remove * * Removing the entry file removes its binary type handler, exactly like * writing -1 to it does. The status and register control files can't be * removed. The VFS calls this with the root inode lock held which * serializes against the write based add and remove paths. */static int bm_unlink(struct inode *dir, struct dentry *dentry){ struct binfmt_misc_entry *e = d_inode(dentry)->i_private;
if (!e) return -EPERM;
unlink_binfmt_handler(i_binfmt_misc(dir), e); return simple_unlink(dir, dentry);}
static const struct inode_operations bm_dir_inode_operations = { .lookup = simple_lookup, .unlink = bm_unlink,};
/* /<entry> */
static int bm_entry_open(struct inode *inode, struct file *file){ int ret;
ret = single_open(file, bm_entry_show, inode->i_private); if (ret) return ret;
/* seq_open() clears FMODE_PWRITE, bm_entry_write() takes any offset */ if (file->f_mode & FMODE_WRITE) file->f_mode |= FMODE_PWRITE; return 0;}
/* * Longest '+<name> <path>' a write can spell, and with it the longest * command an entry file takes: the two delimiters and a newline on top of * the two names. */#define MAX_BINDING_LENGTH (BINFMT_MISC_INTERP_NAME_MAX + PATH_MAX + 3)
/** * bm_entry_add_interp - bind another interpreter to a staged entry * @e: the entry * @file: the entry file being written to, for its credentials * @buf: the '+<name> <path>' command, parsed in place and owned by the caller * @count: its length * * A 'D' entry is registered outside the search list, which is what leaves * it open to being configured: it cannot be matched, so no exec can be * holding its interpreters and the set can still grow. Its first enable * publishes it and ends that. One interpreter per write, up to * BINFMT_MISC_INTERP_MAX of them, none of which has to fit in a register * string. * * Return: @count on success, a negative errno on failure */static ssize_t bm_entry_add_interp(struct binfmt_misc_entry *e, struct file *file, char *buf, size_t count){ struct file *f __free(close_interp_file) = NULL; struct inode *root = bm_root_inode(file_inode(file)->i_sb); size_t nlen, plen; char *name, *path; int retval;
/* Settled before the open: type is fixed, publication is permanent. */ if (!test_bit(MISC_FMT_BPF_BIT, &e->flags)) return -EINVAL; if (!hlist_unhashed_lockless(&e->node)) return -EBUSY;
/* '+<name> <path>': the path is everything past the first space. */ name = buf + 1; path = strchr(name, ' '); if (!path) return -EINVAL; *path++ = '\0';
plen = strlen(path); /* The command has to end at the write, like a register string. */ if (path + plen != buf + count) return -EINVAL; if (plen && path[plen - 1] == '\n') path[--plen] = '\0'; /* Resolved now, so a relative path would name the writer's cwd. */ if (path[0] != '/') return -EINVAL;
nlen = path - name - 1; if (!nlen || nlen > BINFMT_MISC_INTERP_NAME_MAX) return -EINVAL; /* The name prints between delimiters, so keep it a printable word. */ for (const char *p = name; *p; p++) if (!isascii(*p) || !isgraph(*p)) return -EINVAL;
/* Opened before the lock: resolving it may walk this very filesystem. */ f = open_interp_file(file->f_cred, path); if (IS_ERR(f)) return PTR_ERR(f);
inode_lock(root); if (d_unhashed(e->dentry)) retval = -ENOENT; /* removed while we were opening it */ else if (!hlist_unhashed(&e->node)) retval = -EBUSY; /* published while we were opening it */ else retval = entry_attach_interpreter(e, name, path, f); inode_unlock(root); if (retval) return retval;
/* The file is owned by the entry now. */ retain_and_null_ptr(f); return count;}
static ssize_t bm_entry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos){ struct inode *inode = file_inode(file); struct binfmt_misc_entry *e = inode->i_private; char *buf __free(kfree) = NULL; int res;
/* A binding is the longest command this file takes. */ if (count > MAX_BINDING_LENGTH) return -E2BIG;
buf = memdup_user_nul(buffer, count); if (IS_ERR(buf)) return PTR_ERR(buf);
/* '+<name> <path>' binds an interpreter, everything else toggles. */ if (buf[0] == '+') return bm_entry_add_interp(e, file, buf, count);
res = parse_command(buf, count);
switch (res) { case BM_CMD_DISABLE: clear_bit(MISC_FMT_ENABLED_BIT, &e->flags); break; case BM_CMD_ENABLE: { struct inode *root = bm_root_inode(inode->i_sb);
/* * The first enable publishes a 'D' entry into the search * list, whole. The lock keeps that ordered against a second * enable, against removal - a removed entry has nothing left * to publish - and against binding: what can be matched can * no longer be configured. */ inode_lock(root); set_bit(MISC_FMT_ENABLED_BIT, &e->flags); if (hlist_unhashed(&e->node) && !d_unhashed(e->dentry)) { struct binfmt_misc *misc = i_binfmt_misc(inode);
spin_lock(&misc->entries_lock); hlist_add_head_rcu(&e->node, &misc->entries); spin_unlock(&misc->entries_lock); } inode_unlock(root); break; } case BM_CMD_REMOVE: bm_remove_entry(e, inode->i_sb); break; default: return res; }
return count;}
static const struct file_operations bm_entry_operations = { .open = bm_entry_open, .read = seq_read, .write = bm_entry_write, .llseek = seq_lseek, .release = single_release,};
/* /register */
/* add to filesystem */static int add_entry(struct binfmt_misc_entry *e, struct super_block *sb){ struct dentry *dentry = simple_start_creating(sb->s_root, e->name); struct inode *inode; struct binfmt_misc *misc;
if (IS_ERR(dentry)) return PTR_ERR(dentry);
inode = bm_get_inode(sb, S_IFREG | 0644); if (unlikely(!inode)) { simple_done_creating(dentry); return -ENOMEM; }
e->dentry = dentry; inode->i_private = e; inode->i_fop = &bm_entry_operations;
d_make_persistent(dentry, inode); /* A 'D' entry stays out of the search list until its first enable. */ if (test_bit(MISC_FMT_ENABLED_BIT, &e->flags)) { misc = i_binfmt_misc(inode); spin_lock(&misc->entries_lock); hlist_add_head_rcu(&e->node, &misc->entries); spin_unlock(&misc->entries_lock); } simple_done_creating(dentry); return 0;}
static ssize_t bm_register_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos){ struct binfmt_misc_entry *e __free(put_binfmt_handler) = NULL; struct super_block *sb = file_inode(file)->i_sb; int err;
e = create_entry(buffer, count); if (IS_ERR(e)) return PTR_ERR(e);
if (test_bit(MISC_FMT_BPF_BIT, &e->flags)) { e->bpf_ops = binfmt_misc_get_ops(sb->s_user_ns, e->bpf_ops_name); if (!e->bpf_ops) { pr_notice("register: no bpf handler named %s\n", e->bpf_ops_name); return -ENOENT; } }
if (e->flags & MISC_FMT_OPEN_FILE) { struct file *f = open_interp_file(file->f_cred, e->interpreter);
if (IS_ERR(f)) return PTR_ERR(f); err = entry_attach_interpreter(e, "", e->interpreter, f); if (err) { close_interp_file(f); return err; } }
err = add_entry(e, sb); if (err) return err;
/* The entry is owned by its inode now. */ retain_and_null_ptr(e); return count;}
static const struct file_operations bm_register_operations = { .write = bm_register_write, .llseek = noop_llseek,};
/* /status */
static ssize_tbm_status_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){ struct binfmt_misc *misc; const char *s;
misc = i_binfmt_misc(file_inode(file)); s = READ_ONCE(misc->enabled) ? "enabled\n" : "disabled\n"; return simple_read_from_buffer(buf, nbytes, ppos, s, strlen(s));}
static ssize_t bm_status_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos){ struct binfmt_misc *misc; int res = read_command(buffer, count);
misc = i_binfmt_misc(file_inode(file)); switch (res) { case BM_CMD_DISABLE: WRITE_ONCE(misc->enabled, false); break; case BM_CMD_ENABLE: WRITE_ONCE(misc->enabled, true); break; case BM_CMD_REMOVE: bm_remove_all_entries(misc, file_inode(file)->i_sb); break; default: return res; }
return count;}
static const struct file_operations bm_status_operations = { .read = bm_status_read, .write = bm_status_write, .llseek = default_llseek,};
/* Superblock handling */
static const struct super_operations bm_super_ops = { .statfs = simple_statfs, .evict_inode = bm_evict_inode,};
static int bm_fill_super(struct super_block *sb, struct fs_context *fc){ int err; struct user_namespace *user_ns = sb->s_user_ns; struct binfmt_misc *misc; static const struct tree_descr bm_files[] = { [2] = {"status", &bm_status_operations, S_IWUSR|S_IRUGO}, [3] = {"register", &bm_register_operations, S_IWUSR}, /* last one */ {""} };
/* The fscontext fd may have been passed to another user namespace. */ if (user_ns != current_user_ns()) return -EINVAL;
/* Never exec off this instance and never let anything stack on it. */ sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
/* * Lazily allocate a new binfmt_misc instance for this namespace, i.e. * do it here during the first mount of binfmt_misc. We don't need to * waste memory for every user namespace allocation. It's likely much * more common to not mount a separate binfmt_misc instance than it is * to mount one. * * While multiple superblocks can exist they are keyed by userns in * s_fs_info for binfmt_misc. Hence, the vfs guarantees that * bm_fill_super() is called exactly once whenever a binfmt_misc * superblock for a userns is created. This in turn lets us conclude * that when a binfmt_misc superblock is created for the first time for * a userns there's no one racing us. Therefore we don't need any * barriers when we dereference binfmt_misc. */ misc = user_ns->binfmt_misc; if (!misc) { /* * If it turns out that most user namespaces actually want to * register their own binary type handler and therefore all * create their own separate binfmt_misc mounts we should * consider turning this into a kmem cache. */ misc = kzalloc_obj(struct binfmt_misc); if (!misc) return -ENOMEM;
INIT_HLIST_HEAD(&misc->entries); spin_lock_init(&misc->entries_lock);
/* Pairs with smp_load_acquire() in current_binfmt_misc(). */ smp_store_release(&user_ns->binfmt_misc, misc); }
/* * When the binfmt_misc superblock for this userns is shutdown * ->enabled might have been set to false and we don't reinitialize * ->enabled again during shutdown as someone might already be mounting * binfmt_misc again. It also would be pointless since by then we know * that the binary type list for this binfmt_misc mount is empty making * load_misc_binary() return -ENOEXEC independent of whether ->enabled * is true. Instead, if someone mounts binfmt_misc for the first time or * again we simply reset ->enabled to true. */ WRITE_ONCE(misc->enabled, true);
err = simple_fill_super(sb, BINFMTFS_MAGIC, bm_files); if (err) return err;
sb->s_op = &bm_super_ops; d_inode(sb->s_root)->i_op = &bm_dir_inode_operations; return 0;}
static void bm_free(struct fs_context *fc){ if (fc->s_fs_info) put_user_ns(fc->s_fs_info);}
static int bm_get_tree(struct fs_context *fc){ return get_tree_keyed(fc, bm_fill_super, get_user_ns(fc->user_ns));}
static const struct fs_context_operations bm_context_ops = { .free = bm_free, .get_tree = bm_get_tree,};
static void bm_kill_sb(struct super_block *sb){ struct user_namespace *user_ns = sb->s_fs_info;
kill_anon_super(sb); put_user_ns(user_ns);}
static int bm_init_fs_context(struct fs_context *fc){ fc->ops = &bm_context_ops; return 0;}
static struct linux_binfmt misc_format = { .module = THIS_MODULE, .load_binary = load_misc_binary,};
static struct file_system_type bm_fs_type = { .owner = THIS_MODULE, .name = "binfmt_misc", .init_fs_context = bm_init_fs_context, .fs_flags = FS_USERNS_MOUNT, .kill_sb = bm_kill_sb,};MODULE_ALIAS_FS("binfmt_misc");
static int __init init_misc_binfmt(void){ int err = register_filesystem(&bm_fs_type); if (!err) insert_binfmt(&misc_format); return err;}
static void __exit exit_misc_binfmt(void){ unregister_binfmt(&misc_format); unregister_filesystem(&bm_fs_type); /* Flush pending bm_entry_free_rcu() callbacks before the text goes. */ srcu_barrier(&bm_entries_srcu);}
core_initcall(init_misc_binfmt);module_exit(exit_misc_binfmt);MODULE_DESCRIPTION("Kernel support for miscellaneous binaries");MODULE_LICENSE("GPL");