mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-18 09:36:53 +07:00
686fef928b
Modern kernel callback systems pass the structure associated with a given callback to the callback function. The timer callback remains one of the legacy cases where an arbitrary unsigned long argument continues to be passed as the callback argument. This has several problems: - This bloats the timer_list structure with a normally redundant .data field. - No type checking is being performed, forcing callbacks to do explicit type casts of the unsigned long argument into the object that was passed, rather than using container_of(), as done in most of the other callback infrastructure. - Neighboring buffer overflows can overwrite both the .function and the .data field, providing attackers with a way to elevate from a buffer overflow into a simplistic ROP-like mechanism that allows calling arbitrary functions with a controlled first argument. - For future Control Flow Integrity work, this creates a unique function prototype for timer callbacks, instead of allowing them to continue to be clustered with other void functions that take a single unsigned long argument. This adds a new timer initialization API, which will ultimately replace the existing setup_timer(), setup_{deferrable,pinned,etc}_timer() family, named timer_setup() (to mirror hrtimer_setup(), making instances of its use much easier to grep for). In order to support the migration of existing timers into the new callback arguments, timer_setup() casts its arguments to the existing legacy types, and explicitly passes the timer pointer as the legacy data argument. Once all setup_*timer() callers have been replaced with timer_setup(), the casts can be removed, and the data argument can be dropped with the timer expiration code changed to just pass the timer to the callback directly. Since the regular pattern of using container_of() during local variable declaration repeats the need for the variable type declaration to be included, this adds a helper modeled after other from_*() helpers that wrap container_of(), named from_timer(). This helper uses typeof(*variable), removing the type redundancy and minimizing the need for line wraps in forthcoming conversions from "unsigned data long" to "struct timer_list *" in the timer callbacks: -void callback(unsigned long data) +void callback(struct timer_list *t) { - struct some_data_structure *local = (struct some_data_structure *)data; + struct some_data_structure *local = from_timer(local, t, timer); Finally, in order to support the handful of timer users that perform open-coded assignments of the .function (and .data) fields, provide cast macros (TIMER_FUNC_TYPE and TIMER_DATA_TYPE) that can be used temporarily. Once conversion has been completed, these can be globally trivially removed. Signed-off-by: Kees Cook <keescook@chromium.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lkml.kernel.org/r/20170928133817.GA113410@beast
254 lines
8.3 KiB
C
254 lines
8.3 KiB
C
#ifndef _LINUX_TIMER_H
|
|
#define _LINUX_TIMER_H
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/ktime.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/debugobjects.h>
|
|
#include <linux/stringify.h>
|
|
|
|
struct tvec_base;
|
|
|
|
struct timer_list {
|
|
/*
|
|
* All fields that change during normal runtime grouped to the
|
|
* same cacheline
|
|
*/
|
|
struct hlist_node entry;
|
|
unsigned long expires;
|
|
void (*function)(unsigned long);
|
|
unsigned long data;
|
|
u32 flags;
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
struct lockdep_map lockdep_map;
|
|
#endif
|
|
};
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
/*
|
|
* NB: because we have to copy the lockdep_map, setting the lockdep_map key
|
|
* (second argument) here is required, otherwise it could be initialised to
|
|
* the copy of the lockdep_map later! We use the pointer to and the string
|
|
* "<file>:<line>" as the key resp. the name of the lockdep_map.
|
|
*/
|
|
#define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn) \
|
|
.lockdep_map = STATIC_LOCKDEP_MAP_INIT(_kn, &_kn),
|
|
#else
|
|
#define __TIMER_LOCKDEP_MAP_INITIALIZER(_kn)
|
|
#endif
|
|
|
|
/*
|
|
* A deferrable timer will work normally when the system is busy, but
|
|
* will not cause a CPU to come out of idle just to service it; instead,
|
|
* the timer will be serviced when the CPU eventually wakes up with a
|
|
* subsequent non-deferrable timer.
|
|
*
|
|
* An irqsafe timer is executed with IRQ disabled and it's safe to wait for
|
|
* the completion of the running instance from IRQ handlers, for example,
|
|
* by calling del_timer_sync().
|
|
*
|
|
* Note: The irq disabled callback execution is a special case for
|
|
* workqueue locking issues. It's not meant for executing random crap
|
|
* with interrupts disabled. Abuse is monitored!
|
|
*/
|
|
#define TIMER_CPUMASK 0x0003FFFF
|
|
#define TIMER_MIGRATING 0x00040000
|
|
#define TIMER_BASEMASK (TIMER_CPUMASK | TIMER_MIGRATING)
|
|
#define TIMER_DEFERRABLE 0x00080000
|
|
#define TIMER_PINNED 0x00100000
|
|
#define TIMER_IRQSAFE 0x00200000
|
|
#define TIMER_ARRAYSHIFT 22
|
|
#define TIMER_ARRAYMASK 0xFFC00000
|
|
|
|
#define TIMER_TRACE_FLAGMASK (TIMER_MIGRATING | TIMER_DEFERRABLE | TIMER_PINNED | TIMER_IRQSAFE)
|
|
|
|
#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
|
|
.entry = { .next = TIMER_ENTRY_STATIC }, \
|
|
.function = (_function), \
|
|
.expires = (_expires), \
|
|
.data = (_data), \
|
|
.flags = (_flags), \
|
|
__TIMER_LOCKDEP_MAP_INITIALIZER( \
|
|
__FILE__ ":" __stringify(__LINE__)) \
|
|
}
|
|
|
|
#define TIMER_INITIALIZER(_function, _expires, _data) \
|
|
__TIMER_INITIALIZER((_function), (_expires), (_data), 0)
|
|
|
|
#define TIMER_PINNED_INITIALIZER(_function, _expires, _data) \
|
|
__TIMER_INITIALIZER((_function), (_expires), (_data), TIMER_PINNED)
|
|
|
|
#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) \
|
|
__TIMER_INITIALIZER((_function), (_expires), (_data), TIMER_DEFERRABLE)
|
|
|
|
#define TIMER_PINNED_DEFERRED_INITIALIZER(_function, _expires, _data) \
|
|
__TIMER_INITIALIZER((_function), (_expires), (_data), TIMER_DEFERRABLE | TIMER_PINNED)
|
|
|
|
#define DEFINE_TIMER(_name, _function, _expires, _data) \
|
|
struct timer_list _name = \
|
|
TIMER_INITIALIZER(_function, _expires, _data)
|
|
|
|
void init_timer_key(struct timer_list *timer, unsigned int flags,
|
|
const char *name, struct lock_class_key *key);
|
|
|
|
#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
|
|
extern void init_timer_on_stack_key(struct timer_list *timer,
|
|
unsigned int flags, const char *name,
|
|
struct lock_class_key *key);
|
|
extern void destroy_timer_on_stack(struct timer_list *timer);
|
|
#else
|
|
static inline void destroy_timer_on_stack(struct timer_list *timer) { }
|
|
static inline void init_timer_on_stack_key(struct timer_list *timer,
|
|
unsigned int flags, const char *name,
|
|
struct lock_class_key *key)
|
|
{
|
|
init_timer_key(timer, flags, name, key);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
#define __init_timer(_timer, _flags) \
|
|
do { \
|
|
static struct lock_class_key __key; \
|
|
init_timer_key((_timer), (_flags), #_timer, &__key); \
|
|
} while (0)
|
|
|
|
#define __init_timer_on_stack(_timer, _flags) \
|
|
do { \
|
|
static struct lock_class_key __key; \
|
|
init_timer_on_stack_key((_timer), (_flags), #_timer, &__key); \
|
|
} while (0)
|
|
#else
|
|
#define __init_timer(_timer, _flags) \
|
|
init_timer_key((_timer), (_flags), NULL, NULL)
|
|
#define __init_timer_on_stack(_timer, _flags) \
|
|
init_timer_on_stack_key((_timer), (_flags), NULL, NULL)
|
|
#endif
|
|
|
|
#define init_timer(timer) \
|
|
__init_timer((timer), 0)
|
|
#define init_timer_pinned(timer) \
|
|
__init_timer((timer), TIMER_PINNED)
|
|
#define init_timer_deferrable(timer) \
|
|
__init_timer((timer), TIMER_DEFERRABLE)
|
|
#define init_timer_pinned_deferrable(timer) \
|
|
__init_timer((timer), TIMER_DEFERRABLE | TIMER_PINNED)
|
|
#define init_timer_on_stack(timer) \
|
|
__init_timer_on_stack((timer), 0)
|
|
|
|
#define __setup_timer(_timer, _fn, _data, _flags) \
|
|
do { \
|
|
__init_timer((_timer), (_flags)); \
|
|
(_timer)->function = (_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); \
|
|
(_timer)->data = (_data); \
|
|
} while (0)
|
|
|
|
#define setup_timer(timer, fn, data) \
|
|
__setup_timer((timer), (fn), (data), 0)
|
|
#define setup_pinned_timer(timer, fn, data) \
|
|
__setup_timer((timer), (fn), (data), TIMER_PINNED)
|
|
#define setup_deferrable_timer(timer, fn, data) \
|
|
__setup_timer((timer), (fn), (data), TIMER_DEFERRABLE)
|
|
#define setup_pinned_deferrable_timer(timer, fn, data) \
|
|
__setup_timer((timer), (fn), (data), TIMER_DEFERRABLE | TIMER_PINNED)
|
|
#define setup_timer_on_stack(timer, fn, data) \
|
|
__setup_timer_on_stack((timer), (fn), (data), 0)
|
|
#define setup_pinned_timer_on_stack(timer, fn, data) \
|
|
__setup_timer_on_stack((timer), (fn), (data), TIMER_PINNED)
|
|
#define setup_deferrable_timer_on_stack(timer, fn, data) \
|
|
__setup_timer_on_stack((timer), (fn), (data), TIMER_DEFERRABLE)
|
|
#define setup_pinned_deferrable_timer_on_stack(timer, fn, data) \
|
|
__setup_timer_on_stack((timer), (fn), (data), TIMER_DEFERRABLE | TIMER_PINNED)
|
|
|
|
#define TIMER_DATA_TYPE unsigned long
|
|
#define TIMER_FUNC_TYPE void (*)(TIMER_DATA_TYPE)
|
|
|
|
static inline void timer_setup(struct timer_list *timer,
|
|
void (*callback)(struct timer_list *),
|
|
unsigned int flags)
|
|
{
|
|
__setup_timer(timer, (TIMER_FUNC_TYPE)callback,
|
|
(TIMER_DATA_TYPE)timer, flags);
|
|
}
|
|
|
|
#define from_timer(var, callback_timer, timer_fieldname) \
|
|
container_of(callback_timer, typeof(*var), timer_fieldname)
|
|
|
|
/**
|
|
* timer_pending - is a timer pending?
|
|
* @timer: the timer in question
|
|
*
|
|
* timer_pending will tell whether a given timer is currently pending,
|
|
* or not. Callers must ensure serialization wrt. other operations done
|
|
* to this timer, eg. interrupt contexts, or other CPUs on SMP.
|
|
*
|
|
* return value: 1 if the timer is pending, 0 if not.
|
|
*/
|
|
static inline int timer_pending(const struct timer_list * timer)
|
|
{
|
|
return timer->entry.pprev != NULL;
|
|
}
|
|
|
|
extern void add_timer_on(struct timer_list *timer, int cpu);
|
|
extern int del_timer(struct timer_list * timer);
|
|
extern int mod_timer(struct timer_list *timer, unsigned long expires);
|
|
extern int mod_timer_pending(struct timer_list *timer, unsigned long expires);
|
|
|
|
/*
|
|
* The jiffies value which is added to now, when there is no timer
|
|
* in the timer wheel:
|
|
*/
|
|
#define NEXT_TIMER_MAX_DELTA ((1UL << 30) - 1)
|
|
|
|
extern void add_timer(struct timer_list *timer);
|
|
|
|
extern int try_to_del_timer_sync(struct timer_list *timer);
|
|
|
|
#ifdef CONFIG_SMP
|
|
extern int del_timer_sync(struct timer_list *timer);
|
|
#else
|
|
# define del_timer_sync(t) del_timer(t)
|
|
#endif
|
|
|
|
#define del_singleshot_timer_sync(t) del_timer_sync(t)
|
|
|
|
extern void init_timers(void);
|
|
extern void run_local_timers(void);
|
|
struct hrtimer;
|
|
extern enum hrtimer_restart it_real_fn(struct hrtimer *);
|
|
|
|
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
|
|
struct ctl_table;
|
|
|
|
extern unsigned int sysctl_timer_migration;
|
|
int timer_migration_handler(struct ctl_table *table, int write,
|
|
void __user *buffer, size_t *lenp,
|
|
loff_t *ppos);
|
|
#endif
|
|
|
|
unsigned long __round_jiffies(unsigned long j, int cpu);
|
|
unsigned long __round_jiffies_relative(unsigned long j, int cpu);
|
|
unsigned long round_jiffies(unsigned long j);
|
|
unsigned long round_jiffies_relative(unsigned long j);
|
|
|
|
unsigned long __round_jiffies_up(unsigned long j, int cpu);
|
|
unsigned long __round_jiffies_up_relative(unsigned long j, int cpu);
|
|
unsigned long round_jiffies_up(unsigned long j);
|
|
unsigned long round_jiffies_up_relative(unsigned long j);
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
int timers_dead_cpu(unsigned int cpu);
|
|
#else
|
|
#define timers_dead_cpu NULL
|
|
#endif
|
|
|
|
#endif
|