static_call: Allow module use without exposing static_call_key

[ Upstream commit 73f44fe19d359635a607e8e8daa0da4001c1cfc2 ]

When exporting static_call_key; with EXPORT_STATIC_CALL*(), the module
can use static_call_update() to change the function called.  This is
not desirable in general.

Not exporting static_call_key however also disallows usage of
static_call(), since objtool needs the key to construct the
static_call_site.

Solve this by allowing objtool to create the static_call_site using
the trampoline address when it builds a module and cannot find the
static_call_key symbol. The module loader will then try and map the
trampole back to a key before it constructs the normal sites list.

Doing this requires a trampoline -> key associsation, so add another
magic section that keeps those.

Originally-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lkml.kernel.org/r/20210127231837.ifddpn7rhwdaepiu@treble
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Josh Poimboeuf
2021-01-27 17:18:37 -06:00
committed by Greg Kroah-Hartman
parent 433cd7ca38
commit a63068e939
7 changed files with 149 additions and 11 deletions

View File

@@ -12,6 +12,8 @@
extern struct static_call_site __start_static_call_sites[],
__stop_static_call_sites[];
extern struct static_call_tramp_key __start_static_call_tramp_key[],
__stop_static_call_tramp_key[];
static bool static_call_initialized;
@@ -332,10 +334,59 @@ static int __static_call_mod_text_reserved(void *start, void *end)
return ret;
}
static unsigned long tramp_key_lookup(unsigned long addr)
{
struct static_call_tramp_key *start = __start_static_call_tramp_key;
struct static_call_tramp_key *stop = __stop_static_call_tramp_key;
struct static_call_tramp_key *tramp_key;
for (tramp_key = start; tramp_key != stop; tramp_key++) {
unsigned long tramp;
tramp = (long)tramp_key->tramp + (long)&tramp_key->tramp;
if (tramp == addr)
return (long)tramp_key->key + (long)&tramp_key->key;
}
return 0;
}
static int static_call_add_module(struct module *mod)
{
return __static_call_init(mod, mod->static_call_sites,
mod->static_call_sites + mod->num_static_call_sites);
struct static_call_site *start = mod->static_call_sites;
struct static_call_site *stop = start + mod->num_static_call_sites;
struct static_call_site *site;
for (site = start; site != stop; site++) {
unsigned long addr = (unsigned long)static_call_key(site);
unsigned long key;
/*
* Is the key is exported, 'addr' points to the key, which
* means modules are allowed to call static_call_update() on
* it.
*
* Otherwise, the key isn't exported, and 'addr' points to the
* trampoline so we need to lookup the key.
*
* We go through this dance to prevent crazy modules from
* abusing sensitive static calls.
*/
if (!kernel_text_address(addr))
continue;
key = tramp_key_lookup(addr);
if (!key) {
pr_warn("Failed to fixup __raw_static_call() usage at: %ps\n",
static_call_addr(site));
return -EINVAL;
}
site->key = (key - (long)&site->key) |
(site->key & STATIC_CALL_SITE_FLAGS);
}
return __static_call_init(mod, start, stop);
}
static void static_call_del_module(struct module *mod)