ANDROID: cfi: ensure RCU is watching in __cfi_slowpath
Indirect calls can happen when RCU is not watching, so we need to wake it up again for the CFI shadow and __module_address. As these calls can happen anywhere, use rcu_nmi_enter() similarly to kernel_text_address(), and switch to rcu_read_lock_sched() for shadow access. Bug: 169017431 Change-Id: Iebb857df898e644b4952a62d86fa5ff9852b5711 Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
This commit is contained in:
parent
77bc008d89
commit
d0dc5d8586
34
kernel/cfi.c
34
kernel/cfi.c
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/hardirq.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
@ -246,33 +247,36 @@ static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s,
|
|||||||
|
|
||||||
static inline cfi_check_fn find_module_cfi_check(void *ptr)
|
static inline cfi_check_fn find_module_cfi_check(void *ptr)
|
||||||
{
|
{
|
||||||
|
cfi_check_fn f = CFI_CHECK_FN;
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
|
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
mod = __module_address((unsigned long)ptr);
|
mod = __module_address((unsigned long)ptr);
|
||||||
|
if (mod)
|
||||||
|
f = mod->cfi_check;
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
|
|
||||||
if (mod)
|
return f;
|
||||||
return mod->cfi_check;
|
|
||||||
|
|
||||||
return CFI_CHECK_FN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline cfi_check_fn find_cfi_check(void *ptr)
|
static inline cfi_check_fn find_cfi_check(void *ptr)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_CFI_CLANG_SHADOW
|
bool rcu;
|
||||||
cfi_check_fn f;
|
cfi_check_fn f;
|
||||||
|
|
||||||
if (!rcu_access_pointer(cfi_shadow))
|
rcu = rcu_is_watching();
|
||||||
return CFI_CHECK_FN; /* No loaded modules */
|
if (!rcu)
|
||||||
|
rcu_nmi_enter();
|
||||||
|
|
||||||
|
#ifdef CONFIG_CFI_CLANG_SHADOW
|
||||||
/* Look up the __cfi_check function to use */
|
/* Look up the __cfi_check function to use */
|
||||||
rcu_read_lock();
|
rcu_read_lock_sched();
|
||||||
f = ptr_to_check_fn(rcu_dereference(cfi_shadow), (unsigned long)ptr);
|
f = ptr_to_check_fn(rcu_dereference_sched(cfi_shadow),
|
||||||
rcu_read_unlock();
|
(unsigned long)ptr);
|
||||||
|
rcu_read_unlock_sched();
|
||||||
|
|
||||||
if (f)
|
if (f)
|
||||||
return f;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fall back to find_module_cfi_check, which works also for a larger
|
* Fall back to find_module_cfi_check, which works also for a larger
|
||||||
@ -280,7 +284,13 @@ static inline cfi_check_fn find_cfi_check(void *ptr)
|
|||||||
*/
|
*/
|
||||||
#endif /* CONFIG_CFI_CLANG_SHADOW */
|
#endif /* CONFIG_CFI_CLANG_SHADOW */
|
||||||
|
|
||||||
return find_module_cfi_check(ptr);
|
f = find_module_cfi_check(ptr);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (!rcu)
|
||||||
|
rcu_nmi_exit();
|
||||||
|
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag)
|
void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag)
|
||||||
|
Loading…
Reference in New Issue
Block a user