@@ -16,6 +16,7 @@ struct timer_list {
*/
struct hlist_node entry;
unsigned long expires;
+ unsigned long canary;
void (*function)(unsigned long);
unsigned long data;
u32 flags;
@@ -91,6 +92,7 @@ struct timer_list {
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key);
+void init_timer_func(struct timer_list *timer, void (*func)(unsigned long));
#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
extern void init_timer_on_stack_key(struct timer_list *timer,
@@ -140,14 +142,14 @@ static inline void init_timer_on_stack_key(struct timer_list *timer,
#define __setup_timer(_timer, _fn, _data, _flags) \
do { \
__init_timer((_timer), (_flags)); \
- (_timer)->function = (_fn); \
+ init_timer_func((_timer), (_fn)); \
(_timer)->data = (_data); \
} while (0)
#define __setup_timer_on_stack(_timer, _fn, _data, _flags) \
do { \
__init_timer_on_stack((_timer), (_flags)); \
- (_timer)->function = (_fn); \
+ init_timer_func((_timer), (_fn)); \
(_timer)->data = (_data); \
} while (0)
@@ -44,6 +44,7 @@
#include <linux/sched/debug.h>
#include <linux/slab.h>
#include <linux/compat.h>
+#include <linux/random.h>
#include <linux/uaccess.h>
#include <asm/unistd.h>
@@ -1060,6 +1061,34 @@ int mod_timer(struct timer_list *timer, unsigned long expires)
}
EXPORT_SYMBOL(mod_timer);
+static DEFINE_MUTEX(timer_canary_mutex);
+static unsigned long timer_canary __ro_after_init;
+
+/**
+ * init_timer_func - set the function used for the timer
+ * @timer: the timer to be updated
+ * @func: the function to be called by the timer
+ *
+ * This should only be called once per timer creation to set the function.
+ * Normally used via the setup_timer_*() macros or add_timer().
+ */
+void init_timer_func(struct timer_list *timer, void (*func)(unsigned long))
+{
+ /* Initialize the global timer canary on first use. */
+ if (!timer_canary) {
+ mutex_lock(&timer_canary_mutex);
+ if (!timer_canary)
+ timer_canary = get_random_long();
+ mutex_unlock(&timer_canary_mutex);
+ }
+
+ /* Record timer canary for this timer function. */
+ timer->function = func;
+ timer->canary = (unsigned long)timer->function ^
+ (unsigned long)timer ^ timer_canary;
+}
+EXPORT_SYMBOL(init_timer_func);
+
/**
* add_timer - start a timer
* @timer: the timer to be added
@@ -1077,6 +1106,7 @@ EXPORT_SYMBOL(mod_timer);
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
+ init_timer_func(timer, timer->function);
mod_timer(timer, timer->expires);
}
EXPORT_SYMBOL(add_timer);
@@ -1095,6 +1125,7 @@ void add_timer_on(struct timer_list *timer, int cpu)
BUG_ON(timer_pending(timer) || !timer->function);
+ init_timer_func(timer, timer->function);
new_base = get_timer_cpu_base(timer->flags, cpu);
/*
@@ -1244,6 +1275,7 @@ static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
unsigned long data)
{
int count = preempt_count();
+ void (*badfn)(unsigned long) = NULL;
#ifdef CONFIG_LOCKDEP
/*
@@ -1265,7 +1297,14 @@ static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
lock_map_acquire(&lockdep_map);
trace_timer_expire_entry(timer);
- fn(data);
+
+ /* Make sure the timer function hasn't changed since canary set. */
+ if (((unsigned long)fn ^ (unsigned long)timer ^ timer->canary) !=
+ timer_canary) {
+ badfn = fn;
+ } else
+ fn(data);
+
trace_timer_expire_exit(timer);
lock_map_release(&lockdep_map);
@@ -1281,6 +1320,10 @@ static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
*/
preempt_count_set(count);
}
+
+ WARN_RATELIMIT(badfn,
+ "timer: corrupt timer function (was %pF)\n",
+ (void *)((unsigned long)timer ^ timer->canary ^ timer_canary));
}
static void expire_timers(struct timer_base *base, struct hlist_head *head)
This introduces canaries to struct timer_list in an effort to protect the function callback pointer from getting rewritten during stack or heap overflow attacks. The struct timer_list has become a recent target for security flaw exploitation because it includes the "data" argument in the structure, along with the function callback. This provides attackers with a ROP-like primitive for performing limited kernel function calls without needing all the prerequisites to stage a ROP attack. Recent examples of exploits using struct timer_list attacks: http://www.openwall.com/lists/oss-security/2016/12/06/1 (https://www.exploit-db.com/exploits/40871/) https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html (https://www.exploit-db.com/exploits/41458/) Timers normally have their callback functions initialized either via the setup_timer_*() macros or manually before calls to add_timer(). The per-timer canary gets set in either case, and then checked at timer expiration time before calling the function. Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/timer.h | 6 ++++-- kernel/time/timer.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-)