mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 02:21:39 -04:00
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: 4346ba1604 ("fprobe: Rewrite fprobe on function-graph tracer")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
This commit is contained in:
@@ -445,6 +445,7 @@ struct filter_match_data {
|
|||||||
size_t index;
|
size_t index;
|
||||||
size_t size;
|
size_t size;
|
||||||
unsigned long *addrs;
|
unsigned long *addrs;
|
||||||
|
struct module **mods;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int filter_match_callback(void *data, const char *name, unsigned long addr)
|
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
|
|||||||
if (!ftrace_location(addr))
|
if (!ftrace_location(addr))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (match->addrs)
|
if (match->addrs) {
|
||||||
match->addrs[match->index] = addr;
|
struct module *mod = __module_text_address(addr);
|
||||||
|
|
||||||
|
if (mod && !try_module_get(mod))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
match->mods[match->index] = mod;
|
||||||
|
match->addrs[match->index] = addr;
|
||||||
|
}
|
||||||
match->index++;
|
match->index++;
|
||||||
return match->index == match->size;
|
return match->index == match->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make IP list from the filter/no-filter glob patterns.
|
* Make IP list from the filter/no-filter glob patterns.
|
||||||
* Return the number of matched symbols, or -ENOENT.
|
* Return the number of matched symbols, or errno.
|
||||||
|
* If @addrs == NULL, this just counts the number of matched symbols. If @addrs
|
||||||
|
* is passed with an array, we need to pass the an @mods array of the same size
|
||||||
|
* to increment the module refcount for each symbol.
|
||||||
|
* This means we also need to call `module_put` for each element of @mods after
|
||||||
|
* using the @addrs.
|
||||||
*/
|
*/
|
||||||
static int ip_list_from_filter(const char *filter, const char *notfilter,
|
static int get_ips_from_filter(const char *filter, const char *notfilter,
|
||||||
unsigned long *addrs, size_t size)
|
unsigned long *addrs, struct module **mods,
|
||||||
|
size_t size)
|
||||||
{
|
{
|
||||||
struct filter_match_data match = { .filter = filter, .notfilter = notfilter,
|
struct filter_match_data match = { .filter = filter, .notfilter = notfilter,
|
||||||
.index = 0, .size = size, .addrs = addrs};
|
.index = 0, .size = size, .addrs = addrs, .mods = mods};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (addrs && !mods)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
ret = kallsyms_on_each_symbol(filter_match_callback, &match);
|
ret = kallsyms_on_each_symbol(filter_match_callback, &match);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
ret = module_kallsyms_on_each_symbol(NULL, filter_match_callback, &match);
|
if (IS_ENABLED(CONFIG_MODULES)) {
|
||||||
if (ret < 0)
|
ret = module_kallsyms_on_each_symbol(NULL, filter_match_callback, &match);
|
||||||
return ret;
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return match.index ?: -ENOENT;
|
return match.index ?: -ENOENT;
|
||||||
}
|
}
|
||||||
@@ -543,24 +561,35 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num)
|
|||||||
*/
|
*/
|
||||||
int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
|
int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
|
||||||
{
|
{
|
||||||
unsigned long *addrs;
|
unsigned long *addrs __free(kfree) = NULL;
|
||||||
int ret;
|
struct module **mods __free(kfree) = NULL;
|
||||||
|
int ret, num;
|
||||||
|
|
||||||
if (!fp || !filter)
|
if (!fp || !filter)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = ip_list_from_filter(filter, notfilter, NULL, FPROBE_IPS_MAX);
|
num = get_ips_from_filter(filter, notfilter, NULL, NULL, FPROBE_IPS_MAX);
|
||||||
|
if (num < 0)
|
||||||
|
return num;
|
||||||
|
|
||||||
|
addrs = kcalloc(num, sizeof(*addrs), GFP_KERNEL);
|
||||||
|
if (!addrs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mods = kcalloc(num, sizeof(*mods), GFP_KERNEL);
|
||||||
|
if (!mods)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = get_ips_from_filter(filter, notfilter, addrs, mods, num);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
addrs = kcalloc(ret, sizeof(unsigned long), GFP_KERNEL);
|
ret = register_fprobe_ips(fp, addrs, ret);
|
||||||
if (!addrs)
|
|
||||||
return -ENOMEM;
|
|
||||||
ret = ip_list_from_filter(filter, notfilter, addrs, ret);
|
|
||||||
if (ret > 0)
|
|
||||||
ret = register_fprobe_ips(fp, addrs, ret);
|
|
||||||
|
|
||||||
kfree(addrs);
|
for (int i = 0; i < num; i++) {
|
||||||
|
if (mods[i])
|
||||||
|
module_put(mods[i]);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(register_fprobe);
|
EXPORT_SYMBOL_GPL(register_fprobe);
|
||||||
|
|||||||
Reference in New Issue
Block a user