From 3045070cf5b808b02ef18cf8bbc3481b7879bfca Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 18 Apr 2019 12:44:31 -0700 Subject: [PATCH] ANDROID: timer: fix timer_setup with CFI timer_setup casts the callback function to an incompatible type, which trips CFI for all users of the API: CFI failure (target: [] nf_ct_frag6_expire+0x0/0x4): ... Call trace: __cfi_check_fail+0x1c/0x24 ... call_timer_fn+0x304/0x308 Note that this only affects 4.14, the timer code was refactored in the later upstream releases. Bug: 130800382 Bug: 156058713 Change-Id: If7d706dcbe08ca0001d025606e5d3f3ca1559a7b Signed-off-by: Sami Tolvanen --- include/linux/timer.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) 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)