Skip to content

Commit

Permalink
Merge tag 'probes-fixes-v6.7-rc3' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/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
  • Loading branch information
torvalds committed Dec 2, 2023
2 parents 815fb87 + a1461f1 commit 669fc83
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 21 deletions.
13 changes: 4 additions & 9 deletions include/linux/kprobes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion include/linux/rethook.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
4 changes: 2 additions & 2 deletions kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
23 changes: 14 additions & 9 deletions kernel/trace/rethook.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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);
}
Expand All @@ -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.
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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))
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions lib/objpool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down

0 comments on commit 669fc83

Please sign in to comment.