alloc_tag: handle module codetag load errors as module load failures
authorSuren Baghdasaryan <surenb@google.com>
Wed, 21 May 2025 16:06:02 +0000 (09:06 -0700)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 6 Jun 2025 05:02:23 +0000 (22:02 -0700)
Failures inside codetag_load_module() are currently ignored.  As a result
an error there would not cause a module load failure and freeing of the
associated resources.  Correct this behavior by propagating the error code
to the caller and handling possible errors.  With this change, error to
allocate percpu counters, which happens at this stage, will not be ignored
and will cause a module load failure and freeing of resources.  With this
change we also do not need to disable memory allocation profiling when
this error happens, instead we fail to load the module.

Link: https://lkml.kernel.org/r/20250521160602.1940771-1-surenb@google.com
Fixes: 10075262888b ("alloc_tag: allocate percpu counters for module tags dynamically")
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Reported-by: Casey Chen <cachen@purestorage.com>
Closes: https://lore.kernel.org/all/20250520231620.15259-1-cachen@purestorage.com/
Cc: Daniel Gomez <da.gomez@samsung.com>
Cc: David Wang <00107082@163.com>
Cc: Kent Overstreet <kent.overstreet@linux.dev>
Cc: Luis Chamberalin <mcgrof@kernel.org>
Cc: Petr Pavlu <petr.pavlu@suse.com>
Cc: Sami Tolvanen <samitolvanen@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/codetag.h
kernel/module/main.c
lib/alloc_tag.c
lib/codetag.c

index 0ee4c21c6dbc7cb838f79d8f62255a3824167a59..5f2b9a1f722c763d37192f12bd4f5d831d999f14 100644 (file)
@@ -36,8 +36,8 @@ union codetag_ref {
 struct codetag_type_desc {
        const char *section;
        size_t tag_size;
-       void (*module_load)(struct module *mod,
-                           struct codetag *start, struct codetag *end);
+       int (*module_load)(struct module *mod,
+                          struct codetag *start, struct codetag *end);
        void (*module_unload)(struct module *mod,
                              struct codetag *start, struct codetag *end);
 #ifdef CONFIG_MODULES
@@ -89,7 +89,7 @@ void *codetag_alloc_module_section(struct module *mod, const char *name,
                                   unsigned long align);
 void codetag_free_module_sections(struct module *mod);
 void codetag_module_replaced(struct module *mod, struct module *new_mod);
-void codetag_load_module(struct module *mod);
+int codetag_load_module(struct module *mod);
 void codetag_unload_module(struct module *mod);
 
 #else /* defined(CONFIG_CODE_TAGGING) && defined(CONFIG_MODULES) */
@@ -103,7 +103,7 @@ codetag_alloc_module_section(struct module *mod, const char *name,
                             unsigned long align) { return NULL; }
 static inline void codetag_free_module_sections(struct module *mod) {}
 static inline void codetag_module_replaced(struct module *mod, struct module *new_mod) {}
-static inline void codetag_load_module(struct module *mod) {}
+static inline int codetag_load_module(struct module *mod) { return 0; }
 static inline void codetag_unload_module(struct module *mod) {}
 
 #endif /* defined(CONFIG_CODE_TAGGING) && defined(CONFIG_MODULES) */
index 3d64e69cc03e93e283e143beab50be83a0c7ca53..08b59c37735e8dfa1915b100aecd87f908985859 100644 (file)
@@ -3386,11 +3386,12 @@ static int load_module(struct load_info *info, const char __user *uargs,
                        goto sysfs_cleanup;
        }
 
+       if (codetag_load_module(mod))
+               goto sysfs_cleanup;
+
        /* Get rid of temporary copy. */
        free_copy(info, flags);
 
-       codetag_load_module(mod);
-
        /* Done! */
        trace_module_load(mod);
 
index 45dae7da70e17dc5b71fa0c9e41ae5442c689d1d..d48b80f3f007e1f4e35101de7ba68c78abf173ac 100644 (file)
@@ -607,15 +607,16 @@ out:
        mas_unlock(&mas);
 }
 
-static void load_module(struct module *mod, struct codetag *start, struct codetag *stop)
+static int load_module(struct module *mod, struct codetag *start, struct codetag *stop)
 {
        /* Allocate module alloc_tag percpu counters */
        struct alloc_tag *start_tag;
        struct alloc_tag *stop_tag;
        struct alloc_tag *tag;
 
+       /* percpu counters for core allocations are already statically allocated */
        if (!mod)
-               return;
+               return 0;
 
        start_tag = ct_to_alloc_tag(start);
        stop_tag = ct_to_alloc_tag(stop);
@@ -627,12 +628,13 @@ static void load_module(struct module *mod, struct codetag *start, struct codeta
                                free_percpu(tag->counters);
                                tag->counters = NULL;
                        }
-                       shutdown_mem_profiling(true);
-                       pr_err("Failed to allocate memory for allocation tag percpu counters in the module %s. Memory allocation profiling is disabled!\n",
+                       pr_err("Failed to allocate memory for allocation tag percpu counters in the module %s\n",
                               mod->name);
-                       break;
+                       return -ENOMEM;
                }
        }
+
+       return 0;
 }
 
 static void replace_module(struct module *mod, struct module *new_mod)
index de332e98d6f5b5e6be53939bff2a8fd1d7862ff2..650d54d7e14da687cbeea54b0a610d5616f8f35e 100644 (file)
@@ -167,6 +167,7 @@ static int codetag_module_init(struct codetag_type *cttype, struct module *mod)
 {
        struct codetag_range range;
        struct codetag_module *cmod;
+       int mod_id;
        int err;
 
        range = get_section_range(mod, cttype->desc.section);
@@ -190,11 +191,20 @@ static int codetag_module_init(struct codetag_type *cttype, struct module *mod)
        cmod->range = range;
 
        down_write(&cttype->mod_lock);
-       err = idr_alloc(&cttype->mod_idr, cmod, 0, 0, GFP_KERNEL);
-       if (err >= 0) {
-               cttype->count += range_size(cttype, &range);
-               if (cttype->desc.module_load)
-                       cttype->desc.module_load(mod, range.start, range.stop);
+       mod_id = idr_alloc(&cttype->mod_idr, cmod, 0, 0, GFP_KERNEL);
+       if (mod_id >= 0) {
+               if (cttype->desc.module_load) {
+                       err = cttype->desc.module_load(mod, range.start, range.stop);
+                       if (!err)
+                               cttype->count += range_size(cttype, &range);
+                       else
+                               idr_remove(&cttype->mod_idr, mod_id);
+               } else {
+                       cttype->count += range_size(cttype, &range);
+                       err = 0;
+               }
+       } else {
+               err = mod_id;
        }
        up_write(&cttype->mod_lock);
 
@@ -295,17 +305,23 @@ void codetag_module_replaced(struct module *mod, struct module *new_mod)
        mutex_unlock(&codetag_lock);
 }
 
-void codetag_load_module(struct module *mod)
+int codetag_load_module(struct module *mod)
 {
        struct codetag_type *cttype;
+       int ret = 0;
 
        if (!mod)
-               return;
+               return 0;
 
        mutex_lock(&codetag_lock);
-       list_for_each_entry(cttype, &codetag_types, link)
-               codetag_module_init(cttype, mod);
+       list_for_each_entry(cttype, &codetag_types, link) {
+               ret = codetag_module_init(cttype, mod);
+               if (ret)
+                       break;
+       }
        mutex_unlock(&codetag_lock);
+
+       return ret;
 }
 
 void codetag_unload_module(struct module *mod)