mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
Probes fixes for v6.7-r3:
- objpool: Fix objpool overrun case on memory/cache access delay especially on the big.LITTLE SoC. The objpool uses a copy of object slot index internal loop, but the slot index can be changed on another processor in parallel. In that case, the difference of 'head' local copy and the 'slot->last' index will be bigger than local slot size. In that case, we need to re-read the slot::head to update it. - kretprobe: Fix to use appropriate rcu API for kretprobe holder. Since kretprobe_holder::rp is RCU managed, it should use rcu_assign_pointer() and rcu_dereference_check() correctly. Also adding __rcu tag for finding wrong usage by sparse. - rethook: Fix to use appropriate rcu API for rethook::handler. The same as kretprobe, rethook::handler is RCU managed and it should use rcu_assign_pointer() and rcu_dereference_check(). This also adds __rcu tag for finding wrong usage by sparse. -----BEGIN PGP SIGNATURE----- iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmVpfBobHG1hc2FtaS5o aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8bNyMIAJSLICKQNuFiBJEn/rty ACWJ9QMOnwi0DoVaepG/m9QJh6AIUUFW4//9helmSm0GIVzxQ2+f8UeKU+sYiVtH ro9atea4W4+FMTvtEB1cU8oG5CDVT4WQdUXbjMktqYe3+WB8Zt8+fIP0mnbTFAVr yStpliGPecmlupJVRYqrJGyDdbkUxXxVlPsP/eDrHFgbBWv8Incw0f+MLGSi6LSE sZ1MaKCdi2tlHbtD/fiowfLoBMZwQAKY4hq/XguVsWh+BGaGUgwtif+8ESwPeu22 KEZLyWDQ1N8XBHyOBotV7vsBEwh6LKtLGVXIBsO4KxVyGw6msxWBis0dt/tkn+kk LEg= =B9WK -----END PGP SIGNATURE----- Merge tag 'probes-fixes-v6.7-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull probes fixes from Masami Hiramatsu: - objpool: Fix objpool overrun case on memory/cache access delay especially on the big.LITTLE SoC. The objpool uses a copy of object slot index internal loop, but the slot index can be changed on another processor in parallel. In that case, the difference of 'head' local copy and the 'slot->last' index will be bigger than local slot size. In that case, we need to re-read the slot::head to update it. - kretprobe: Fix to use appropriate rcu API for kretprobe holder. Since kretprobe_holder::rp is RCU managed, it should use rcu_assign_pointer() and rcu_dereference_check() correctly. Also adding __rcu tag for finding wrong usage by sparse. - rethook: Fix to use appropriate rcu API for rethook::handler. The same as kretprobe, rethook::handler is RCU managed and it should use rcu_assign_pointer() and rcu_dereference_check(). This also adds __rcu tag for finding wrong usage by sparse. * tag 'probes-fixes-v6.7-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: rethook: Use __rcu pointer for rethook::handler kprobes: consistent rcu api usage for kretprobe holder lib: objpool: fix head overrun on RK3588 SBC
This commit is contained in:
commit
669fc83452
@ -139,7 +139,7 @@ static inline bool kprobe_ftrace(struct kprobe *p)
|
||||
*
|
||||
*/
|
||||
struct kretprobe_holder {
|
||||
struct kretprobe *rp;
|
||||
struct kretprobe __rcu *rp;
|
||||
struct objpool_head pool;
|
||||
};
|
||||
|
||||
@ -197,10 +197,8 @@ extern int arch_trampoline_kprobe(struct kprobe *p);
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
static nokprobe_inline struct kretprobe *get_kretprobe(struct kretprobe_instance *ri)
|
||||
{
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(),
|
||||
"Kretprobe is accessed from instance under preemptive context");
|
||||
|
||||
return (struct kretprobe *)READ_ONCE(ri->node.rethook->data);
|
||||
/* rethook::data is non-changed field, so that you can access it freely. */
|
||||
return (struct kretprobe *)ri->node.rethook->data;
|
||||
}
|
||||
static nokprobe_inline unsigned long get_kretprobe_retaddr(struct kretprobe_instance *ri)
|
||||
{
|
||||
@ -245,10 +243,7 @@ unsigned long kretprobe_trampoline_handler(struct pt_regs *regs,
|
||||
|
||||
static nokprobe_inline struct kretprobe *get_kretprobe(struct kretprobe_instance *ri)
|
||||
{
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(),
|
||||
"Kretprobe is accessed from instance under preemptive context");
|
||||
|
||||
return READ_ONCE(ri->rph->rp);
|
||||
return rcu_dereference_check(ri->rph->rp, rcu_read_lock_any_held());
|
||||
}
|
||||
|
||||
static nokprobe_inline unsigned long get_kretprobe_retaddr(struct kretprobe_instance *ri)
|
||||
|
@ -28,7 +28,12 @@ typedef void (*rethook_handler_t) (struct rethook_node *, void *, unsigned long,
|
||||
*/
|
||||
struct rethook {
|
||||
void *data;
|
||||
rethook_handler_t handler;
|
||||
/*
|
||||
* To avoid sparse warnings, this uses a raw function pointer with
|
||||
* __rcu, instead of rethook_handler_t. But this must be same as
|
||||
* rethook_handler_t.
|
||||
*/
|
||||
void (__rcu *handler) (struct rethook_node *, void *, unsigned long, struct pt_regs *);
|
||||
struct objpool_head pool;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
@ -2252,7 +2252,7 @@ int register_kretprobe(struct kretprobe *rp)
|
||||
rp->rph = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
rp->rph->rp = rp;
|
||||
rcu_assign_pointer(rp->rph->rp, rp);
|
||||
rp->nmissed = 0;
|
||||
/* Establish function entry probe point */
|
||||
ret = register_kprobe(&rp->kp);
|
||||
@ -2300,7 +2300,7 @@ void unregister_kretprobes(struct kretprobe **rps, int num)
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
rethook_free(rps[i]->rh);
|
||||
#else
|
||||
rps[i]->rph->rp = NULL;
|
||||
rcu_assign_pointer(rps[i]->rph->rp, NULL);
|
||||
#endif
|
||||
}
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
|
@ -48,7 +48,7 @@ static void rethook_free_rcu(struct rcu_head *head)
|
||||
*/
|
||||
void rethook_stop(struct rethook *rh)
|
||||
{
|
||||
WRITE_ONCE(rh->handler, NULL);
|
||||
rcu_assign_pointer(rh->handler, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,7 +63,7 @@ void rethook_stop(struct rethook *rh)
|
||||
*/
|
||||
void rethook_free(struct rethook *rh)
|
||||
{
|
||||
WRITE_ONCE(rh->handler, NULL);
|
||||
rethook_stop(rh);
|
||||
|
||||
call_rcu(&rh->rcu, rethook_free_rcu);
|
||||
}
|
||||
@ -82,6 +82,12 @@ static int rethook_fini_pool(struct objpool_head *head, void *context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline rethook_handler_t rethook_get_handler(struct rethook *rh)
|
||||
{
|
||||
return (rethook_handler_t)rcu_dereference_check(rh->handler,
|
||||
rcu_read_lock_any_held());
|
||||
}
|
||||
|
||||
/**
|
||||
* rethook_alloc() - Allocate struct rethook.
|
||||
* @data: a data to pass the @handler when hooking the return.
|
||||
@ -107,7 +113,7 @@ struct rethook *rethook_alloc(void *data, rethook_handler_t handler,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rh->data = data;
|
||||
rh->handler = handler;
|
||||
rcu_assign_pointer(rh->handler, handler);
|
||||
|
||||
/* initialize the objpool for rethook nodes */
|
||||
if (objpool_init(&rh->pool, num, size, GFP_KERNEL, rh,
|
||||
@ -135,9 +141,10 @@ static void free_rethook_node_rcu(struct rcu_head *head)
|
||||
*/
|
||||
void rethook_recycle(struct rethook_node *node)
|
||||
{
|
||||
lockdep_assert_preemption_disabled();
|
||||
rethook_handler_t handler;
|
||||
|
||||
if (likely(READ_ONCE(node->rethook->handler)))
|
||||
handler = rethook_get_handler(node->rethook);
|
||||
if (likely(handler))
|
||||
objpool_push(node, &node->rethook->pool);
|
||||
else
|
||||
call_rcu(&node->rcu, free_rethook_node_rcu);
|
||||
@ -153,9 +160,7 @@ NOKPROBE_SYMBOL(rethook_recycle);
|
||||
*/
|
||||
struct rethook_node *rethook_try_get(struct rethook *rh)
|
||||
{
|
||||
rethook_handler_t handler = READ_ONCE(rh->handler);
|
||||
|
||||
lockdep_assert_preemption_disabled();
|
||||
rethook_handler_t handler = rethook_get_handler(rh);
|
||||
|
||||
/* Check whether @rh is going to be freed. */
|
||||
if (unlikely(!handler))
|
||||
@ -300,7 +305,7 @@ unsigned long rethook_trampoline_handler(struct pt_regs *regs,
|
||||
rhn = container_of(first, struct rethook_node, llist);
|
||||
if (WARN_ON_ONCE(rhn->frame != frame))
|
||||
break;
|
||||
handler = READ_ONCE(rhn->rethook->handler);
|
||||
handler = rethook_get_handler(rhn->rethook);
|
||||
if (handler)
|
||||
handler(rhn, rhn->rethook->data,
|
||||
correct_ret_addr, regs);
|
||||
|
@ -201,6 +201,23 @@ static inline void *objpool_try_get_slot(struct objpool_head *pool, int cpu)
|
||||
while (head != READ_ONCE(slot->last)) {
|
||||
void *obj;
|
||||
|
||||
/*
|
||||
* data visibility of 'last' and 'head' could be out of
|
||||
* order since memory updating of 'last' and 'head' are
|
||||
* performed in push() and pop() independently
|
||||
*
|
||||
* before any retrieving attempts, pop() must guarantee
|
||||
* 'last' is behind 'head', that is to say, there must
|
||||
* be available objects in slot, which could be ensured
|
||||
* by condition 'last != head && last - head <= nr_objs'
|
||||
* that is equivalent to 'last - head - 1 < nr_objs' as
|
||||
* 'last' and 'head' are both unsigned int32
|
||||
*/
|
||||
if (READ_ONCE(slot->last) - head - 1 >= pool->nr_objs) {
|
||||
head = READ_ONCE(slot->head);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* obj must be retrieved before moving forward head */
|
||||
obj = READ_ONCE(slot->entries[head & slot->mask]);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user