Linux Modules
 help / color / mirror / Atom feed
From: Hao Ge <hao.ge@linux.dev>
To: Andrew Morton <akpm@linux-foundation.org>,
	Suren Baghdasaryan <surenb@google.com>
Cc: linux-modules@vger.kernel.org, sashiko-reviews@lists.linux.dev
Subject: Re: [PATCH v5 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled
Date: Thu, 13 Aug 2026 17:44:02 +0800	[thread overview]
Message-ID: <7e66e13e-e4f1-4747-a125-0e9bef9204ca@linux.dev> (raw)
In-Reply-To: <f244c880-c907-456b-81ae-07c28456185e@linux.dev>



On 2026/8/12 14:32, Hao Ge wrote:
> CC Andrew and Suren
> 
> 
> On 2026/8/12 14:04, sashiko-bot@kernel.org wrote:
>> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
>>
>> New issues:
>> - [High] Unconditional allocation of per-CPU counters for modules causes a memory leak when memory profiling is disabled and the module load is retried.
>>
>> Pre-existing issues:
>> - [Critical] Concurrent module loading can cause an out-of-bounds memory write and module corruption because the global profiling flag is toggled without cross-phase synchronization.
>> - [High] A race condition on the global module tags size variable can lead to unmapped virtual memory access and a kernel panic.
>> - [High] If page population fails, a dangling module pointer is left in the maple tree, leading to a Use-After-Free.
>> -- 
>>
>> --- Patch [2]: [PATCH v5 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled ---
>> commit ffb2b208763f32cdfb3d6cb740d4d4efa5c99ae0
>> Author: Hao Ge <hao.ge@linux.dev>
>>
>> alloc_tag: fix undetected compressed tag overflow when profiling is disabled
>>
>> This patch modifies module layout and allocation to retry loading without
>> memory profiling if the compressed tag limit is exceeded. It removes the
>> profiling enabled guard when checking for tag overflow and handles the
>> fallback by resetting section flags and repeating the layout process.
>>
>>> diff --git a/kernel/module/main.c b/kernel/module/main.c
>>> --- a/kernel/module/main.c
>>> +++ b/kernel/module/main.c
>>
>> [ ... ]
>>
>>> @@ -2966,18 +2967,38 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
>>>        */
>>>       module_mark_ro_after_init(info->hdr, info->sechdrs, info->secstrings);
>>>   +    /*
>>> +     * Save the sizes reserved by module_frob_arch_sections() so they can
>>> +     * be restored if we retry below.
>>> +     */
>>> +    for_each_mod_mem_type(type)
>>> +        frob_size[type] = info->mod->mem[type].size;
>>> +
>>>       /*
>>>        * 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.
>>>        */
>>> +retry:
>>>       layout_sections(info->mod, info);
>>>       layout_symtab(info->mod, info);
>>>         /* Allocate and move to the final place */
>>>       err = move_module(info->mod, info);
>>
>> [Severity: Critical]
>> This isn't a bug introduced by this patch, but can concurrent module loading
>> cause memory corruption if profiling is disabled mid-flight?
>>
>> In move_module(), if one thread disables profiling globally via
>> shutdown_mem_profiling() while another thread is halfway through module
>> loading (between layout calculation and memory movement), the second thread's
>> section offset queries could evaluate to false.
>>
>> This would cause the module loader to use an un-incremented 0 offset and
>> incorrectly write the codetag section into the base memory, overwriting the
>> start of the module's core memory block.
>>
>>> -    if (err)
>>> -        return ERR_PTR(err);
>>> +    if (err) {
>>> +        if (err != -EAGAIN)
>>> +            return ERR_PTR(err);
>>> +        /*
>>> +         * -EAGAIN means profiling was disabled but the module
>>> +         * can still load without it. Reset state and retry.
>>> +         */
>>> +        rewrite_section_headers(info, flags);
>>> +        for_each_mod_mem_type(type)
>>> +            info->mod->mem[type].size = frob_size[type];
>>> +        info->sechdrs[info->index.sym].sh_flags &= ~(unsigned long)SHF_ALLOC;
>>> +        info->sechdrs[info->index.str].sh_flags &= ~(unsigned long)SHF_ALLOC;
>>> +        goto retry;
>>
>> [Severity: High]
>> Does this retry mechanism cause a memory leak for per-CPU counters?
>>
>> When the module load is retried via this new loop, the codetag section is
>> treated as standard memory and bypasses maple tree registration. However, the
>> subsequent codetag module initialization unconditionally allocates per-CPU
>> memory for these tags regardless of the profiling state.
>>
>> During module unload in release_module_tags(), the cleanup path exits early
>> because the module cannot be found in the maple tree, permanently leaking
>> the per-CPU memory on every unload.
> 
> The per-CPU counter leak is pre-existing, not introduced by the retry
> 
>                                                      mechanism.
> 
> The root cause is that shutdown_mem_profiling() permanently sets
> 
> 
> mem_profiling_support to false, after which needs_section_mem()
> 
>                                                        returns false for every subsequent module. Their codetag sections are
> 
>                                         placed as regular data, so they are never inserted into the alloc_tag
> 
>                       maple tree. Yet codetag_load_module() still unconditionally invokes
> 
>          the load_module() callback, which allocates percpu counters that
> 
> release_module_tags() cannot find on unload.
> 
> I'll send out a separate patch to fix this leak later.
> 
> 

Sorry for the messy indentation — it's down to my mail client's formatting config.
A colleague pointed out the official recommended indent configs for kernel mail clients,
which I'd missed entirely.
https://elixir.bootlin.com/linux/v7.2-rc7/source/Documentation/process/email-clients.rst
Sorry for all the hassle I've caused everyone earlier.
The patch fixing the per-CPU counter leak has been sent out:
https://lore.kernel.org/all/20260813093421.135230-2-hao.ge@linux.dev/

Thanks
Best Regards
Hao

> 
>>
>>> +    }
>>>         /* 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
>>> --- a/mm/alloc_tag.c
>>> +++ b/mm/alloc_tag.c
>>
>> [ ... ]
>>
>>> @@ -950,10 +950,12 @@ static void *reserve_module_tags(struct module *mod, unsigned long size,
>>>           int grow_res;
>>>             module_tags.size = offset + size;
>>
>> [Severity: High]
>> This is a pre-existing issue, but does the update to module_tags.size race
>> with concurrent module loads?
>>
>> The high-water mark for the module tags size is updated here without a lock
>> after the maple tree lock is released. If two concurrent module loads race on
>> this update, a higher offset might be overwritten by a smaller one.
>>
>> Subsequently, vm_module_tags_populate() relies on this corrupted size and
>> would fail to map the physical pages for the higher virtual addresses, leading
>> to a page fault when the module accesses its allocated tags.
>>
>>> -        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();
>>
>> [Severity: High]
>> This is a pre-existing issue, but could this lead to a dangling pointer in
>> the maple tree if page population fails?
>>
>> If vm_module_tags_populate() fails and returns an error, the allocation
>> routine returns an error here but doesn't remove the newly inserted module
>> pointer from the maple tree.
>>
>> Since this error bypasses the standard codetag cleanup routines in the module
>> loader (where codetag_section_found might remain false in move_module()),
>> the maple tree retains a pointer to the aborted module. Once the module
>> structure is freed, future maple tree iterations could dereference this freed
>> pointer.
>>

  reply	other threads:[~2026-08-13  9:43 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-12  5:41 [PATCH v5 0/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled Hao Ge
2026-08-12  5:41 ` [PATCH v5 1/2] alloc_tag: move release_module_tags() above reserve_module_tags() Hao Ge
2026-08-12  6:03   ` sashiko-bot
2026-08-15  3:40   ` Suren Baghdasaryan
2026-08-12  5:41 ` [PATCH v5 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled Hao Ge
2026-08-12  6:04   ` sashiko-bot
2026-08-12  6:32     ` Hao Ge
2026-08-13  9:44       ` Hao Ge [this message]
2026-08-15  5:58   ` Suren Baghdasaryan
2026-08-15 10:45     ` Petr Pavlu
2026-08-16 15:16       ` Suren Baghdasaryan
2026-08-17  2:24         ` Hao Ge

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7e66e13e-e4f1-4747-a125-0e9bef9204ca@linux.dev \
    --to=hao.ge@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=surenb@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox