]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf maps: Avoid RC_CHK use after free
authorIan Rogers <irogers@google.com>
Wed, 19 Nov 2025 05:05:54 +0000 (21:05 -0800)
committerNamhyung Kim <namhyung@kernel.org>
Thu, 20 Nov 2025 00:20:15 +0000 (16:20 -0800)
The case of __maps__fixup_overlap_and_insert where the "new" maps
covers existing mappings can create a use-after-free with reference
count checking enabled. The issue is that "pos" holds a map pointer
from maps_by_address that is put from maps_by_address but then used to
look for a map in maps_by_name (the compared map is now a
use-after-free). The issue stems from using maps__remove which redoes
some of the searches already done by __maps__fixup_overlap_and_insert,
so optimize the code (by avoiding repeated searches) and avoid the
use-after-free by inlining the appropriate removal code.

Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202511141407.f9edcfa6-lkp@intel.com
Signed-off-by: Ian Rogers <irogers@google.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/util/maps.c

index 779f6230130af22735dd5d6ba5e9ac6c80e7d554..c321d4f4d84669abfab5e5ff54b2d3c53be1a3d7 100644 (file)
@@ -931,8 +931,9 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
                        return err;
                } else {
                        struct map *next = NULL;
+                       unsigned int nr_maps = maps__nr_maps(maps);
 
-                       if (i + 1 < maps__nr_maps(maps))
+                       if (i + 1 < nr_maps)
                                next = maps_by_address[i + 1];
 
                        if (!next  || map__start(next) >= map__end(new)) {
@@ -953,7 +954,24 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
                                check_invariants(maps);
                                return err;
                        }
-                       __maps__remove(maps, pos);
+                       /*
+                        * pos fully covers the previous mapping so remove
+                        * it. The following is an inlined version of
+                        * maps__remove that reuses the already computed
+                        * indices.
+                        */
+                       map__put(maps_by_address[i]);
+                       memmove(&maps_by_address[i],
+                               &maps_by_address[i + 1],
+                               (nr_maps - i - 1) * sizeof(*maps_by_address));
+
+                       if (maps_by_name) {
+                               map__put(maps_by_name[ni]);
+                               memmove(&maps_by_name[ni],
+                                       &maps_by_name[ni + 1],
+                                       (nr_maps - ni - 1) *  sizeof(*maps_by_name));
+                       }
+                       --RC_CHK_ACCESS(maps)->nr_maps;
                        check_invariants(maps);
                        /*
                         * Maps are ordered but no need to increase `i` as the