Skip to content

[libc++][hardening] Add iterator validity checks on unordered containers #80230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 12, 2024

Conversation

davidben
Copy link
Contributor

@davidben davidben commented Feb 1, 2024

These are simply null checks, so use _LIBCPP_ASSERT_NON_NULL. This allows us to restore a bunch of the old debug tests. I've extended them to also cover the const iterators, as those run through different codepaths than the const ones.

This does the easier (and less important) half of #80212.

@davidben davidben requested a review from a team as a code owner February 1, 2024 02:50
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 1, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 1, 2024

@llvm/pr-subscribers-libcxx

Author: David Benjamin (davidben)

Changes

These are simply null checks, so use _LIBCPP_ASSERT_NON_NULL. This allows us to restore a bunch of the old debug tests. I've extended them to also cover the const iterators, as those run through different codepaths than the const ones.

This does the easier (and less important) half of #80212.


Patch is 46.27 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/80230.diff

17 Files Affected:

  • (modified) libcxx/include/__hash_table (+36-4)
  • (modified) libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.dereference.pass.cpp (+31-20)
  • (modified) libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.increment.pass.cpp (+35-22)
  • (modified) libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.dereference.pass.cpp (+29-18)
  • (modified) libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.increment.pass.cpp (+13-2)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.dereference.pass.cpp (+31-20)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.increment.pass.cpp (+37-24)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multimap/debug.local_iterator.dereference.pass.cpp (+29-18)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multimap/debug.local_iterator.increment.pass.cpp (+45-28)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multiset/debug.iterator.dereference.pass.cpp (+26-19)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multiset/debug.iterator.increment.pass.cpp (+37-26)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multiset/debug.local_iterator.dereference.pass.cpp (+28-21)
  • (modified) libcxx/test/libcxx/containers/unord/unord.multiset/debug.local_iterator.increment.pass.cpp (+43-30)
  • (modified) libcxx/test/libcxx/containers/unord/unord.set/debug.iterator.dereference.pass.cpp (+26-19)
  • (modified) libcxx/test/libcxx/containers/unord/unord.set/debug.iterator.increment.pass.cpp (+37-26)
  • (modified) libcxx/test/libcxx/containers/unord/unord.set/debug.local_iterator.dereference.pass.cpp (+28-21)
  • (modified) libcxx/test/libcxx/containers/unord/unord.set/debug.local_iterator.increment.pass.cpp (+43-28)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 13420012006e2..1e7ed1df5c526 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -284,13 +284,21 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI __hash_iterator() _NOEXCEPT : __node_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __node_->__upcast()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI reference operator*() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container iterator");
+    return __node_->__upcast()->__get_value();
+  }
 
   _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container iterator");
     return pointer_traits<pointer>::pointer_to(__node_->__upcast()->__get_value());
   }
 
   _LIBCPP_HIDE_FROM_ABI __hash_iterator& operator++() {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to increment a non-incrementable unordered container iterator");
     __node_ = __node_->__next_;
     return *this;
   }
@@ -345,12 +353,20 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI __hash_const_iterator(const __non_const_iterator& __x) _NOEXCEPT : __node_(__x.__node_) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __node_->__upcast()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI reference operator*() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container const_iterator");
+    return __node_->__upcast()->__get_value();
+  }
   _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container const_iterator");
     return pointer_traits<pointer>::pointer_to(__node_->__upcast()->__get_value());
   }
 
   _LIBCPP_HIDE_FROM_ABI __hash_const_iterator& operator++() {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to increment a non-incrementable unordered container const_iterator");
     __node_ = __node_->__next_;
     return *this;
   }
@@ -400,13 +416,21 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI __hash_local_iterator() _NOEXCEPT : __node_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __node_->__upcast()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI reference operator*() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container local_iterator");
+    return __node_->__upcast()->__get_value();
+  }
 
   _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container local_iterator");
     return pointer_traits<pointer>::pointer_to(__node_->__upcast()->__get_value());
   }
 
   _LIBCPP_HIDE_FROM_ABI __hash_local_iterator& operator++() {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to increment a non-incrementable unordered container local_iterator");
     __node_ = __node_->__next_;
     if (__node_ != nullptr && std::__constrain_hash(__node_->__hash(), __bucket_count_) != __bucket_)
       __node_ = nullptr;
@@ -475,13 +499,21 @@ public:
         __bucket_(__x.__bucket_),
         __bucket_count_(__x.__bucket_count_) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __node_->__upcast()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI reference operator*() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container const_local_iterator");
+    return __node_->__upcast()->__get_value();
+  }
 
   _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to dereference a non-dereferenceable unordered container const_local_iterator");
     return pointer_traits<pointer>::pointer_to(__node_->__upcast()->__get_value());
   }
 
   _LIBCPP_HIDE_FROM_ABI __hash_const_local_iterator& operator++() {
+    _LIBCPP_ASSERT_NON_NULL(
+        __node_ != nullptr, "Attempted to increment a non-incrementable unordered container const_local_iterator");
     __node_ = __node_->__next_;
     if (__node_ != nullptr && std::__constrain_hash(__node_->__hash(), __bucket_count_) != __bucket_)
       __node_ = nullptr;
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.dereference.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.dereference.pass.cpp
index 5ea7f4d97fcc1..f57341d64ff39 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.dereference.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.dereference.pass.cpp
@@ -10,8 +10,9 @@
 
 // Dereference non-dereferenceable iterator.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-has-legacy-debug-mode, c++03
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <unordered_map>
 #include <string>
@@ -20,22 +21,32 @@
 #include "min_allocator.h"
 
 int main(int, char**) {
-    {
-        typedef std::unordered_map<int, std::string> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.end();
-        TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
-    }
-
-    {
-        typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
-                                   min_allocator<std::pair<const int, std::string>>> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.end();
-        TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
-    }
-
-    return 0;
+  {
+    typedef std::unordered_map<int, std::string> C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.end();
+    TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
+    C::const_iterator i2 = c.cend();
+    TEST_LIBCPP_ASSERT_FAILURE(
+        *i2, "Attempted to dereference a non-dereferenceable unordered container const_iterator");
+  }
+
+  {
+    typedef std::unordered_map<int,
+                               std::string,
+                               std::hash<int>,
+                               std::equal_to<int>,
+                               min_allocator<std::pair<const int, std::string>>>
+        C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.end();
+    TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
+    C::const_iterator i2 = c.cend();
+    TEST_LIBCPP_ASSERT_FAILURE(
+        *i2, "Attempted to dereference a non-dereferenceable unordered container const_iterator");
+  }
+
+  return 0;
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.increment.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.increment.pass.cpp
index 2ed09bc81aaa9..84c0bedf54c38 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.increment.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/debug.iterator.increment.pass.cpp
@@ -10,8 +10,9 @@
 
 // Increment iterator past end.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-has-legacy-debug-mode, c++03
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <unordered_map>
 #include <cassert>
@@ -21,26 +22,38 @@
 #include "min_allocator.h"
 
 int main(int, char**) {
-    {
-        typedef std::unordered_map<int, std::string> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.begin();
-        ++i;
-        assert(i == c.end());
-        TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
-    }
-
-    {
-        typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
-                                   min_allocator<std::pair<const int, std::string>>> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.begin();
-        ++i;
-        assert(i == c.end());
-        TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
-    }
+  {
+    typedef std::unordered_map<int, std::string> C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.begin();
+    ++i;
+    assert(i == c.end());
+    TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
+    C::const_iterator i2 = c.cbegin();
+    ++i2;
+    assert(i2 == c.cend());
+    TEST_LIBCPP_ASSERT_FAILURE(++i2, "Attempted to increment a non-incrementable unordered container const_iterator");
+  }
+
+  {
+    typedef std::unordered_map<int,
+                               std::string,
+                               std::hash<int>,
+                               std::equal_to<int>,
+                               min_allocator<std::pair<const int, std::string>>>
+        C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.begin();
+    ++i;
+    assert(i == c.end());
+    TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
+    C::const_iterator i2 = c.cbegin();
+    ++i2;
+    assert(i2 == c.cend());
+    TEST_LIBCPP_ASSERT_FAILURE(++i2, "Attempted to increment a non-incrementable unordered container const_iterator");
+  }
 
     return 0;
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.dereference.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.dereference.pass.cpp
index 2e4e62dbb41f4..8b47f54895560 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.dereference.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.dereference.pass.cpp
@@ -10,8 +10,9 @@
 
 // Dereference non-dereferenceable iterator.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-has-legacy-debug-mode, c++03
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <unordered_map>
 #include <string>
@@ -20,20 +21,30 @@
 #include "min_allocator.h"
 
 int main(int, char**) {
-    {
-        typedef std::unordered_map<int, std::string> C;
-        C c(1);
-        C::local_iterator i = c.end(0);
-        TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container local_iterator");
-    }
-
-    {
-        typedef std::unordered_map<int, std::string, std::hash<int>, std::equal_to<int>,
-                                   min_allocator<std::pair<const int, std::string>>> C;
-        C c(1);
-        C::local_iterator i = c.end(0);
-        TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container local_iterator");
-    }
-
-    return 0;
+  {
+    typedef std::unordered_map<int, std::string> C;
+    C c(1);
+    C::local_iterator i = c.end(0);
+    TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container local_iterator");
+    C::const_local_iterator i2 = c.cend(0);
+    TEST_LIBCPP_ASSERT_FAILURE(
+        *i2, "Attempted to dereference a non-dereferenceable unordered container const_local_iterator");
+  }
+
+  {
+    typedef std::unordered_map<int,
+                               std::string,
+                               std::hash<int>,
+                               std::equal_to<int>,
+                               min_allocator<std::pair<const int, std::string>>>
+        C;
+    C c(1);
+    C::local_iterator i = c.end(0);
+    TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container local_iterator");
+    C::const_local_iterator i2 = c.cend(0);
+    TEST_LIBCPP_ASSERT_FAILURE(
+        *i2, "Attempted to dereference a non-dereferenceable unordered container const_local_iterator");
+  }
+
+  return 0;
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.increment.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.increment.pass.cpp
index 28599263447a5..f0ae410643c7f 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.increment.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/debug.local_iterator.increment.pass.cpp
@@ -10,8 +10,9 @@
 
 // Increment local_iterator past end.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-has-legacy-debug-mode, c++03
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <unordered_map>
 #include <string>
@@ -31,6 +32,11 @@ int main(int, char**) {
         ++i;
         assert(i == c.end(b));
         TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container local_iterator");
+        C::const_local_iterator i2 = c.cbegin(b);
+        assert(i2 != c.cend(b));
+        ++i2;
+        assert(i2 == c.cend(b));
+        TEST_LIBCPP_ASSERT_FAILURE(++i2, "Attempted to increment a non-incrementable unordered container const_local_iterator");
     }
 
     {
@@ -43,6 +49,11 @@ int main(int, char**) {
         ++i;
         assert(i == c.end(b));
         TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container local_iterator");
+        C::const_local_iterator i2 = c.cbegin(b);
+        assert(i2 != c.cend(b));
+        ++i2;
+        assert(i2 == c.cend(b));
+        TEST_LIBCPP_ASSERT_FAILURE(++i2, "Attempted to increment a non-incrementable unordered container const_local_iterator");
     }
 
     return 0;
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.dereference.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.dereference.pass.cpp
index 3dad48b3925d1..d295a82a8a1f5 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.dereference.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.dereference.pass.cpp
@@ -10,8 +10,9 @@
 
 // Dereference non-dereferenceable iterator.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-has-legacy-debug-mode, c++03
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <string>
 #include <unordered_map>
@@ -20,22 +21,32 @@
 #include "min_allocator.h"
 
 int main(int, char**) {
-    {
-        typedef std::unordered_multimap<int, std::string> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.end();
-        TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
-    }
-
-    {
-        typedef std::unordered_multimap<int, std::string, std::hash<int>, std::equal_to<int>,
-                                        min_allocator<std::pair<const int, std::string>>> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.end();
-        TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
-    }
-
-    return 0;
+  {
+    typedef std::unordered_multimap<int, std::string> C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.end();
+    TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
+    C::const_iterator i2 = c.cend();
+    TEST_LIBCPP_ASSERT_FAILURE(
+        *i2, "Attempted to dereference a non-dereferenceable unordered container const_iterator");
+  }
+
+  {
+    typedef std::unordered_multimap<int,
+                                    std::string,
+                                    std::hash<int>,
+                                    std::equal_to<int>,
+                                    min_allocator<std::pair<const int, std::string>>>
+        C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.end();
+    TEST_LIBCPP_ASSERT_FAILURE(*i, "Attempted to dereference a non-dereferenceable unordered container iterator");
+    C::const_iterator i2 = c.cend();
+    TEST_LIBCPP_ASSERT_FAILURE(
+        *i2, "Attempted to dereference a non-dereferenceable unordered container const_iterator");
+  }
+
+  return 0;
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.increment.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.increment.pass.cpp
index b02bac6022f7c..4247edc8def97 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.increment.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/debug.iterator.increment.pass.cpp
@@ -10,8 +10,9 @@
 
 // Increment iterator past end.
 
-// REQUIRES: has-unix-headers
-// UNSUPPORTED: !libcpp-has-legacy-debug-mode, c++03
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <unordered_map>
 #include <cassert>
@@ -21,26 +22,38 @@
 #include "min_allocator.h"
 
 int main(int, char**) {
-    {
-        typedef std::unordered_multimap<int, std::string> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.begin();
-        ++i;
-        assert(i == c.end());
-        TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
-    }
-
-    {
-        typedef std::unordered_multimap<int, std::string, std::hash<int>, std::equal_to<int>,
-                            min_allocator<std::pair<const int, std::string>>> C;
-        C c;
-        c.insert(std::make_pair(1, "one"));
-        C::iterator i = c.begin();
-        ++i;
-        assert(i == c.end());
-        TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
-    }
-
-    return 0;
+  {
+    typedef std::unordered_multimap<int, std::string> C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.begin();
+    ++i;
+    assert(i == c.end());
+    TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
+    C::const_iterator i2 = c.cbegin();
+    ++i2;
+    assert(i2 == c.cend());
+    TEST_LIBCPP_ASSERT_FAILURE(++i2, "Attempted to increment a non-incrementable unordered container const_iterator");
+  }
+
+  {
+    typedef std::unordered_multimap<int,
+                                    std::string,
+                                    std::hash<int>,
+                                    std::equal_to<int>,
+                                    min_allocator<std::pair<const int, std::string>>>
+        C;
+    C c;
+    c.insert(std::make_pair(1, "one"));
+    C::iterator i = c.begin();
+    ++i;
+    assert(i == c.end());
+    TEST_LIBCPP_ASSERT_FAILURE(++i, "Attempted to increment a non-incrementable unordered container iterator");
+    C::const_iterator i2 = c.cbegin();
+    ++i2;
+    assert(i2 == c.cend());
+    TEST_LIBCPP_ASSERT_FAILURE(++i2, "Attempted to increment a non-incrementable unordered container const_iterator");
+  }
+
+  return 0;
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/debug.local_iterator.dereference.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/debug.local_iterator.dereference.pass.cpp
index 9719ba5889759..7ea87964e05f0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/debug.local_iterator.dereference.pass.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/debug.local_iterator.dereference.pass.cpp
@@ -10,8 +10,9 @@
 ...
[truncated]

Copy link

github-actions bot commented Feb 1, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

These are simply null checks, so use _LIBCPP_ASSERT_NON_NULL. This
allows us to restore a bunch of the old debug tests. I've extended them
to also cover the const iterators, as those run through different
codepaths than the const ones.

This does the easier (and less important) half of llvm#80212.
Copy link
Member

@var-const var-const left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, this is awesome!

@var-const var-const added the hardening Issues related to the hardening effort label Feb 7, 2024
@var-const var-const self-assigned this Feb 8, 2024
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really nice to be able to re-enable these debug checks without a bunch of complicated machinery!

@var-const
Copy link
Member

@davidben Is there anything blocking this patch from being merged? The CI failure should be easy to fix (it's just clang-format AFAIS).

@davidben
Copy link
Contributor Author

No blocker on my end. I missed the clang-format error. Will fix!

@davidben
Copy link
Contributor Author

(Fixed, I think.)

@var-const
Copy link
Member

@davidben This looks ready to merge -- can you merge (you have access, right?), or would you prefer one of us to do so?

@davidben
Copy link
Contributor Author

I don't have access. Just an occasional contributor here. :-)

@var-const
Copy link
Member

@davidben Ah, sorry, I didn't realize you were waiting for us to merge. Will merge now, thanks a lot!

@var-const var-const merged commit 41658ba into llvm:main Mar 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hardening Issues related to the hardening effort libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants