Skip to content

Commit d24fa97

Browse files
committed
tracing: fprobe: Fix to lock module while registering fprobe
Since register_fprobe() does not get the module reference count while registering fgraph filter, if the target functions (symbols) are in modules, those modules can be unloaded when registering fprobe to fgraph. To avoid this issue, get the reference counter of module for each symbol, and put it after register the fprobe. Link: https://lore.kernel.org/all/174330568792.459674.16874380163991113156.stgit@devnote2/ Reported-by: Steven Rostedt <rostedt@goodmis.org> Closes: https://lore.kernel.org/all/20250325130628.3a9e234c@gandalf.local.home/ Fixes: 4346ba1 ("fprobe: Rewrite fprobe on function-graph tracer") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
1 parent 38fec10 commit d24fa97

File tree

1 file changed

+48
-19
lines changed

1 file changed

+48
-19
lines changed

kernel/trace/fprobe.c

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ struct filter_match_data {
445445
size_t index;
446446
size_t size;
447447
unsigned long *addrs;
448+
struct module **mods;
448449
};
449450

450451
static int filter_match_callback(void *data, const char *name, unsigned long addr)
@@ -458,30 +459,47 @@ static int filter_match_callback(void *data, const char *name, unsigned long add
458459
if (!ftrace_location(addr))
459460
return 0;
460461

461-
if (match->addrs)
462-
match->addrs[match->index] = addr;
462+
if (match->addrs) {
463+
struct module *mod = __module_text_address(addr);
464+
465+
if (mod && !try_module_get(mod))
466+
return 0;
463467

468+
match->mods[match->index] = mod;
469+
match->addrs[match->index] = addr;
470+
}
464471
match->index++;
465472
return match->index == match->size;
466473
}
467474

468475
/*
469476
* Make IP list from the filter/no-filter glob patterns.
470-
* Return the number of matched symbols, or -ENOENT.
477+
* Return the number of matched symbols, or errno.
478+
* If @addrs == NULL, this just counts the number of matched symbols. If @addrs
479+
* is passed with an array, we need to pass the an @mods array of the same size
480+
* to increment the module refcount for each symbol.
481+
* This means we also need to call `module_put` for each element of @mods after
482+
* using the @addrs.
471483
*/
472-
static int ip_list_from_filter(const char *filter, const char *notfilter,
473-
unsigned long *addrs, size_t size)
484+
static int get_ips_from_filter(const char *filter, const char *notfilter,
485+
unsigned long *addrs, struct module **mods,
486+
size_t size)
474487
{
475488
struct filter_match_data match = { .filter = filter, .notfilter = notfilter,
476-
.index = 0, .size = size, .addrs = addrs};
489+
.index = 0, .size = size, .addrs = addrs, .mods = mods};
477490
int ret;
478491

492+
if (addrs && !mods)
493+
return -EINVAL;
494+
479495
ret = kallsyms_on_each_symbol(filter_match_callback, &match);
480496
if (ret < 0)
481497
return ret;
482-
ret = module_kallsyms_on_each_symbol(NULL, filter_match_callback, &match);
483-
if (ret < 0)
484-
return ret;
498+
if (IS_ENABLED(CONFIG_MODULES)) {
499+
ret = module_kallsyms_on_each_symbol(NULL, filter_match_callback, &match);
500+
if (ret < 0)
501+
return ret;
502+
}
485503

486504
return match.index ?: -ENOENT;
487505
}
@@ -543,24 +561,35 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num)
543561
*/
544562
int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
545563
{
546-
unsigned long *addrs;
547-
int ret;
564+
unsigned long *addrs __free(kfree) = NULL;
565+
struct module **mods __free(kfree) = NULL;
566+
int ret, num;
548567

549568
if (!fp || !filter)
550569
return -EINVAL;
551570

552-
ret = ip_list_from_filter(filter, notfilter, NULL, FPROBE_IPS_MAX);
553-
if (ret < 0)
554-
return ret;
571+
num = get_ips_from_filter(filter, notfilter, NULL, NULL, FPROBE_IPS_MAX);
572+
if (num < 0)
573+
return num;
555574

556-
addrs = kcalloc(ret, sizeof(unsigned long), GFP_KERNEL);
575+
addrs = kcalloc(num, sizeof(*addrs), GFP_KERNEL);
557576
if (!addrs)
558577
return -ENOMEM;
559-
ret = ip_list_from_filter(filter, notfilter, addrs, ret);
560-
if (ret > 0)
561-
ret = register_fprobe_ips(fp, addrs, ret);
562578

563-
kfree(addrs);
579+
mods = kcalloc(num, sizeof(*mods), GFP_KERNEL);
580+
if (!mods)
581+
return -ENOMEM;
582+
583+
ret = get_ips_from_filter(filter, notfilter, addrs, mods, num);
584+
if (ret < 0)
585+
return ret;
586+
587+
ret = register_fprobe_ips(fp, addrs, ret);
588+
589+
for (int i = 0; i < num; i++) {
590+
if (mods[i])
591+
module_put(mods[i]);
592+
}
564593
return ret;
565594
}
566595
EXPORT_SYMBOL_GPL(register_fprobe);

0 commit comments

Comments
 (0)