Skip to content

Conversation

@mordante
Copy link
Member

Guards against introducing new places where operator& depends on a template type.

@mordante mordante requested a review from a team as a code owner February 22, 2025 17:44
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 22, 2025

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

Guards against introducing new places where operator& depends on a template type.


Full diff: https://github.com/llvm/llvm-project/pull/128366.diff

4 Files Affected:

  • (modified) libcxx/test/tools/clang_tidy_checks/CMakeLists.txt (+1)
  • (modified) libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp (+3)
  • (added) libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp (+44)
  • (added) libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp (+18)
diff --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
index 0f8f0e8864d0f..e8e62c3f4ba40 100644
--- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
+++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
@@ -96,6 +96,7 @@ set(SOURCES
     proper_version_checks.cpp
     qualify_declval.cpp
     robust_against_adl.cpp
+    robust_against_operator_ampersand.cpp
     uglify_attributes.cpp
 
     libcpp_module.cpp
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index bc7c8ce7ec443..a52e25f2cf08f 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -17,6 +17,7 @@
 #include "proper_version_checks.hpp"
 #include "qualify_declval.hpp"
 #include "robust_against_adl.hpp"
+#include "robust_against_operator_ampersand.hpp"
 #include "uglify_attributes.hpp"
 
 namespace {
@@ -30,6 +31,8 @@ class LibcxxTestModule : public clang::tidy::ClangTidyModule {
     check_factories.registerCheck<libcpp::nodebug_on_aliases>("libcpp-nodebug-on-aliases");
     check_factories.registerCheck<libcpp::proper_version_checks>("libcpp-cpp-version-check");
     check_factories.registerCheck<libcpp::robust_against_adl_check>("libcpp-robust-against-adl");
+    check_factories.registerCheck<libcpp::robust_against_operator_ampersand>(
+        "libcpp-robust-against-operator-ampersand");
     check_factories.registerCheck<libcpp::uglify_attributes>("libcpp-uglify-attributes");
     check_factories.registerCheck<libcpp::qualify_declval>("libcpp-qualify-declval");
   }
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
new file mode 100644
index 0000000000000..8361e0c3eee88
--- /dev/null
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-tidy/ClangTidyCheck.h"
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang/Tooling/FixIt.h"
+
+#include "robust_against_operator_ampersand.hpp"
+
+// This clang-tidy check ensures that we don't use operator& on dependant
+// types. If the type is user supplied it may call the type's operator&.
+// Instead use std::addressof.
+
+namespace libcpp {
+robust_against_operator_ampersand::robust_against_operator_ampersand(
+    llvm::StringRef name, clang::tidy::ClangTidyContext* context)
+    : clang::tidy::ClangTidyCheck(name, context) {}
+
+void robust_against_operator_ampersand::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
+  using namespace clang::ast_matchers;
+  finder->addMatcher(
+      cxxOperatorCallExpr(allOf(hasOperatorName("&"), argumentCountIs(1), isTypeDependent()),
+                          unless(hasUnaryOperand(dependentScopeDeclRefExpr())))
+          .bind("match"),
+      this);
+}
+
+void robust_against_operator_ampersand::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
+  if (const auto* call = result.Nodes.getNodeAs< clang::CXXOperatorCallExpr >("match"); call != nullptr) {
+    diag(call->getBeginLoc(), "Guard against user provided operator& for dependent types.")
+        << clang::FixItHint::CreateReplacement(
+               call->getSourceRange(),
+               (llvm::Twine(
+                    "std::addressof(" + clang::tooling::fixit::getText(*call->getArg(0), *result.Context) + ")"))
+                   .str());
+  }
+}
+
+} // namespace libcpp
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp
new file mode 100644
index 0000000000000..5cdc0baca5c23
--- /dev/null
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-tidy/ClangTidyCheck.h"
+
+namespace libcpp {
+class robust_against_operator_ampersand : public clang::tidy::ClangTidyCheck {
+public:
+  robust_against_operator_ampersand(llvm::StringRef, clang::tidy::ClangTidyContext*);
+  void registerMatchers(clang::ast_matchers::MatchFinder*) override;
+  void check(const clang::ast_matchers::MatchFinder::MatchResult&) override;
+};
+} // namespace libcpp

@mordante mordante force-pushed the users/mordante/operator_hijacking_clang-tidy branch from 5a96aad to ab09f9d Compare February 22, 2025 18:32
@mordante mordante force-pushed the users/mordante/operator_hijacking_not_exploitable branch from 05c33c8 to c24720b Compare February 26, 2025 16:36
Base automatically changed from users/mordante/operator_hijacking_not_exploitable to main February 27, 2025 16:47
@mordante mordante force-pushed the users/mordante/operator_hijacking_clang-tidy branch from ab09f9d to c40bf19 Compare March 3, 2025 18:04
@mordante mordante changed the base branch from main to users/mordante/operator_hijacking_fixes March 3, 2025 18:05
@github-actions
Copy link

github-actions bot commented Mar 3, 2025

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

@denzor200
Copy link

This check should be in regular Clang Tidy, using of std::addressof actual not only for libbcpp, any user-writted code might follow the guidline to use std::addressof instead of operator& for a generic type. For example, the Boost library: https://github.com/boostorg/pfr/blob/f09e6aeae9d050897fff72b93d5f5e866cc5e11a/include/boost/pfr/detail/core_name20_static.hpp#L196

Please, look at the issue about it: #121172

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

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

@denzor200 It's a lot easier for us to add a libc++-speicific clang-tidy check than a general one. libc++ checks have significantly lower quality requirements, since they only have to work for libc++ (making "seems to work fine" good enough). If anybody wants to make this a general check they're welcome, but it's certainly more effort.

Comment on lines +15 to +17
// This clang-tidy check ensures that we don't use operator& on dependant
// types. If the type is user supplied it may call the type's operator&.
// Instead use std::addressof.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should go into the coding guidelines instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

I disagree. The code should documented what the intention is.

As for the coding guidelines; it already has this information documented.

(Note testing for operator, in the same part of the documentation is on my todo list.)

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @mordante here, this is documented explicitly in the coding guidelines already:

Function overloading also applies to operators. Using &user_object may call a user-defined operator&. Use std::addressof instead. Similarly, to avoid invoking a user-defined operator,, make sure to cast the result to void when using the , or avoid it in the first place. For example:

IMO this comment doesn't provide absolutely necessary information, but it also doesn't hurt. I'd keep it.

Copy link
Contributor

Choose a reason for hiding this comment

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

The guidelines should still document that this check exists. IMO this comment isn't useful, since it will get out of sync with the guidelines. I'd be much happier with a reference to the guidelines than documenting the same thing in different places.

Copy link
Member

Choose a reason for hiding this comment

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

I would suggest the following, then:

Suggested change
// This clang-tidy check ensures that we don't use operator& on dependant
// types. If the type is user supplied it may call the type's operator&.
// Instead use std::addressof.
// This clang-tidy check ensures that we don't use operator& on dependant
// types, as documented in our coding guidelines.

I am mostly neutral on this issue. It's true that we're duplicating a bit of information, but we're really not duplicating a lot of it. I'd let @mordante decide here to avoid blocking this whole patch on such a minor point.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it's useful still, but I wouldn't block it on this. However, I would block on not mentioning the check in the documentation as we do with all other checks.

@mordante
Copy link
Member Author

This check should be in regular Clang Tidy, using of std::addressof actual not only for libbcpp, any user-writted code might follow the guidline to use std::addressof instead of operator& for a generic type. For example, the Boost library: https://github.com/boostorg/pfr/blob/f09e6aeae9d050897fff72b93d5f5e866cc5e11a/include/boost/pfr/detail/core_name20_static.hpp#L196

Please, look at the issue about it: #121172

As @philnik777 mentioned we like to add checks to our own plugin. Another benefit of our plugin is that the check is available in clang-tidy 19, 20, and HEAD and not just in HEAD.

Still I'm open to adding a similar check to the regular Clang Tidy. Then we can remove our own check once all libc++ supported clang-tidy versions have this check.

@mordante mordante force-pushed the users/mordante/operator_hijacking_fixes branch from 687de63 to fd0c93a Compare March 18, 2025 17:08
Base automatically changed from users/mordante/operator_hijacking_fixes to main March 18, 2025 17:08
Guards against introducing new places where operator& depends on a
template type.
@mordante mordante force-pushed the users/mordante/operator_hijacking_clang-tidy branch from c40bf19 to bc68ee1 Compare March 18, 2025 17:20
@github-actions
Copy link

github-actions bot commented Mar 18, 2025

⚠️ Python code formatter, darker found issues in your code. ⚠️

You can test this locally with the following command:
darker --check --diff -r HEAD~1...HEAD libcxx/test/libcxx/clang_tidy.gen.py
View the diff from darker here.
--- clang_tidy.gen.py	2025-04-05 15:46:32.000000 +0000
+++ clang_tidy.gen.py	2025-04-05 15:48:28.428352 +0000
@@ -17,11 +17,12 @@
 import sys
 sys.path.append(sys.argv[1])
 from libcxx.header_information import lit_header_restrictions, lit_header_undeprecations, public_headers
 
 for header in public_headers:
-  print(f"""\
+    print(
+        f"""\
 //--- {header}.sh.cpp
 
 // REQUIRES: has-clang-tidy
 
 // The frozen headers should not be updated to the latest libc++ style, so don't test.
@@ -35,6 +36,7 @@
 
 // TODO: run clang-tidy with modules enabled once they are supported
 // RUN: %{{clang-tidy}} %s --warnings-as-errors=* -header-filter=.* --config-file=%{{libcxx-dir}}/.clang-tidy --load=%{{test-tools-dir}}/clang_tidy_checks/libcxx-tidy.plugin -- -Wweak-vtables %{{compile_flags}} -fno-modules
 
 #include <{header}>
-""")
+"""
+    )

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.

LGTM

Comment on lines +15 to +17
// This clang-tidy check ensures that we don't use operator& on dependant
// types. If the type is user supplied it may call the type's operator&.
// Instead use std::addressof.
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest the following, then:

Suggested change
// This clang-tidy check ensures that we don't use operator& on dependant
// types. If the type is user supplied it may call the type's operator&.
// Instead use std::addressof.
// This clang-tidy check ensures that we don't use operator& on dependant
// types, as documented in our coding guidelines.

I am mostly neutral on this issue. It's true that we're duplicating a bit of information, but we're really not duplicating a lot of it. I'd let @mordante decide here to avoid blocking this whole patch on such a minor point.

@mordante mordante merged commit a406fb8 into main Apr 7, 2025
79 of 80 checks passed
@mordante mordante deleted the users/mordante/operator_hijacking_clang-tidy branch April 7, 2025 16:20
@philnik777
Copy link
Contributor

@mordante Can you please update the coding guidelines?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants