Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf maps: Avoid RC_CHK use after free

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>

authored by

Ian Rogers and committed by
Namhyung Kim
245cfbcd 51d87d97

+20 -2
+20 -2
tools/perf/util/maps.c
··· 931 931 return err; 932 932 } else { 933 933 struct map *next = NULL; 934 + unsigned int nr_maps = maps__nr_maps(maps); 934 935 935 - if (i + 1 < maps__nr_maps(maps)) 936 + if (i + 1 < nr_maps) 936 937 next = maps_by_address[i + 1]; 937 938 938 939 if (!next || map__start(next) >= map__end(new)) { ··· 954 953 check_invariants(maps); 955 954 return err; 956 955 } 957 - __maps__remove(maps, pos); 956 + /* 957 + * pos fully covers the previous mapping so remove 958 + * it. The following is an inlined version of 959 + * maps__remove that reuses the already computed 960 + * indices. 961 + */ 962 + map__put(maps_by_address[i]); 963 + memmove(&maps_by_address[i], 964 + &maps_by_address[i + 1], 965 + (nr_maps - i - 1) * sizeof(*maps_by_address)); 966 + 967 + if (maps_by_name) { 968 + map__put(maps_by_name[ni]); 969 + memmove(&maps_by_name[ni], 970 + &maps_by_name[ni + 1], 971 + (nr_maps - ni - 1) * sizeof(*maps_by_name)); 972 + } 973 + --RC_CHK_ACCESS(maps)->nr_maps; 958 974 check_invariants(maps); 959 975 /* 960 976 * Maps are ordered but no need to increase `i` as the