extern int core_kernel_text(unsigned long addr);
extern int __kernel_text_address(unsigned long addr);
extern int kernel_text_address(unsigned long addr);
+extern int func_ptr_is_kernel_text(void *ptr);
+extern void *dereference_function_descriptor(void *ptr);
+
struct pid;
extern struct pid *session_of_pgrp(struct pid *pgrp);
return 1;
return module_text_address(addr) != NULL;
}
+
+/*
+ * On some architectures (PPC64, IA64) function pointers
+ * are actually only tokens to some data that then holds the
+ * real function address. As a result, to find if a function
+ * pointer is part of the kernel text, we need to do some
+ * special dereferencing first.
+ */
+int func_ptr_is_kernel_text(void *ptr)
+{
+ unsigned long addr;
+ addr = (unsigned long) dereference_function_descriptor(ptr);
+ if (core_kernel_text(addr))
+ return 1;
+ return module_text_address(addr) != NULL;
+}
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n)
{
- if (!kernel_text_address((unsigned long)n->notifier_call)) {
- WARN(1, "Invalid notifier registered!");
- return 0;
- }
while ((*nl) != NULL) {
if (n->priority > (*nl)->priority)
break;
static int notifier_chain_cond_register(struct notifier_block **nl,
struct notifier_block *n)
{
- if (!kernel_text_address((unsigned long)n->notifier_call)) {
- WARN(1, "Invalid notifier registered!");
- return 0;
- }
while ((*nl) != NULL) {
if ((*nl) == n)
return 0;
next_nb = rcu_dereference(nb->next);
#ifdef CONFIG_DEBUG_NOTIFIERS
- if (!kernel_text_address((unsigned long)nb->notifier_call)) {
+ if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
WARN(1, "Invalid notifier called!");
nb = next_nb;
continue;
return buf;
}
-static inline void *dereference_function_descriptor(void *ptr)
+void *dereference_function_descriptor(void *ptr)
{
#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
void *p;