* [PATCH v4 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled
[not found] <20260810071518.115381-1-hao.ge@linux.dev>
@ 2026-08-10 7:15 ` Hao Ge
2026-08-11 15:49 ` Suren Baghdasaryan
0 siblings, 1 reply; 4+ messages in thread
From: Hao Ge @ 2026-08-10 7:15 UTC (permalink / raw)
To: gehao; +Cc: Hao Ge, stable, Suren Baghdasaryan
In reserve_module_tags(), the tag overflow check is gated on
mem_alloc_profiling_enabled():
if (mem_alloc_profiling_enabled() && !tags_addressable())
If profiling is toggled off at runtime and a module is loaded whose
tags exceed the compressed-mode limit, shutdown_mem_profiling() is
skipped. vm_module_tags_populate() still maps memory for the tags and
the module loads successfully, but the total tag count now exceeds what
NR_UNUSED_PAGEFLAG_BITS can address.
Once profiling is re-enabled, ref_to_idx() computes each tag's index
as its position in the alloc_tag array. update_page_tag_ref() masks
it to alloc_tag_ref_mask before storing in page->flags. Indices
beyond the mask are truncated and idx_to_ref() resolves them to wrong
tags.
This silently corrupts /proc/allocinfo: allocated pages get attributed
to the wrong call sites, so the statistics it reports are wrong.
mem_alloc_profiling_enabled() and mem_profiling_compressed are
independent. Once compressed mode is established at boot, it stays
active regardless of runtime toggles of mem_profiling.
Remove the mem_alloc_profiling_enabled() guard. On overflow, shut down
profiling, release the reservation, and return -EAGAIN so that
layout_and_allocate() retries with profiling disabled: codetag sections
are then placed as regular module data and the module loads without
profiling rather than being rejected entirely.
Skip percpu counter allocation in load_module() when profiling is
disabled, as those counters would never be freed on unload.
Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
Cc: stable@vger.kernel.org
Suggested-by: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Hao Ge <hao.ge@linux.dev>
---
kernel/module/main.c | 17 +++++++++++++++--
mm/alloc_tag.c | 11 ++++++++---
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..c32bb47a573a 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2971,13 +2971,26 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
* 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);
- 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 = 0;
+ info->sechdrs[info->index.sym].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ info->sechdrs[info->index.str].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ goto retry;
+ }
/* 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 af44f90379f2..3eba6331174d 100644
--- 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;
- 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();
@@ -975,6 +977,9 @@ static int load_module(struct module *mod, struct codetag *start, struct codetag
struct alloc_tag *stop_tag;
struct alloc_tag *tag;
+ if (!mem_profiling_support)
+ return 0;
+
/* percpu counters for core allocations are already statically allocated */
if (!mod)
return 0;
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v4 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled
[not found] <20260810093955.153015-1-hao.ge@linux.dev>
@ 2026-08-10 9:39 ` Hao Ge
0 siblings, 0 replies; 4+ messages in thread
From: Hao Ge @ 2026-08-10 9:39 UTC (permalink / raw)
To: Suren Baghdasaryan, Andrew Morton, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Aaron Tomlin
Cc: linux-modules, linux-kernel, linux-mm, Hao Ge, stable
In reserve_module_tags(), the tag overflow check is gated on
mem_alloc_profiling_enabled():
if (mem_alloc_profiling_enabled() && !tags_addressable())
If profiling is toggled off at runtime and a module is loaded whose
tags exceed the compressed-mode limit, shutdown_mem_profiling() is
skipped. vm_module_tags_populate() still maps memory for the tags and
the module loads successfully, but the total tag count now exceeds what
NR_UNUSED_PAGEFLAG_BITS can address.
Once profiling is re-enabled, ref_to_idx() computes each tag's index
as its position in the alloc_tag array. update_page_tag_ref() masks
it to alloc_tag_ref_mask before storing in page->flags. Indices
beyond the mask are truncated and idx_to_ref() resolves them to wrong
tags.
This silently corrupts /proc/allocinfo: allocated pages get attributed
to the wrong call sites, so the statistics it reports are wrong.
mem_alloc_profiling_enabled() and mem_profiling_compressed are
independent. Once compressed mode is established at boot, it stays
active regardless of runtime toggles of mem_profiling.
Remove the mem_alloc_profiling_enabled() guard. On overflow, shut down
profiling, release the reservation, and return -EAGAIN so that
layout_and_allocate() retries with profiling disabled: codetag sections
are then placed as regular module data and the module loads without
profiling rather than being rejected entirely.
Skip percpu counter allocation in load_module() when profiling is
disabled, as those counters would never be freed on unload.
Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
Cc: stable@vger.kernel.org
Suggested-by: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Hao Ge <hao.ge@linux.dev>
---
kernel/module/main.c | 17 +++++++++++++++--
mm/alloc_tag.c | 11 ++++++++---
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..c32bb47a573a 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2971,13 +2971,26 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
* 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);
- 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 = 0;
+ info->sechdrs[info->index.sym].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ info->sechdrs[info->index.str].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ goto retry;
+ }
/* 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 af44f90379f2..3eba6331174d 100644
--- 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;
- 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();
@@ -975,6 +977,9 @@ static int load_module(struct module *mod, struct codetag *start, struct codetag
struct alloc_tag *stop_tag;
struct alloc_tag *tag;
+ if (!mem_profiling_support)
+ return 0;
+
/* percpu counters for core allocations are already statically allocated */
if (!mod)
return 0;
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v4 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled
2026-08-10 7:15 ` Hao Ge
@ 2026-08-11 15:49 ` Suren Baghdasaryan
2026-08-12 23:36 ` Suren Baghdasaryan
0 siblings, 1 reply; 4+ messages in thread
From: Suren Baghdasaryan @ 2026-08-11 15:49 UTC (permalink / raw)
To: Hao Ge; +Cc: gehao, stable
On Mon, Aug 10, 2026 at 12:14 AM Hao Ge <hao.ge@linux.dev> wrote:
>
> In reserve_module_tags(), the tag overflow check is gated on
> mem_alloc_profiling_enabled():
>
> if (mem_alloc_profiling_enabled() && !tags_addressable())
>
> If profiling is toggled off at runtime and a module is loaded whose
> tags exceed the compressed-mode limit, shutdown_mem_profiling() is
> skipped. vm_module_tags_populate() still maps memory for the tags and
> the module loads successfully, but the total tag count now exceeds what
> NR_UNUSED_PAGEFLAG_BITS can address.
>
> Once profiling is re-enabled, ref_to_idx() computes each tag's index
> as its position in the alloc_tag array. update_page_tag_ref() masks
> it to alloc_tag_ref_mask before storing in page->flags. Indices
> beyond the mask are truncated and idx_to_ref() resolves them to wrong
> tags.
>
> This silently corrupts /proc/allocinfo: allocated pages get attributed
> to the wrong call sites, so the statistics it reports are wrong.
>
> mem_alloc_profiling_enabled() and mem_profiling_compressed are
> independent. Once compressed mode is established at boot, it stays
> active regardless of runtime toggles of mem_profiling.
>
> Remove the mem_alloc_profiling_enabled() guard. On overflow, shut down
> profiling, release the reservation, and return -EAGAIN so that
> layout_and_allocate() retries with profiling disabled: codetag sections
> are then placed as regular module data and the module loads without
> profiling rather than being rejected entirely.
>
> Skip percpu counter allocation in load_module() when profiling is
> disabled, as those counters would never be freed on unload.
The approach looks correct but we need to address Sashiko's findings.
I'll focus on the pre-existing issues and will post fixes later today
or tomorrow.
>
> Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
> Cc: stable@vger.kernel.org
> Suggested-by: Suren Baghdasaryan <surenb@google.com>
> Signed-off-by: Hao Ge <hao.ge@linux.dev>
> ---
> kernel/module/main.c | 17 +++++++++++++++--
> mm/alloc_tag.c | 11 ++++++++---
> 2 files changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 46dd8d25a605..c32bb47a573a 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -2971,13 +2971,26 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
> * 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);
> - 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 = 0;
> + info->sechdrs[info->index.sym].sh_flags &= ~(unsigned long)SHF_ALLOC;
> + info->sechdrs[info->index.str].sh_flags &= ~(unsigned long)SHF_ALLOC;
> + goto retry;
> + }
>
> /* 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 af44f90379f2..3eba6331174d 100644
> --- 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;
> - 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();
> @@ -975,6 +977,9 @@ static int load_module(struct module *mod, struct codetag *start, struct codetag
> struct alloc_tag *stop_tag;
> struct alloc_tag *tag;
>
> + if (!mem_profiling_support)
> + return 0;
> +
> /* percpu counters for core allocations are already statically allocated */
> if (!mod)
> return 0;
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v4 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled
2026-08-11 15:49 ` Suren Baghdasaryan
@ 2026-08-12 23:36 ` Suren Baghdasaryan
0 siblings, 0 replies; 4+ messages in thread
From: Suren Baghdasaryan @ 2026-08-12 23:36 UTC (permalink / raw)
To: Hao Ge; +Cc: gehao, stable
On Tue, Aug 11, 2026 at 8:49 AM Suren Baghdasaryan <surenb@google.com> wrote:
>
> On Mon, Aug 10, 2026 at 12:14 AM Hao Ge <hao.ge@linux.dev> wrote:
> >
> > In reserve_module_tags(), the tag overflow check is gated on
> > mem_alloc_profiling_enabled():
> >
> > if (mem_alloc_profiling_enabled() && !tags_addressable())
> >
> > If profiling is toggled off at runtime and a module is loaded whose
> > tags exceed the compressed-mode limit, shutdown_mem_profiling() is
> > skipped. vm_module_tags_populate() still maps memory for the tags and
> > the module loads successfully, but the total tag count now exceeds what
> > NR_UNUSED_PAGEFLAG_BITS can address.
> >
> > Once profiling is re-enabled, ref_to_idx() computes each tag's index
> > as its position in the alloc_tag array. update_page_tag_ref() masks
> > it to alloc_tag_ref_mask before storing in page->flags. Indices
> > beyond the mask are truncated and idx_to_ref() resolves them to wrong
> > tags.
> >
> > This silently corrupts /proc/allocinfo: allocated pages get attributed
> > to the wrong call sites, so the statistics it reports are wrong.
> >
> > mem_alloc_profiling_enabled() and mem_profiling_compressed are
> > independent. Once compressed mode is established at boot, it stays
> > active regardless of runtime toggles of mem_profiling.
> >
> > Remove the mem_alloc_profiling_enabled() guard. On overflow, shut down
> > profiling, release the reservation, and return -EAGAIN so that
> > layout_and_allocate() retries with profiling disabled: codetag sections
> > are then placed as regular module data and the module loads without
> > profiling rather than being rejected entirely.
> >
> > Skip percpu counter allocation in load_module() when profiling is
> > disabled, as those counters would never be freed on unload.
>
> The approach looks correct but we need to address Sashiko's findings.
> I'll focus on the pre-existing issues and will post fixes later today
> or tomorrow.
Sorry, I'll need some more time for these. Day job got in the way :/
I'll try to get it done by end of the week.
>
> >
> > Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
> > Cc: stable@vger.kernel.org
> > Suggested-by: Suren Baghdasaryan <surenb@google.com>
> > Signed-off-by: Hao Ge <hao.ge@linux.dev>
> > ---
> > kernel/module/main.c | 17 +++++++++++++++--
> > mm/alloc_tag.c | 11 ++++++++---
> > 2 files changed, 23 insertions(+), 5 deletions(-)
> >
> > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > index 46dd8d25a605..c32bb47a573a 100644
> > --- a/kernel/module/main.c
> > +++ b/kernel/module/main.c
> > @@ -2971,13 +2971,26 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
> > * 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);
> > - 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 = 0;
> > + info->sechdrs[info->index.sym].sh_flags &= ~(unsigned long)SHF_ALLOC;
> > + info->sechdrs[info->index.str].sh_flags &= ~(unsigned long)SHF_ALLOC;
> > + goto retry;
> > + }
> >
> > /* 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 af44f90379f2..3eba6331174d 100644
> > --- 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;
> > - 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();
> > @@ -975,6 +977,9 @@ static int load_module(struct module *mod, struct codetag *start, struct codetag
> > struct alloc_tag *stop_tag;
> > struct alloc_tag *tag;
> >
> > + if (!mem_profiling_support)
> > + return 0;
> > +
> > /* percpu counters for core allocations are already statically allocated */
> > if (!mod)
> > return 0;
> > --
> > 2.25.1
> >
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-12 23:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260810093955.153015-1-hao.ge@linux.dev>
2026-08-10 9:39 ` [PATCH v4 2/2] alloc_tag: fix undetected compressed tag overflow when profiling is disabled Hao Ge
[not found] <20260810071518.115381-1-hao.ge@linux.dev>
2026-08-10 7:15 ` Hao Ge
2026-08-11 15:49 ` Suren Baghdasaryan
2026-08-12 23:36 ` Suren Baghdasaryan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox