Linux kernel -stable discussions
 help / color / mirror / Atom feed
* [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