Commit 73f44fe1 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Ingo Molnar
Browse files

static_call: Allow module use without exposing static_call_key



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: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Link: https://lkml.kernel.org/r/20210127231837.ifddpn7rhwdaepiu@treble
parent e59e10f8
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -37,4 +37,11 @@
#define ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name)			\
#define ARCH_DEFINE_STATIC_CALL_NULL_TRAMP(name)			\
	__ARCH_DEFINE_STATIC_CALL_TRAMP(name, "ret; nop; nop; nop; nop")
	__ARCH_DEFINE_STATIC_CALL_TRAMP(name, "ret; nop; nop; nop; nop")



#define ARCH_ADD_TRAMP_KEY(name)					\
	asm(".pushsection .static_call_tramp_key, \"a\"		\n"	\
	    ".long " STATIC_CALL_TRAMP_STR(name) " - .		\n"	\
	    ".long " STATIC_CALL_KEY_STR(name) " - .		\n"	\
	    ".popsection					\n")

#endif /* _ASM_STATIC_CALL_H */
#endif /* _ASM_STATIC_CALL_H */
+4 −1
Original line number Original line Diff line number Diff line
@@ -393,7 +393,10 @@
	. = ALIGN(8);							\
	. = ALIGN(8);							\
	__start_static_call_sites = .;					\
	__start_static_call_sites = .;					\
	KEEP(*(.static_call_sites))					\
	KEEP(*(.static_call_sites))					\
	__stop_static_call_sites = .;
	__stop_static_call_sites = .;					\
	__start_static_call_tramp_key = .;				\
	KEEP(*(.static_call_tramp_key))					\
	__stop_static_call_tramp_key = .;


/*
/*
 * Allow architectures to handle ro_after_init data on their
 * Allow architectures to handle ro_after_init data on their
+20 −2
Original line number Original line Diff line number Diff line
@@ -138,6 +138,12 @@ struct static_call_key {
	};
	};
};
};


/* For finding the key associated with a trampoline */
struct static_call_tramp_key {
	s32 tramp;
	s32 key;
};

extern void __static_call_update(struct static_call_key *key, void *tramp, void *func);
extern void __static_call_update(struct static_call_key *key, void *tramp, void *func);
extern int static_call_mod_init(struct module *mod);
extern int static_call_mod_init(struct module *mod);
extern int static_call_text_reserved(void *start, void *end);
extern int static_call_text_reserved(void *start, void *end);
@@ -165,11 +171,18 @@ extern long __static_call_return0(void);
#define EXPORT_STATIC_CALL(name)					\
#define EXPORT_STATIC_CALL(name)					\
	EXPORT_SYMBOL(STATIC_CALL_KEY(name));				\
	EXPORT_SYMBOL(STATIC_CALL_KEY(name));				\
	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))

#define EXPORT_STATIC_CALL_GPL(name)					\
#define EXPORT_STATIC_CALL_GPL(name)					\
	EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name));			\
	EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name));			\
	EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))
	EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))


/* Leave the key unexported, so modules can't change static call targets: */
#define EXPORT_STATIC_CALL_TRAMP(name)					\
	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name));				\
	ARCH_ADD_TRAMP_KEY(name)
#define EXPORT_STATIC_CALL_TRAMP_GPL(name)				\
	EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name));			\
	ARCH_ADD_TRAMP_KEY(name)

#elif defined(CONFIG_HAVE_STATIC_CALL)
#elif defined(CONFIG_HAVE_STATIC_CALL)


static inline int static_call_init(void) { return 0; }
static inline int static_call_init(void) { return 0; }
@@ -216,11 +229,16 @@ static inline long __static_call_return0(void)
#define EXPORT_STATIC_CALL(name)					\
#define EXPORT_STATIC_CALL(name)					\
	EXPORT_SYMBOL(STATIC_CALL_KEY(name));				\
	EXPORT_SYMBOL(STATIC_CALL_KEY(name));				\
	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))

#define EXPORT_STATIC_CALL_GPL(name)					\
#define EXPORT_STATIC_CALL_GPL(name)					\
	EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name));			\
	EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name));			\
	EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))
	EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))


/* Leave the key unexported, so modules can't change static call targets: */
#define EXPORT_STATIC_CALL_TRAMP(name)					\
	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
#define EXPORT_STATIC_CALL_TRAMP_GPL(name)				\
	EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))

#else /* Generic implementation */
#else /* Generic implementation */


static inline int static_call_init(void) { return 0; }
static inline int static_call_init(void) { return 0; }
+25 −2
Original line number Original line Diff line number Diff line
@@ -10,6 +10,7 @@
#define STATIC_CALL_KEY_PREFIX_STR	__stringify(STATIC_CALL_KEY_PREFIX)
#define STATIC_CALL_KEY_PREFIX_STR	__stringify(STATIC_CALL_KEY_PREFIX)
#define STATIC_CALL_KEY_PREFIX_LEN	(sizeof(STATIC_CALL_KEY_PREFIX_STR) - 1)
#define STATIC_CALL_KEY_PREFIX_LEN	(sizeof(STATIC_CALL_KEY_PREFIX_STR) - 1)
#define STATIC_CALL_KEY(name)		__PASTE(STATIC_CALL_KEY_PREFIX, name)
#define STATIC_CALL_KEY(name)		__PASTE(STATIC_CALL_KEY_PREFIX, name)
#define STATIC_CALL_KEY_STR(name)	__stringify(STATIC_CALL_KEY(name))


#define STATIC_CALL_TRAMP_PREFIX	__SCT__
#define STATIC_CALL_TRAMP_PREFIX	__SCT__
#define STATIC_CALL_TRAMP_PREFIX_STR	__stringify(STATIC_CALL_TRAMP_PREFIX)
#define STATIC_CALL_TRAMP_PREFIX_STR	__stringify(STATIC_CALL_TRAMP_PREFIX)
@@ -39,17 +40,39 @@ struct static_call_site {


#ifdef CONFIG_HAVE_STATIC_CALL
#ifdef CONFIG_HAVE_STATIC_CALL


#define __raw_static_call(name)	(&STATIC_CALL_TRAMP(name))

#ifdef CONFIG_HAVE_STATIC_CALL_INLINE

/*
/*
 * __ADDRESSABLE() is used to ensure the key symbol doesn't get stripped from
 * __ADDRESSABLE() is used to ensure the key symbol doesn't get stripped from
 * the symbol table so that objtool can reference it when it generates the
 * the symbol table so that objtool can reference it when it generates the
 * .static_call_sites section.
 * .static_call_sites section.
 */
 */
#define __STATIC_CALL_ADDRESSABLE(name) \
	__ADDRESSABLE(STATIC_CALL_KEY(name))

#define __static_call(name)						\
#define __static_call(name)						\
({									\
({									\
	__ADDRESSABLE(STATIC_CALL_KEY(name));				\
	__STATIC_CALL_ADDRESSABLE(name);				\
	&STATIC_CALL_TRAMP(name);					\
	__raw_static_call(name);					\
})
})


#else /* !CONFIG_HAVE_STATIC_CALL_INLINE */

#define __STATIC_CALL_ADDRESSABLE(name)
#define __static_call(name)	__raw_static_call(name)

#endif /* CONFIG_HAVE_STATIC_CALL_INLINE */

#ifdef MODULE
#define __STATIC_CALL_MOD_ADDRESSABLE(name)
#define static_call_mod(name)	__raw_static_call(name)
#else
#define __STATIC_CALL_MOD_ADDRESSABLE(name) __STATIC_CALL_ADDRESSABLE(name)
#define static_call_mod(name)	__static_call(name)
#endif

#define static_call(name)	__static_call(name)
#define static_call(name)	__static_call(name)


#else
#else
+53 −2
Original line number Original line Diff line number Diff line
@@ -12,6 +12,8 @@


extern struct static_call_site __start_static_call_sites[],
extern struct static_call_site __start_static_call_sites[],
			       __stop_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;
static bool static_call_initialized;


@@ -323,10 +325,59 @@ static int __static_call_mod_text_reserved(void *start, void *end)
	return ret;
	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)
static int static_call_add_module(struct module *mod)
{
{
	return __static_call_init(mod, mod->static_call_sites,
	struct static_call_site *start = mod->static_call_sites;
				  mod->static_call_sites + mod->num_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)
static void static_call_del_module(struct module *mod)
Loading