]>
Commit | Line | Data |
---|---|---|
9fd83755 JR |
1 | From 1fd97f9f4f773e3e9dfc787e9c90b1418fa5a7d4 Mon Sep 17 00:00:00 2001 |
2 | From: Michael Jeanson <mjeanson@efficios.com> | |
3 | Date: Wed, 29 Nov 2017 17:03:21 -0500 | |
4 | Subject: [PATCH] timer API transition for kernel 4.15 | |
5 | ||
6 | The timer API changes starting from kernel 4.15.0. | |
7 | ||
8 | There's an interresting LWN article on this subject: | |
9 | ||
10 | https://lwn.net/Articles/735887/ | |
11 | ||
12 | Check these upstream commits for more details: | |
13 | ||
14 | commit 686fef928bba6be13cabe639f154af7d72b63120 | |
15 | Author: Kees Cook <keescook@chromium.org> | |
16 | Date: Thu Sep 28 06:38:17 2017 -0700 | |
17 | ||
18 | timer: Prepare to change timer callback argument type | |
19 | ||
20 | Modern kernel callback systems pass the structure associated with a | |
21 | given callback to the callback function. The timer callback remains one | |
22 | of the legacy cases where an arbitrary unsigned long argument continues | |
23 | to be passed as the callback argument. This has several problems: | |
24 | ||
25 | - This bloats the timer_list structure with a normally redundant | |
26 | .data field. | |
27 | ||
28 | - No type checking is being performed, forcing callbacks to do | |
29 | explicit type casts of the unsigned long argument into the object | |
30 | that was passed, rather than using container_of(), as done in most | |
31 | of the other callback infrastructure. | |
32 | ||
33 | - Neighboring buffer overflows can overwrite both the .function and | |
34 | the .data field, providing attackers with a way to elevate from a buffer | |
35 | overflow into a simplistic ROP-like mechanism that allows calling | |
36 | arbitrary functions with a controlled first argument. | |
37 | ||
38 | - For future Control Flow Integrity work, this creates a unique function | |
39 | prototype for timer callbacks, instead of allowing them to continue to | |
40 | be clustered with other void functions that take a single unsigned long | |
41 | argument. | |
42 | ||
43 | This adds a new timer initialization API, which will ultimately replace | |
44 | the existing setup_timer(), setup_{deferrable,pinned,etc}_timer() family, | |
45 | named timer_setup() (to mirror hrtimer_setup(), making instances of its | |
46 | use much easier to grep for). | |
47 | ||
48 | In order to support the migration of existing timers into the new | |
49 | callback arguments, timer_setup() casts its arguments to the existing | |
50 | legacy types, and explicitly passes the timer pointer as the legacy | |
51 | data argument. Once all setup_*timer() callers have been replaced with | |
52 | timer_setup(), the casts can be removed, and the data argument can be | |
53 | dropped with the timer expiration code changed to just pass the timer | |
54 | to the callback directly. | |
55 | ||
56 | : | |
57 | Modern kernel callback systems pass the structure associated with a | |
58 | given callback to the callback function. The timer callback remains one | |
59 | of the legacy cases where an arbitrary unsigned long argument continues | |
60 | to be passed as the callback argument. This has several problems: | |
61 | ||
62 | - This bloats the timer_list structure with a normally redundant | |
63 | .data field. | |
64 | ||
65 | - No type checking is being performed, forcing callbacks to do | |
66 | explicit type casts of the unsigned long argument into the object | |
67 | that was passed, rather than using container_of(), as done in most | |
68 | of the other callback infrastructure. | |
69 | ||
70 | - Neighboring buffer overflows can overwrite both the .function and | |
71 | the .data field, providing attackers with a way to elevate from a buffer | |
72 | overflow into a simplistic ROP-like mechanism that allows calling | |
73 | arbitrary functions with a controlled first argument. | |
74 | ||
75 | - For future Control Flow Integrity work, this creates a unique function | |
76 | prototype for timer callbacks, instead of allowing them to continue to | |
77 | be clustered with other void functions that take a single unsigned long | |
78 | argument. | |
79 | ||
80 | This adds a new timer initialization API, which will ultimately replace | |
81 | the existing setup_timer(), setup_{deferrable,pinned,etc}_timer() family, | |
82 | named timer_setup() (to mirror hrtimer_setup(), making instances of its | |
83 | use much easier to grep for). | |
84 | ||
85 | In order to support the migration of existing timers into the new | |
86 | callback arguments, timer_setup() casts its arguments to the existing | |
87 | legacy types, and explicitly passes the timer pointer as the legacy | |
88 | data argument. Once all setup_*timer() callers have been replaced with | |
89 | timer_setup(), the casts can be removed, and the data argument can be | |
90 | dropped with the timer expiration code changed to just pass the timer | |
91 | to the callback directly. | |
92 | ||
93 | Since the regular pattern of using container_of() during local variable | |
94 | declaration repeats the need for the variable type declaration | |
95 | to be included, this adds a helper modeled after other from_*() | |
96 | helpers that wrap container_of(), named from_timer(). This helper uses | |
97 | typeof(*variable), removing the type redundancy and minimizing the need | |
98 | for line wraps in forthcoming conversions from "unsigned data long" to | |
99 | "struct timer_list *" in the timer callbacks: | |
100 | ||
101 | -void callback(unsigned long data) | |
102 | +void callback(struct timer_list *t) | |
103 | { | |
104 | - struct some_data_structure *local = (struct some_data_structure *)data; | |
105 | + struct some_data_structure *local = from_timer(local, t, timer); | |
106 | ||
107 | Finally, in order to support the handful of timer users that perform | |
108 | open-coded assignments of the .function (and .data) fields, provide | |
109 | cast macros (TIMER_FUNC_TYPE and TIMER_DATA_TYPE) that can be used | |
110 | temporarily. Once conversion has been completed, these can be globally | |
111 | trivially removed. | |
112 | ||
113 | ... | |
114 | ||
115 | commit e99e88a9d2b067465adaa9c111ada99a041bef9a | |
116 | Author: Kees Cook <keescook@chromium.org> | |
117 | Date: Mon Oct 16 14:43:17 2017 -0700 | |
118 | ||
119 | treewide: setup_timer() -> timer_setup() | |
120 | ||
121 | This converts all remaining cases of the old setup_timer() API into using | |
122 | timer_setup(), where the callback argument is the structure already | |
123 | holding the struct timer_list. These should have no behavioral changes, | |
124 | since they just change which pointer is passed into the callback with | |
125 | the same available pointers after conversion. It handles the following | |
126 | examples, in addition to some other variations. | |
127 | ||
128 | ... | |
129 | ||
130 | commit 185981d54a60ae90942c6ba9006b250f3348cef2 | |
131 | Author: Kees Cook <keescook@chromium.org> | |
132 | Date: Wed Oct 4 16:26:58 2017 -0700 | |
133 | ||
134 | timer: Remove init_timer_pinned() in favor of timer_setup() | |
135 | ||
136 | This refactors the only users of init_timer_pinned() to use | |
137 | the new timer_setup() and from_timer(). Drops the definition of | |
138 | init_timer_pinned(). | |
139 | ||
140 | ... | |
141 | ||
142 | Signed-off-by: Michael Jeanson <mjeanson@efficios.com> | |
143 | Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
144 | --- | |
145 | lib/ringbuffer/ring_buffer_frontend.c | 27 +++++++------ | |
146 | wrapper/timer.h | 72 ++++++++++++++++++++++++++++------- | |
147 | 2 files changed, 72 insertions(+), 27 deletions(-) | |
148 | ||
149 | diff --git a/lib/ringbuffer/ring_buffer_frontend.c b/lib/ringbuffer/ring_buffer_frontend.c | |
150 | index bdd31ad..abd9757 100644 | |
151 | --- a/lib/ringbuffer/ring_buffer_frontend.c | |
152 | +++ b/lib/ringbuffer/ring_buffer_frontend.c | |
153 | @@ -314,9 +314,9 @@ int lib_ring_buffer_create(struct lib_ring_buffer *buf, | |
154 | return ret; | |
155 | } | |
156 | ||
157 | -static void switch_buffer_timer(unsigned long data) | |
158 | +static void switch_buffer_timer(LTTNG_TIMER_FUNC_ARG_TYPE t) | |
159 | { | |
160 | - struct lib_ring_buffer *buf = (struct lib_ring_buffer *)data; | |
161 | + struct lib_ring_buffer *buf = lttng_from_timer(buf, t, switch_timer); | |
162 | struct channel *chan = buf->backend.chan; | |
163 | const struct lib_ring_buffer_config *config = &chan->backend.config; | |
164 | ||
165 | @@ -341,22 +341,22 @@ static void lib_ring_buffer_start_switch_timer(struct lib_ring_buffer *buf) | |
166 | { | |
167 | struct channel *chan = buf->backend.chan; | |
168 | const struct lib_ring_buffer_config *config = &chan->backend.config; | |
169 | + unsigned int flags = 0; | |
170 | ||
171 | if (!chan->switch_timer_interval || buf->switch_timer_enabled) | |
172 | return; | |
173 | ||
174 | if (config->alloc == RING_BUFFER_ALLOC_PER_CPU) | |
175 | - lttng_init_timer_pinned(&buf->switch_timer); | |
176 | - else | |
177 | - init_timer(&buf->switch_timer); | |
178 | + flags = LTTNG_TIMER_PINNED; | |
179 | ||
180 | - buf->switch_timer.function = switch_buffer_timer; | |
181 | + lttng_timer_setup(&buf->switch_timer, switch_buffer_timer, flags, buf); | |
182 | buf->switch_timer.expires = jiffies + chan->switch_timer_interval; | |
183 | - buf->switch_timer.data = (unsigned long)buf; | |
184 | + | |
185 | if (config->alloc == RING_BUFFER_ALLOC_PER_CPU) | |
186 | add_timer_on(&buf->switch_timer, buf->backend.cpu); | |
187 | else | |
188 | add_timer(&buf->switch_timer); | |
189 | + | |
190 | buf->switch_timer_enabled = 1; | |
191 | } | |
192 | ||
193 | @@ -377,9 +377,9 @@ static void lib_ring_buffer_stop_switch_timer(struct lib_ring_buffer *buf) | |
194 | /* | |
195 | * Polling timer to check the channels for data. | |
196 | */ | |
197 | -static void read_buffer_timer(unsigned long data) | |
198 | +static void read_buffer_timer(LTTNG_TIMER_FUNC_ARG_TYPE t) | |
199 | { | |
200 | - struct lib_ring_buffer *buf = (struct lib_ring_buffer *)data; | |
201 | + struct lib_ring_buffer *buf = lttng_from_timer(buf, t, read_timer); | |
202 | struct channel *chan = buf->backend.chan; | |
203 | const struct lib_ring_buffer_config *config = &chan->backend.config; | |
204 | ||
205 | @@ -406,6 +406,7 @@ static void lib_ring_buffer_start_read_timer(struct lib_ring_buffer *buf) | |
206 | { | |
207 | struct channel *chan = buf->backend.chan; | |
208 | const struct lib_ring_buffer_config *config = &chan->backend.config; | |
209 | + unsigned int flags; | |
210 | ||
211 | if (config->wakeup != RING_BUFFER_WAKEUP_BY_TIMER | |
212 | || !chan->read_timer_interval | |
213 | @@ -413,18 +414,16 @@ static void lib_ring_buffer_start_read_timer(struct lib_ring_buffer *buf) | |
214 | return; | |
215 | ||
216 | if (config->alloc == RING_BUFFER_ALLOC_PER_CPU) | |
217 | - lttng_init_timer_pinned(&buf->read_timer); | |
218 | - else | |
219 | - init_timer(&buf->read_timer); | |
220 | + flags = LTTNG_TIMER_PINNED; | |
221 | ||
222 | - buf->read_timer.function = read_buffer_timer; | |
223 | + lttng_timer_setup(&buf->read_timer, read_buffer_timer, flags, buf); | |
224 | buf->read_timer.expires = jiffies + chan->read_timer_interval; | |
225 | - buf->read_timer.data = (unsigned long)buf; | |
226 | ||
227 | if (config->alloc == RING_BUFFER_ALLOC_PER_CPU) | |
228 | add_timer_on(&buf->read_timer, buf->backend.cpu); | |
229 | else | |
230 | add_timer(&buf->read_timer); | |
231 | + | |
232 | buf->read_timer_enabled = 1; | |
233 | } | |
234 | ||
235 | diff --git a/wrapper/timer.h b/wrapper/timer.h | |
236 | index c1c0c95..4fc9828 100644 | |
237 | --- a/wrapper/timer.h | |
238 | +++ b/wrapper/timer.h | |
239 | @@ -27,30 +27,76 @@ | |
240 | #include <linux/timer.h> | |
241 | #include <lttng-kernel-version.h> | |
242 | ||
243 | +/* | |
244 | + * In the olden days, pinned timers were initialized normaly with init_timer() | |
245 | + * and then modified with mod_timer_pinned(). | |
246 | + * | |
247 | + * Then came kernel 4.8.0 and they had to be initilized as pinned with | |
248 | + * init_timer_pinned() and then modified as regular timers with mod_timer(). | |
249 | + * | |
250 | + * Then came kernel 4.15.0 with a new timer API where init_timer() is no more. | |
251 | + * It's replaced by timer_setup() where pinned is now part of timer flags. | |
252 | + */ | |
253 | + | |
254 | + | |
255 | +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)) | |
256 | + | |
257 | +#define LTTNG_TIMER_PINNED TIMER_PINNED | |
258 | +#define LTTNG_TIMER_FUNC_ARG_TYPE struct timer_list * | |
259 | + | |
260 | +#define lttng_mod_timer_pinned(timer, expires) \ | |
261 | + mod_timer(timer, expires) | |
262 | + | |
263 | +#define lttng_from_timer(var, callback_timer, timer_fieldname) \ | |
264 | + from_timer(var, callback_timer, timer_fieldname) | |
265 | + | |
266 | +#define lttng_timer_setup(timer, callback, flags, unused) \ | |
267 | + timer_setup(timer, callback, flags) | |
268 | + | |
269 | ||
270 | -#if (LTTNG_RT_VERSION_CODE >= LTTNG_RT_KERNEL_VERSION(4,6,4,8) \ | |
271 | +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) */ | |
272 | + | |
273 | + | |
274 | +# if (LTTNG_RT_VERSION_CODE >= LTTNG_RT_KERNEL_VERSION(4,6,4,8) \ | |
275 | || LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) | |
276 | ||
277 | -#define lttng_init_timer_pinned(timer) \ | |
278 | +#define lttng_init_timer_pinned(timer) \ | |
279 | init_timer_pinned(timer) | |
280 | ||
281 | -static inline int lttng_mod_timer_pinned(struct timer_list *timer, | |
282 | - unsigned long expires) | |
283 | -{ | |
284 | - return mod_timer(timer, expires); | |
285 | -} | |
286 | +#define lttng_mod_timer_pinned(timer, expires) \ | |
287 | + mod_timer(timer, expires) | |
288 | ||
289 | -#else | |
290 | +# else /* LTTNG_RT_VERSION_CODE >= LTTNG_RT_KERNEL_VERSION(4,6,4,8) */ | |
291 | ||
292 | -#define lttng_init_timer_pinned(timer) \ | |
293 | +#define lttng_init_timer_pinned(timer) \ | |
294 | init_timer(timer) | |
295 | ||
296 | -static inline int lttng_mod_timer_pinned(struct timer_list *timer, | |
297 | - unsigned long expires) | |
298 | +#define lttng_mod_timer_pinned(timer, expires) \ | |
299 | + mod_timer_pinned(timer, expires) | |
300 | + | |
301 | +# endif /* LTTNG_RT_VERSION_CODE >= LTTNG_RT_KERNEL_VERSION(4,6,4,8) */ | |
302 | + | |
303 | + | |
304 | +#define LTTNG_TIMER_PINNED TIMER_PINNED | |
305 | +#define LTTNG_TIMER_FUNC_ARG_TYPE unsigned long | |
306 | + | |
307 | +/* timer_fieldname is unused prior to 4.15. */ | |
308 | +#define lttng_from_timer(var, timer_data, timer_fieldname) \ | |
309 | + ((typeof(var))timer_data) | |
310 | + | |
311 | +static inline void lttng_timer_setup(struct timer_list *timer, | |
312 | + void (*function)(LTTNG_TIMER_FUNC_ARG_TYPE), | |
313 | + unsigned int flags, void *data) | |
314 | { | |
315 | - return mod_timer_pinned(timer, expires); | |
316 | + if (flags & LTTNG_TIMER_PINNED) | |
317 | + lttng_init_timer_pinned(timer); | |
318 | + else | |
319 | + init_timer(timer); | |
320 | + | |
321 | + timer->function = function; | |
322 | + timer->data = (unsigned long)data; | |
323 | } | |
324 | ||
325 | -#endif | |
326 | +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) */ | |
327 | ||
328 | #endif /* _LTTNG_WRAPPER_TIMER_H */ |