Re: [PATCH v10 4/6] module: allocate codetag sections before the regular module layout

From: Suren Baghdasaryan

Date: Thu Sep 17 2026 - 20:06:09 EST


On Mon, Sep 14, 2026 at 11:59 PM Hao Ge <hao.ge@xxxxxxxxx> wrote:
>
> 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. The reservation is released and
> module_tags.size rolled back, so a concurrent load which already
> passed needs_section_mem() does not skip vm_module_tags_populate().
>
> An SHT_NOBITS codetag section is zeroed explicitly, the tag area
> pages are not zeroed on allocation.
>
> 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().
>
> Based on a patch by Petr Pavlu [1].
>
> Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
> Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
> Link: https://lore.kernel.org/all/499bb60c-c6e3-43a3-bd92-95a0567ece5e@xxxxxxxx/ [1]
> Reviewed-by: Petr Pavlu <petr.pavlu@xxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Hao Ge <hao.ge@xxxxxxxxx>

Makes the logic much simpler. Thanks!

Reviewed-by: Suren Baghdasaryan <surenb@xxxxxxxxxx>

> ---
> kernel/module/main.c | 101 +++++++++++++++++++++++--------------------
> mm/alloc_tag.c | 9 ++--
> 2 files changed, 60 insertions(+), 50 deletions(-)
>
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index ae2678ac7840..b7ebcc40bdda 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -1723,20 +1723,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);
> }
> @@ -2795,7 +2781,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) {
> @@ -2815,35 +2800,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) {
> /*
> @@ -2875,8 +2838,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;
> }
> @@ -2947,6 +2908,49 @@ 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);
> + else
> + memset(dest, 0, 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;
> @@ -2979,18 +2983,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 95ddf5b743d0..5836803898ad 100644
> --- a/mm/alloc_tag.c
> +++ b/mm/alloc_tag.c
> @@ -958,10 +958,13 @@ 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);
> + module_tags.size = prev_size;
> + return ERR_PTR(-EAGAIN);
> }
>
> grow_res = vm_module_tags_populate();
> --
> 2.25.1
>