All of lore.kernel.org
 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: 7+ 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-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]

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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.