When executing a tracepoint, the tracepoint's func is dereferenced twice - in __DO_TRACE() (where the returned pointer is checked) and later on in __traceiter_##_name where the returned pointer is dereferenced without checking which leads to races against tracepoint_removal_sync() and crashes. This adds a check before referencing the pointer in tracepoint_ptr_deref. Link: https://lkml.kernel.org/r/20210202072326.120557-1-aik@ozlabs.ru
Showing the changes to include/linux (1 of 1 file). Show all
Unified · Split
| @@ -307,11 +307,13 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) · 306 unmodified lines | |||
| 307 | \ | 307 | \ |
| 308 | it_func_ptr = \ | 308 | it_func_ptr = \ |
| 309 | rcu_dereference_raw((&__tracepoint_##_name)->funcs); \ | 309 | rcu_dereference_raw((&__tracepoint_##_name)->funcs); \ |
| 310 | do { \ | 310 | if (it_func_ptr) { \ |
| 311 | it_func = (it_func_ptr)->func; \ | 311 | do { \ |
| 312 | __data = (it_func_ptr)->data; \ | 312 | it_func = (it_func_ptr)->func; \ |
| 313 | ((void(*)(void *, proto))(it_func))(__data, args); \ | 313 | __data = (it_func_ptr)->data; \ |
| 314 | } while ((++it_func_ptr)->func); \ | 314 | ((void(*)(void *, proto))(it_func))(__data, args); \ |
| 315 | } while ((++it_func_ptr)->func); \ | ||
| 316 | } \ | ||
| 315 | return 0; \ | 317 | return 0; \ |
| 316 | } \ | 318 | } \ |
| 317 | DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); | 319 | DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); |