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
Unified · Split
| @@ -307,11 +307,13 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) · 306 unmodified lines | ||
| 307 | 307 | \ |
| 308 | 308 | it_func_ptr = \ |
| 309 | 309 | rcu_dereference_raw((&__tracepoint_##_name)->funcs); \ |
| 310 | − do { \ | |
| 311 | − it_func = (it_func_ptr)->func; \ | |
| 312 | − __data = (it_func_ptr)->data; \ | |
| 313 | − ((void(*)(void *, proto))(it_func))(__data, args); \ | |
| 314 | − } while ((++it_func_ptr)->func); \ | |
| 310 | + if (it_func_ptr) { \ | |
| 311 | + do { \ | |
| 312 | + it_func = (it_func_ptr)->func; \ | |
| 313 | + __data = (it_func_ptr)->data; \ | |
| 314 | + ((void(*)(void *, proto))(it_func))(__data, args); \ | |
| 315 | + } while ((++it_func_ptr)->func); \ | |
| 316 | + } \ | |
| 315 | 317 | return 0; \ |
| 316 | 318 | } \ |
| 317 | 319 | DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); |