From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:06
After posting my first RFC for "module: avoid userspace pressure on unwanted
allocations" [0] I ended up doing much more cleanup on the module loading path.
One of the things that became evident while ensuring we do *less* work before
kmalloc all the things we need for the final module is we are doing a lot of
work before we even add a module onto our linked list, once its accepted for
loading and running init. We even *taint* the kernel even before we accept
a module. We also do some tainting after kernel loading.
This converges both to one point -- right as soon as we accept module
into our linked list. That is, the module is valid as per our kernel
config and we're ready to go. Most of this is just tidying code up. The
biggest functional changes is under the patch "converge taint work together".
I'll post the other functional changes in two other patch sets. This is
mostly cleanup, the next one is the new ELF checks / sanity / cleanup,
and I'm waiting to hear back from David Hildenbrand on the worthiness of
some clutches for allocation. That last part would go in the last patch
series.
In this series I've dropped completely the idea of using aliasing since
different modules can share the same alias, so using that to check if
a module is already loaded turns out not to be useful in any way.
[0] https://lkml.kernel.org/r/20230311051712.4095040-1-mcgrof@kernel.org
Luis Chamberlain (12):
module: move get_modinfo() helpers all above
module: rename next_string() to module_next_tag_pair()
module: add a for_each_modinfo_entry()
module: move early sanity checks into a helper
module: move check_modinfo() early to early_mod_check()
module: rename set_license() to module_license_taint_check()
module: split taint work out of check_modinfo_livepatch()
module: split taint adding with info checking
module: move tainting until after a module hits our linked list
module: move signature taint to module_augment_kernel_taints()
module: converge taint work together
module: rename check_module_license_and_versions() to
check_export_symbol_versions()
kernel/module/internal.h | 5 +
kernel/module/main.c | 292 ++++++++++++++++++++-------------------
2 files changed, 158 insertions(+), 139 deletions(-)
--
2.39.1
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:27:56
This moves check_modinfo() to early_mod_check(). This
doesn't make any functional changes either, as check_modinfo()
was the first call on layout_and_allocate(), so we're just
moving it back one routine and at the end.
This let's us keep separate the checkers from the allocator.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
@@ -2273,10 +2273,6 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)unsignedintndx;interr;-err=check_modinfo(info->mod,info,flags);-if(err)-returnERR_PTR(err);-/* Allow arches to frob section contents and sizes. */err=module_frob_arch_sections(info->hdr,info->sechdrs,info->secstrings,info->mod);
@@ -2688,7 +2684,11 @@ static int early_mod_check(struct load_info *info, int flags)/* Check module struct version now, before we try to use module. */if(!check_modstruct_version(info,info->mod))-returnENOEXEC;+return-ENOEXEC;++err=check_modinfo(info->mod,info,flags);+if(err)+returnerr;return0;}
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:27:59
Converge on a compromise: so long as we have a module hit our linked
list of modules we taint. That is, the module was about to become live.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 52 ++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 28 deletions(-)
@@ -1955,6 +1955,8 @@ static int setup_load_info(struct load_info *info, int flags)*Thesecallstaintthekerneldependingcertainmodulecircumstances*/staticvoidmodule_augment_kernel_taints(structmodule*mod,structload_info*info){+intprev_taint=test_taint(TAINT_PROPRIETARY_MODULE);+if(!get_modinfo(info,"intree")){if(!test_taint(TAINT_OOT_MODULE))pr_warn("%s: loading out-of-tree module taints kernel.\n",
@@ -1993,6 +1995,28 @@ static void module_augment_kernel_taints(struct module *mod, struct load_info *iadd_taint_module(mod,TAINT_UNSIGNED_MODULE,LOCKDEP_STILL_OK);}#endif++/*+*ndiswrapperisunderGPLbyitself,butloadsproprietarymodules.+*Don'tuseadd_taint_module(),asitwouldpreventndiswrapperfrom+*usingGPL-onlysymbolsitneeds.+*/+if(strcmp(mod->name,"ndiswrapper")==0)+add_taint(TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);++/* driverloader was caught wrongly pretending to be under GPL */+if(strcmp(mod->name,"driverloader")==0)+add_taint_module(mod,TAINT_PROPRIETARY_MODULE,+LOCKDEP_NOW_UNRELIABLE);++/* lve claims to be GPL but upstream won't provide source */+if(strcmp(mod->name,"lve")==0)+add_taint_module(mod,TAINT_PROPRIETARY_MODULE,+LOCKDEP_NOW_UNRELIABLE);++if(!prev_taint&&test_taint(TAINT_PROPRIETARY_MODULE))+pr_warn("%s: module license taints kernel.\n",mod->name);+}staticintcheck_modinfo(structmodule*mod,structload_info*info,intflags)
@@ -2198,29 +2222,6 @@ static int move_module(struct module *mod, struct load_info *info)staticintcheck_module_license_and_versions(structmodule*mod){-intprev_taint=test_taint(TAINT_PROPRIETARY_MODULE);--/*-*ndiswrapperisunderGPLbyitself,butloadsproprietarymodules.-*Don'tuseadd_taint_module(),asitwouldpreventndiswrapperfrom-*usingGPL-onlysymbolsitneeds.-*/-if(strcmp(mod->name,"ndiswrapper")==0)-add_taint(TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);--/* driverloader was caught wrongly pretending to be under GPL */-if(strcmp(mod->name,"driverloader")==0)-add_taint_module(mod,TAINT_PROPRIETARY_MODULE,-LOCKDEP_NOW_UNRELIABLE);--/* lve claims to be GPL but upstream won't provide source */-if(strcmp(mod->name,"lve")==0)-add_taint_module(mod,TAINT_PROPRIETARY_MODULE,-LOCKDEP_NOW_UNRELIABLE);--if(!prev_taint&&test_taint(TAINT_PROPRIETARY_MODULE))-pr_warn("%s: module license taints kernel.\n",mod->name);-#ifdef CONFIG_MODVERSIONSif((mod->num_syms&&!mod->crcs)||(mod->num_gpl_syms&&!mod->gpl_crcs)){
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:03
Just move the signature taint into the helper:
module_augment_kernel_taints()
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:10
Add a for_each_modinfo_entry() to make it easier to read and use.
This produces no functional changes but makes this code easiert
to read as we are used to with loops in the kernel and trims more
lines of code.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/internal.h | 3 +++
kernel/module/main.c | 5 +----
2 files changed, 4 insertions(+), 4 deletions(-)
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:14
Move early sanity checkers for the module into a helper.
This let's us make it clear when we are working with the
local copy of the module prior to allocation.
This produces no functional changes, it just makes subsequent
changes easier to read.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 43 ++++++++++++++++++++++++++-----------------
1 file changed, 26 insertions(+), 17 deletions(-)
@@ -2668,6 +2668,31 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname,return0;}+/* Module within temporary copy, this doesn't do any allocation */+staticintearly_mod_check(structload_info*info,intflags)+{+interr;++/*+*Nowthatweknowwehavethecorrectmodulename,check+*ifit'sblacklisted.+*/+if(blacklisted(info->name)){+pr_err("Module %s is blacklisted\n",info->name);+return-EPERM;+}++err=rewrite_section_headers(info,flags);+if(err)+returnerr;++/* Check module struct version now, before we try to use module. */+if(!check_modstruct_version(info,info->mod))+returnENOEXEC;++return0;+}+/**Allocateandloadthemodule:notethatsizeofsection0isalways*zero,andwerelyonthisforoptionalsections.
@@ -2711,26 +2736,10 @@ static int load_module(struct load_info *info, const char __user *uargs,if(err)gotofree_copy;-/*-*Nowthatweknowwehavethecorrectmodulename,check-*ifit'sblacklisted.-*/-if(blacklisted(info->name)){-err=-EPERM;-pr_err("Module %s is blacklisted\n",info->name);-gotofree_copy;-}--err=rewrite_section_headers(info,flags);+err=early_mod_check(info,flags);if(err)gotofree_copy;-/* Check module struct version now, before we try to use module. */-if(!check_modstruct_version(info,info->mod)){-err=-ENOEXEC;-gotofree_copy;-}-/* Figure out module layout, and allocate all the memory. */mod=layout_and_allocate(info,flags);if(IS_ERR(mod)){
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:19
Instead of forward declaring routines for get_modinfo() just move
everything up. This makes no functional changes.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 100 +++++++++++++++++++++----------------------
1 file changed, 48 insertions(+), 52 deletions(-)
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:22
The work to taint the kernel due to a module should be split
up eventually. To aid with this, split up the tainting on
check_modinfo_livepatch().
This let's us bring more early checks together which do return
a value, and makes changes easier to read later where we stuff
all the work to do the taints in one single routine.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
@@ -1808,12 +1808,8 @@ static int check_modinfo_livepatch(struct module *mod, struct load_info *info)/* Nothing more to do */return0;-if(set_livepatch_module(mod)){-add_taint_module(mod,TAINT_LIVEPATCH,LOCKDEP_STILL_OK);-pr_notice_once("%s: tainting kernel with TAINT_LIVEPATCH\n",-mod->name);+if(set_livepatch_module(mod))return0;-}pr_err("%s: module is marked as livepatch module, but livepatch support is disabled",mod->name);
@@ -1993,6 +1989,11 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)if(err)returnerr;+if(is_livepatch_module(mod)){+add_taint_module(mod,TAINT_LIVEPATCH,LOCKDEP_STILL_OK);+pr_notice_once("%s: tainting kernel with TAINT_LIVEPATCH\n",+mod->name);+}module_license_taint_check(mod,get_modinfo(info,"license"));if(get_modinfo(info,"test")){
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:27
It is silly to have taints spread out all over, we can just compromise
and add them if the module ever hit our linked list. Our sanity checkers
should just prevent crappy drivers / bogus ELF modules / etc and kconfig
options should be enough to let you *not* load things you don't want.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:31
This makes the routine easier to understand what the check its checking for.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:35
The set_license() routine would seem to a reader to do some sort of
setting, but it does not. It just adds a taint if the license is
not set or proprietary.
This makes what the code is doing clearer, so much we can remove
the comment about it.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
@@ -1993,8 +1993,7 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)if(err)returnerr;-/* Set up license info based on the info section */-set_license(mod,get_modinfo(info,"license"));+module_license_taint_check(mod,get_modinfo(info,"license"));if(get_modinfo(info,"test")){if(!test_taint(TAINT_TEST))
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:40
check_modinfo() actually does two things:
a) sanity checks, some of which are fatal, and so we
prevent the user from completing trying to load a module
b) taints the kernel
The taints are pretty heavy handed because we're tainting the kernel
*before* we ever even get to load the module into the modules linked
list. That is, it it can fail for other reasons later as we review the
module's structure.
But this commit makes no functional changes, it just makes the intent
clearer and splits the code up where needed to make that happen.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 62 ++++++++++++++++++++++++++++----------------
1 file changed, 40 insertions(+), 22 deletions(-)
@@ -1951,25 +1951,10 @@ static int setup_load_info(struct load_info *info, int flags)return0;}-staticintcheck_modinfo(structmodule*mod,structload_info*info,intflags)+/*+*Thesecallstaintthekerneldependingcertainmodulecircumstances*/+staticvoidmodule_augment_kernel_taints(structmodule*mod,structload_info*info){-constchar*modmagic=get_modinfo(info,"vermagic");-interr;--if(flags&MODULE_INIT_IGNORE_VERMAGIC)-modmagic=NULL;--/* This is allowed: modprobe --force will invalidate it. */-if(!modmagic){-err=try_to_force_load(mod,"bad vermagic");-if(err)-returnerr;-}elseif(!same_magic(modmagic,vermagic,info->index.vers)){-pr_err("%s: version magic '%s' should be '%s'\n",-info->name,modmagic,vermagic);-return-ENOEXEC;-}-if(!get_modinfo(info,"intree")){if(!test_taint(TAINT_OOT_MODULE))pr_warn("%s: loading out-of-tree module taints kernel.\n",
@@ -1985,15 +1970,12 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)"is unknown, you have been warned.\n",mod->name);}-err=check_modinfo_livepatch(mod,info);-if(err)-returnerr;-if(is_livepatch_module(mod)){add_taint_module(mod,TAINT_LIVEPATCH,LOCKDEP_STILL_OK);pr_notice_once("%s: tainting kernel with TAINT_LIVEPATCH\n",mod->name);}+module_license_taint_check(mod,get_modinfo(info,"license"));if(get_modinfo(info,"test")){
@@ -2002,6 +1984,42 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)mod->name);add_taint_module(mod,TAINT_TEST,LOCKDEP_STILL_OK);}+}++staticintcheck_modinfo(structmodule*mod,structload_info*info,intflags)+{+constchar*modmagic=get_modinfo(info,"vermagic");+interr;++if(flags&MODULE_INIT_IGNORE_VERMAGIC)+modmagic=NULL;++/* This is allowed: modprobe --force will invalidate it. */+if(!modmagic){+err=try_to_force_load(mod,"bad vermagic");+if(err)+returnerr;+}elseif(!same_magic(modmagic,vermagic,info->index.vers)){+pr_err("%s: version magic '%s' should be '%s'\n",+info->name,modmagic,vermagic);+return-ENOEXEC;+}++err=check_modinfo_livepatch(mod,info);+if(err)+returnerr;++/*+*Wearetaintingyourkernel*even*ifyoutrytoload+*moduleswithpossibletaintsandwefailtoloadthese+*modulesforotherreasons.+*+*Wehaveadescrepancythough,seetheothertaintsfor+*signatureandthoseincheck_module_license_and_versions().+*+*Weshouldcompromiseandconverge.+*/+module_augment_kernel_taints(mod,info);return0;}
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-19 21:28:42
This makes it clearer what it is doing. While at it,
make it available to other code other than main.c.
This will be used in the subsequent patch and make
the changes easier to read.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/internal.h | 2 ++
kernel/module/main.c | 6 +++---
2 files changed, 5 insertions(+), 3 deletions(-)
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-22 23:43:29
On Sun, Mar 19, 2023 at 02:27:34PM -0700, Luis Chamberlain wrote:
After posting my first RFC for "module: avoid userspace pressure on unwanted
allocations" [0] I ended up doing much more cleanup on the module loading path.
One of the things that became evident while ensuring we do *less* work before
kmalloc all the things we need for the final module is we are doing a lot of
work before we even add a module onto our linked list, once its accepted for
loading and running init. We even *taint* the kernel even before we accept
a module. We also do some tainting after kernel loading.
This converges both to one point -- right as soon as we accept module
into our linked list. That is, the module is valid as per our kernel
config and we're ready to go. Most of this is just tidying code up. The
biggest functional changes is under the patch "converge taint work together".
I'll post the other functional changes in two other patch sets. This is
mostly cleanup, the next one is the new ELF checks / sanity / cleanup,
and I'm waiting to hear back from David Hildenbrand on the worthiness of
some clutches for allocation. That last part would go in the last patch
series.
In this series I've dropped completely the idea of using aliasing since
different modules can share the same alias, so using that to check if
a module is already loaded turns out not to be useful in any way.
[0] https://lkml.kernel.org/r/20230311051712.4095040-1-mcgrof@kernel.org
I've taken these into modules-next for more testing. If folks spot
issues in them though let me know and I can yank them before the merge
window.
Luis
From: Petr Pavlu <petr.pavlu@suse.com> Date: 2023-03-24 13:02:25
On 3/19/23 22:27, Luis Chamberlain wrote:
quoted hunk
Move early sanity checkers for the module into a helper.
This let's us make it clear when we are working with the
local copy of the module prior to allocation.
This produces no functional changes, it just makes subsequent
changes easier to read.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 43 ++++++++++++++++++++++++++-----------------
1 file changed, 26 insertions(+), 17 deletions(-)
@@ -2668,6 +2668,31 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname,return0;}+/* Module within temporary copy, this doesn't do any allocation */+staticintearly_mod_check(structload_info*info,intflags)+{+interr;++/*+*Nowthatweknowwehavethecorrectmodulename,check+*ifit'sblacklisted.+*/+if(blacklisted(info->name)){+pr_err("Module %s is blacklisted\n",info->name);+return-EPERM;+}++err=rewrite_section_headers(info,flags);+if(err)+returnerr;++/* Check module struct version now, before we try to use module. */+if(!check_modstruct_version(info,info->mod))+returnENOEXEC;
The error value when check_modstruct_version() fails is changed in this patch
from -ENOEXEC to ENOEXEC and updated back again in the next patch. It would be
good to avoid introducing this temporary problem and keep the value throughout
as -ENOEXEC.
quoted hunk
+
+ return 0;
+}
+
/*
* Allocate and load the module: note that size of section 0 is always
* zero, and we rely on this for optional sections.
@@ -2711,26 +2736,10 @@ static int load_module(struct load_info *info, const char __user *uargs, if (err) goto free_copy;- /*- * Now that we know we have the correct module name, check- * if it's blacklisted.- */- if (blacklisted(info->name)) {- err = -EPERM;- pr_err("Module %s is blacklisted\n", info->name);- goto free_copy;- }-- err = rewrite_section_headers(info, flags);+ err = early_mod_check(info, flags); if (err) goto free_copy;- /* Check module struct version now, before we try to use module. */- if (!check_modstruct_version(info, info->mod)) {- err = -ENOEXEC;
Original value here.
- goto free_copy;
- }
-
/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(info, flags);
if (IS_ERR(mod)) {
From: Luis Chamberlain <mcgrof@kernel.org> Date: 2023-03-24 18:34:20
On Fri, Mar 24, 2023 at 02:02:06PM +0100, Petr Pavlu wrote:
On 3/19/23 22:27, Luis Chamberlain wrote:
quoted
Move early sanity checkers for the module into a helper.
This let's us make it clear when we are working with the
local copy of the module prior to allocation.
This produces no functional changes, it just makes subsequent
changes easier to read.
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
kernel/module/main.c | 43 ++++++++++++++++++++++++++-----------------
1 file changed, 26 insertions(+), 17 deletions(-)
@@ -2668,6 +2668,31 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname,return0;}+/* Module within temporary copy, this doesn't do any allocation */+staticintearly_mod_check(structload_info*info,intflags)+{+interr;++/*+*Nowthatweknowwehavethecorrectmodulename,check+*ifit'sblacklisted.+*/+if(blacklisted(info->name)){+pr_err("Module %s is blacklisted\n",info->name);+return-EPERM;+}++err=rewrite_section_headers(info,flags);+if(err)+returnerr;++/* Check module struct version now, before we try to use module. */+if(!check_modstruct_version(info,info->mod))+returnENOEXEC;
The error value when check_modstruct_version() fails is changed in this patch
from -ENOEXEC to ENOEXEC and updated back again in the next patch. It would be
good to avoid introducing this temporary problem and keep the value throughout
as -ENOEXEC.