Whether a codetag section goes to the codetag region is decided by
layout_sections() and asked again in move_module(). A concurrent
load can shut profiling down in between, and move_module() then
copies the section to offset 0 of its regular destination,
overwriting whatever is there.

Decide and allocate in one pass, before the layout. Allocation
errors fail the load. On a tag area overflow profiling is already
disabled, so -EAGAIN makes the section fall back to regular module
data and the module still loads.

When profiling was toggled off the overflow check did not run, a
module could load with more tags than the page flags can address,
and re-enabling profiling then silently corrupted /proc/allocinfo.
The check no longer depends on mem_alloc_profiling_enabled().

Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
Reported-by: Sashiko <[email protected]>
Based-on-a-patch-by: Petr Pavlu <[email protected]>
Link: https://lore.kernel.org/all/[email protected]/
Cc: [email protected]
Signed-off-by: Hao Ge <[email protected]>
---
 kernel/module/main.c | 99 +++++++++++++++++++++++---------------------
 mm/alloc_tag.c       |  8 ++--
 2 files changed, 57 insertions(+), 50 deletions(-)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index 078dae188aba..d0a8ac9b3ee5 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1717,20 +1717,6 @@ static void __layout_sections(struct module *mod, struct 
load_info *info, bool i
                        if (WARN_ON_ONCE(type == MOD_INVALID))
                                continue;
 
-                       /*
-                        * Do not allocate codetag memory as we load it into
-                        * preallocated contiguous memory.
-                        */
-                       if (codetag_needs_module_section(mod, sname, 
s->sh_size)) {
-                               /*
-                                * s->sh_entsize won't be used but populate the
-                                * type field to avoid confusion.
-                                */
-                               s->sh_entsize = ((unsigned long)(type) & 
SH_ENTSIZE_TYPE_MASK)
-                                               << SH_ENTSIZE_TYPE_SHIFT;
-                               continue;
-                       }
-
                        s->sh_entsize = module_get_offset_and_type(mod, type, 
s, i);
                        pr_debug("\t%s\n", sname);
                }
@@ -2777,7 +2763,6 @@ static int move_module(struct module *mod, struct 
load_info *info)
 {
        int i, ret;
        enum mod_mem_type t = MOD_MEM_NUM_TYPES;
-       bool codetag_section_found = false;
 
        for_each_mod_mem_type(type) {
                if (!mod->mem[type].size) {
@@ -2797,35 +2782,13 @@ static int move_module(struct module *mod, struct 
load_info *info)
        for (i = 0; i < info->hdr->e_shnum; i++) {
                void *dest;
                Elf_Shdr *shdr = &info->sechdrs[i];
-               const char *sname;
 
                if (!(shdr->sh_flags & SHF_ALLOC)
                    || shdr->sh_entsize == SH_ENTSIZE_STANDALONE)
                        continue;
 
-               sname = info->secstrings + shdr->sh_name;
-               /*
-                * Load codetag sections separately as they might still be used
-                * after module unload.
-                */
-               if (codetag_needs_module_section(mod, sname, shdr->sh_size)) {
-                       dest = codetag_alloc_module_section(mod, sname, 
shdr->sh_size,
-                                       arch_mod_section_prepend(mod, i), 
shdr->sh_addralign);
-                       if (WARN_ON(!dest)) {
-                               ret = -EINVAL;
-                               goto out_err;
-                       }
-                       if (IS_ERR(dest)) {
-                               ret = PTR_ERR(dest);
-                               goto out_err;
-                       }
-                       codetag_section_found = true;
-               } else {
-                       enum mod_mem_type type = shdr->sh_entsize >> 
SH_ENTSIZE_TYPE_SHIFT;
-                       unsigned long offset = shdr->sh_entsize & 
SH_ENTSIZE_OFFSET_MASK;
-
-                       dest = mod->mem[type].base + offset;
-               }
+               dest = mod->mem[shdr->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT].base 
+
+                      (shdr->sh_entsize & SH_ENTSIZE_OFFSET_MASK);
 
                if (shdr->sh_type != SHT_NOBITS) {
                        /*
@@ -2857,8 +2820,6 @@ static int move_module(struct module *mod, struct 
load_info *info)
        module_memory_restore_rox(mod);
        while (t--)
                module_memory_free(mod, t);
-       if (codetag_section_found)
-               codetag_free_module_sections(mod);
 
        return ret;
 }
@@ -2929,6 +2890,47 @@ static bool blacklisted(const char *module_name)
 }
 core_param(module_blacklist, module_blacklist, charp, 0400);
 
+/*
+ * Allocate codetag sections separately. They are loaded into preallocated
+ * contiguous memory because they may still be used after the module is
+ * unloaded.
+ *
+ * If the separate allocation overflows, allocate the section normally
+ * so that the module can still be loaded.
+ */
+static int allocate_codetag_sections(struct load_info *info)
+{
+       for (unsigned int i = 1; i < info->hdr->e_shnum; i++) {
+               Elf_Shdr *shdr = &info->sechdrs[i];
+               const char *sname = info->secstrings + shdr->sh_name;
+               void *dest;
+
+               if (!codetag_needs_module_section(info->mod, sname, 
shdr->sh_size))
+                       continue;
+
+               dest = codetag_alloc_module_section(info->mod, sname, 
shdr->sh_size,
+                               arch_mod_section_prepend(info->mod, i), 
shdr->sh_addralign);
+               if (WARN_ON(!dest)) {
+                       codetag_free_module_sections(info->mod);
+                       return -EINVAL;
+               }
+               if (dest == ERR_PTR(-EAGAIN))
+                       /* Allocate the section as a regular section. */
+                       continue;
+               if (IS_ERR(dest)) {
+                       codetag_free_module_sections(info->mod);
+                       return PTR_ERR(dest);
+               }
+
+               if (shdr->sh_type != SHT_NOBITS)
+                       memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
+               shdr->sh_addr = (unsigned long)dest;
+               shdr->sh_entsize = SH_ENTSIZE_STANDALONE;
+       }
+
+       return 0;
+}
+
 static struct module *layout_and_allocate(struct load_info *info, int flags)
 {
        struct module *mod;
@@ -2961,18 +2963,21 @@ static struct module *layout_and_allocate(struct 
load_info *info, int flags)
         */
        module_mark_ro_after_init(info->hdr, info->sechdrs, info->secstrings);
 
-       /*
-        * Determine total sizes, and put offsets in sh_entsize.  For now
-        * this is done generically; there doesn't appear to be any
-        * special cases for the architectures.
-        */
+       /* Allow codetag sections to be allocated separately first. */
+       err = allocate_codetag_sections(info);
+       if (err)
+               return ERR_PTR(err);
+
+       /* Determine total sizes and put offsets in sh_entsize. */
        layout_sections(info->mod, info);
        layout_symtab(info->mod, info);
 
        /* Allocate and move to the final place */
        err = move_module(info->mod, info);
-       if (err)
+       if (err) {
+               codetag_free_module_sections(info->mod);
                return ERR_PTR(err);
+       }
 
        /* Module has been copied to its final place now: return it. */
        mod = (void *)info->sechdrs[info->index.mod].sh_addr;
diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
index e7a79116ad81..248496470904 100644
--- a/mm/alloc_tag.c
+++ b/mm/alloc_tag.c
@@ -958,10 +958,12 @@ static void *reserve_module_tags(struct module *mod, 
unsigned long size,
                int grow_res;
 
                module_tags.size = offset + size;
-               if (mem_alloc_profiling_enabled() && !tags_addressable()) {
+               if (!tags_addressable()) {
                        shutdown_mem_profiling(true);
-                       pr_warn("With module %s there are too many tags to fit 
in %d page flag bits. Memory allocation profiling is disabled!\n",
-                               mod->name, NR_UNUSED_PAGEFLAG_BITS);
+                       pr_warn_once("With module %s there are too many tags to 
fit in %d page flag bits. Memory allocation profiling is disabled!\n",
+                                    mod->name, NR_UNUSED_PAGEFLAG_BITS);
+                       release_module_tags(mod, false);
+                       return ERR_PTR(-EAGAIN);
                }
 
                grow_res = vm_module_tags_populate();
-- 
2.25.1


Reply via email to