diff --git a/include/linux/timer.h b/include/linux/timer.h index e0ea1fe87572..9bb6c6f60c1c 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -21,6 +21,9 @@ struct timer_list { unsigned long data; u32 flags; +#ifdef CONFIG_CFI_CLANG + void (*__function)(struct timer_list *); +#endif #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif @@ -172,6 +175,30 @@ static inline void init_timer_on_stack_key(struct timer_list *timer, #define TIMER_DATA_TYPE unsigned long #define TIMER_FUNC_TYPE void (*)(TIMER_DATA_TYPE) +#ifdef CONFIG_CFI_CLANG +/* + * With CFI_CLANG, we cannot cast the callback function to TIMER_FUNC_TYPE + * without tripping an indirect call check in call_timer_fn. Therefore, we + * add a new field to struct timer_list and use __timer_callback to perform + * the indirect call using the correct function pointer. + */ +static inline void __timer_callback(unsigned long data) +{ + struct timer_list *timer = (struct timer_list *)data; + + timer->__function(timer); +} + +static inline void timer_setup(struct timer_list *timer, + void (*callback)(struct timer_list *), + unsigned int flags) +{ + timer->__function = callback; + + __setup_timer(timer, __timer_callback, + (TIMER_DATA_TYPE)timer, flags); +} +#else static inline void timer_setup(struct timer_list *timer, void (*callback)(struct timer_list *), unsigned int flags) @@ -179,6 +206,7 @@ static inline void timer_setup(struct timer_list *timer, __setup_timer(timer, (TIMER_FUNC_TYPE)callback, (TIMER_DATA_TYPE)timer, flags); } +#endif #define from_timer(var, callback_timer, timer_fieldname) \ container_of(callback_timer, typeof(*var), timer_fieldname)