Since kernel modules has their own .rodata sections, functions like 'kstrdup_const()' called from the module context are not required to copy string constants from these sections. Likewise, 'kfree_const()' in such a context becomes a no-op also because the whole module's .rodata is freed at module unloading. OTOH this proof-of-concept implementation introduces substantial overhead due to calls to '__module_address()' from 'is_module_rodata()', which is much slower than 'is_kernel_rodata()'. Anyway, comments are highly appreciated.
Signed-off-by: Dmitry Antipov <[email protected]> --- include/linux/module.h | 6 ++++++ kernel/module/main.c | 9 +++++++++ mm/util.c | 7 +++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/linux/module.h b/include/linux/module.h index 14f391b186c6..7edaf2b730ce 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -623,6 +623,7 @@ bool is_module_address(unsigned long addr); bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr); bool is_module_percpu_address(unsigned long addr); bool is_module_text_address(unsigned long addr); +bool is_module_rodata(unsigned long addr); static inline bool within_module_mem_type(unsigned long addr, const struct module *mod, @@ -807,6 +808,11 @@ static inline bool is_module_text_address(unsigned long addr) return false; } +static inline bool is_module_rodata(unsigned long addr) +{ + return false; +} + static inline bool within_module_core(unsigned long addr, const struct module *mod) { diff --git a/kernel/module/main.c b/kernel/module/main.c index c3ce106c70af..5003cacd0786 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3858,6 +3858,15 @@ bool is_module_text_address(unsigned long addr) return __module_text_address(addr) != NULL; } +bool is_module_rodata(unsigned long addr) +{ + struct module *mod; + + guard(rcu)(); + mod = __module_address(addr); + return mod && within_module_mem_type(addr, mod, MOD_RODATA); +} + void module_for_each_mod(int(*func)(struct module *mod, void *data), void *data) { struct module *mod; diff --git a/mm/util.c b/mm/util.c index b05ab6f97e11..8dd1f1e95554 100644 --- a/mm/util.c +++ b/mm/util.c @@ -26,6 +26,7 @@ #include <linux/compat.h> #include <linux/fsnotify.h> #include <linux/page_idle.h> +#include <linux/module.h> #include <linux/uaccess.h> @@ -42,7 +43,8 @@ */ void kfree_const(const void *x) { - if (!is_kernel_rodata((unsigned long)x)) + if (!is_kernel_rodata((unsigned long)x) && + !is_module_rodata((unsigned long)x)) kfree(x); } EXPORT_SYMBOL(kfree_const); @@ -98,7 +100,8 @@ EXPORT_SYMBOL(kstrdup); */ const char *kstrdup_const(const char *s, gfp_t gfp) { - if (is_kernel_rodata((unsigned long)s)) + if (is_kernel_rodata((unsigned long)s) || + is_module_rodata((unsigned long)s)) return s; return kstrdup(s, gfp); -- 2.53.0
