module: Fix freeing of charp module parameters when CONFIG_SYSFS=n

When setting a charp module parameter, the param_set_charp() function
allocates memory to store a copy of the input value. Later, when the module
is potentially unloaded, the destroy_params() function is called to free
this allocated memory.

However, destroy_params() is available only when CONFIG_SYSFS=y, otherwise
only a dummy variant is present. In the unlikely case that the kernel is
configured with CONFIG_MODULES=y and CONFIG_SYSFS=n, this results in
a memory leak of charp values when a module is unloaded.

Fix this issue by making destroy_params() always available when
CONFIG_MODULES=y. Rename the function to module_destroy_params() to clarify
that it is intended for use by the module loader.

Fixes: e180a6b775 ("param: fix charp parameters set via sysfs")
Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
This commit is contained in:
Petr Pavlu 2026-03-13 14:48:02 +01:00 committed by Sami Tolvanen
parent 4afc71bba8
commit deffe1edba
3 changed files with 23 additions and 19 deletions

View File

@ -426,14 +426,9 @@ extern char *parse_args(const char *name,
void *arg, parse_unknown_fn unknown);
/* Called by module remove. */
#ifdef CONFIG_SYSFS
extern void destroy_params(const struct kernel_param *params, unsigned num);
#else
static inline void destroy_params(const struct kernel_param *params,
unsigned num)
{
}
#endif /* !CONFIG_SYSFS */
#ifdef CONFIG_MODULES
void module_destroy_params(const struct kernel_param *params, unsigned int num);
#endif
/* All the helper functions */
/* The macros to do compile-time type checking stolen from Jakub

View File

@ -1408,7 +1408,7 @@ static void free_module(struct module *mod)
module_unload_free(mod);
/* Free any allocated parameters. */
destroy_params(mod->kp, mod->num_kp);
module_destroy_params(mod->kp, mod->num_kp);
if (is_livepatch_module(mod))
free_module_elf(mod);
@ -3519,7 +3519,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
mod_sysfs_teardown(mod);
coming_cleanup:
mod->state = MODULE_STATE_GOING;
destroy_params(mod->kp, mod->num_kp);
module_destroy_params(mod->kp, mod->num_kp);
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod);
klp_module_going(mod);

View File

@ -745,15 +745,6 @@ void module_param_sysfs_remove(struct module *mod)
}
#endif
void destroy_params(const struct kernel_param *params, unsigned num)
{
unsigned int i;
for (i = 0; i < num; i++)
if (params[i].ops->free)
params[i].ops->free(params[i].arg);
}
struct module_kobject * __init_or_module
lookup_or_create_module_kobject(const char *name)
{
@ -985,3 +976,21 @@ static int __init param_sysfs_builtin_init(void)
late_initcall(param_sysfs_builtin_init);
#endif /* CONFIG_SYSFS */
#ifdef CONFIG_MODULES
/*
* module_destroy_params - free all parameters for one module
* @params: module parameters (array)
* @num: number of module parameters
*/
void module_destroy_params(const struct kernel_param *params, unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++)
if (params[i].ops->free)
params[i].ops->free(params[i].arg);
}
#endif /* CONFIG_MODULES */