]> PatchyGit - patchyircd.git/commitdiff
map: fix rename UB
authorEgor <egor@patchysicp.xyz>
Sun, 8 Feb 2026 01:17:59 +0000 (03:17 +0200)
committerEgor <egor@patchysicp.xyz>
Sun, 8 Feb 2026 01:17:59 +0000 (03:17 +0200)
src/map.hpp

index 939fb23..6ae8a39 100644 (file)
@@ -247,17 +247,23 @@ public:
 
 struct clientmap : map_base2<Client *> {
        void rename(Client *v, const char *newk) {
-               if (remove(v)) {
-                       auto pos = m.lower_bound(std::string_view(newk)) - m.begin();
-                       auto vec = std::move(m).extract();
-                       vec.insert(vec.begin() + pos, v);
-                       // FIXME: this is actually UB. better do some const_casting and std::rotate
-                       m.replace(std::move(vec));
-               }
+               auto it1 = m.find(v);
+               if (it1 == m.end())
+                       return;
+               auto it2 = m.lower_bound(std::string_view(newk));
+               // Have to to this with a const_cast on the underlying vector, instead
+               // of m.extract/m.replace, because it's UB to replace with an unsorted
+               // vector. This will be an actual problem in e.g. libcxx with hardening
+               // enabled, since they assert that the array is sorted.
+               Client **p = const_cast<Client **>(&*it1);
+               Client **q = const_cast<Client **>(&*it2);
+               if (p < q)
+                       std::rotate(p, p+1, q);
+               else
+                       std::rotate(q, p, p+1);
        }
 };
-struct channelmap : map_base2<Channel *> {
-};
+using channelmap = map_base2<Channel *>;
 struct stringset : map_base2<std::string> {
        template<typename K>
        std::string get(const K& k) = delete;